Merge branch 'master' into bot-system

This commit is contained in:
Brady
2018-12-18 19:36:10 -06:00
63 changed files with 1257 additions and 397 deletions
+6 -1
View File
@@ -1,11 +1,16 @@
# Baritone # Baritone
[![Build Status](https://travis-ci.com/cabaletta/baritone.svg?branch=master)](https://travis-ci.com/cabaletta/baritone) [![Build Status](https://travis-ci.com/cabaletta/baritone.svg?branch=master)](https://travis-ci.com/cabaletta/baritone)
[![Release](https://img.shields.io/github/release/cabaletta/baritone.svg)](https://github.com/cabaletta/baritone/releases) [![Release](https://img.shields.io/github/release/cabaletta/baritone.svg)](https://github.com/cabaletta/baritone/releases)
[![License](https://img.shields.io/github/license/cabaletta/baritone.svg)](LICENSE) [![License](https://img.shields.io/badge/license-LGPL--3.0-green.svg)](LICENSE)
[![Codacy Badge](https://api.codacy.com/project/badge/Grade/a73d037823b64a5faf597a18d71e3400)](https://www.codacy.com/app/leijurv/baritone?utm_source=github.com&utm_medium=referral&utm_content=cabaletta/baritone&utm_campaign=Badge_Grade) [![Codacy Badge](https://api.codacy.com/project/badge/Grade/a73d037823b64a5faf597a18d71e3400)](https://www.codacy.com/app/leijurv/baritone?utm_source=github.com&utm_medium=referral&utm_content=cabaletta/baritone&utm_campaign=Badge_Grade)
[![HitCount](http://hits.dwyl.com/cabaletta/baritone.svg)](http://hits.dwyl.com/cabaletta/baritone) [![HitCount](http://hits.dwyl.com/cabaletta/baritone.svg)](http://hits.dwyl.com/cabaletta/baritone)
[![Known Vulnerabilities](https://snyk.io/test/github/cabaletta/baritone/badge.svg?targetFile=build.gradle)](https://snyk.io/test/github/cabaletta/baritone?targetFile=build.gradle)
[![contributions welcome](https://img.shields.io/badge/contributions-welcome-brightgreen.svg?style=flat)](https://github.com/cabaletta/baritone/issues) [![contributions welcome](https://img.shields.io/badge/contributions-welcome-brightgreen.svg?style=flat)](https://github.com/cabaletta/baritone/issues)
[![Minecraft](https://img.shields.io/badge/MC-1.12.2-green.svg)](https://minecraft.gamepedia.com/1.12.2) [![Minecraft](https://img.shields.io/badge/MC-1.12.2-green.svg)](https://minecraft.gamepedia.com/1.12.2)
[![Impact integration](https://img.shields.io/badge/Impact%20integration-v1.0.0--hotfix--4-brightgreen.svg)](https://impactdevelopment.github.io/)
[![KAMI integration](https://img.shields.io/badge/KAMI%20integration-v1.0.0-orange.svg)](https://github.com/zeroeightysix/KAMI/)
[![Asuna integration](https://img.shields.io/badge/Asuna%20integration-v1.0.0-orange.svg)](https://github.com/EmotionalLove/Asuna/)
[![Future integration](https://img.shields.io/badge/Future%20integration-%3F%3F%3F-red.svg)](https://futureclient.net/)
A Minecraft pathfinder bot. A Minecraft pathfinder bot.
+5
View File
@@ -102,6 +102,11 @@ mixin {
add sourceSets.launch, 'mixins.baritone.refmap.json' add sourceSets.launch, 'mixins.baritone.refmap.json'
} }
javadoc {
source = sourceSets.api.allJava
classpath = sourceSets.api.compileClasspath
}
jar { jar {
from sourceSets.comms.output, sourceSets.launch.output, sourceSets.api.output from sourceSets.comms.output, sourceSets.launch.output, sourceSets.api.output
preserveFileTimestamps = false preserveFileTimestamps = false
-7
View File
@@ -18,7 +18,6 @@
package baritone.api; package baritone.api;
import baritone.api.behavior.ILookBehavior; import baritone.api.behavior.ILookBehavior;
import baritone.api.behavior.IMemoryBehavior;
import baritone.api.behavior.IPathingBehavior; import baritone.api.behavior.IPathingBehavior;
import baritone.api.cache.IWorldProvider; import baritone.api.cache.IWorldProvider;
import baritone.api.event.listener.IEventBus; import baritone.api.event.listener.IEventBus;
@@ -48,12 +47,6 @@ public interface IBaritone {
*/ */
ILookBehavior getLookBehavior(); ILookBehavior getLookBehavior();
/**
* @return The {@link IMemoryBehavior} instance
* @see IMemoryBehavior
*/
IMemoryBehavior getMemoryBehavior();
/** /**
* @return The {@link IMineProcess} instance * @return The {@link IMineProcess} instance
* @see IMineProcess * @see IMineProcess
+57 -1
View File
@@ -50,6 +50,11 @@ public class Settings {
*/ */
public Setting<Boolean> allowPlace = new Setting<>(true); public Setting<Boolean> allowPlace = new Setting<>(true);
/**
* Allow Baritone to move items in your inventory to your hotbar
*/
public Setting<Boolean> allowInventory = new Setting<>(false);
/** /**
* It doesn't actually take twenty ticks to place a block, this cost is so high * It doesn't actually take twenty ticks to place a block, this cost is so high
* because we want to generally conserve blocks which might be limited * because we want to generally conserve blocks which might be limited
@@ -89,6 +94,13 @@ public class Settings {
*/ */
public Setting<Boolean> assumeSafeWalk = new Setting<>(false); public Setting<Boolean> assumeSafeWalk = new Setting<>(false);
/**
* If true, parkour is allowed to make jumps when standing on blocks at the maximum height, so player feet is y=256
* <p>
* Defaults to false because this fails on NCP
*/
public Setting<Boolean> allowJumpAt256 = new Setting<>(false);
/** /**
* Blocks that Baritone is allowed to place (as throwaway, for sneak bridging, pillaring, etc.) * Blocks that Baritone is allowed to place (as throwaway, for sneak bridging, pillaring, etc.)
*/ */
@@ -141,8 +153,10 @@ public class Settings {
* metric gets better and better with each block, instead of slightly worse. * metric gets better and better with each block, instead of slightly worse.
* <p> * <p>
* Finding the optimal path is worth it, so it's the default. * Finding the optimal path is worth it, so it's the default.
* <p>
* This value is an expression instead of a literal so that it's exactly equal to SPRINT_ONE_BLOCK_COST defined in ActionCosts.java
*/ */
public Setting<Double> costHeuristic = new Setting<>(3.5D); public Setting<Double> costHeuristic = new Setting<>(20 / 5.612);
// a bunch of obscure internal A* settings that you probably don't want to change // a bunch of obscure internal A* settings that you probably don't want to change
/** /**
@@ -158,6 +172,42 @@ public class Settings {
*/ */
public Setting<Double> backtrackCostFavoringCoefficient = new Setting<>(0.5); public Setting<Double> backtrackCostFavoringCoefficient = new Setting<>(0.5);
/**
* Toggle the following 4 settings
* <p>
* They have a noticable performance impact, so they default off
*/
public Setting<Boolean> avoidance = new Setting<>(false);
/**
* Set to 1.0 to effectively disable this feature
* <p>
* Set below 1.0 to go out of your way to walk near mob spawners
*/
public Setting<Double> mobSpawnerAvoidanceCoefficient = new Setting<>(2.0);
public Setting<Integer> mobSpawnerAvoidanceRadius = new Setting<>(16);
/**
* Set to 1.0 to effectively disable this feature
* <p>
* Set below 1.0 to go out of your way to walk near mobs
*/
public Setting<Double> mobAvoidanceCoefficient = new Setting<>(1.5);
public Setting<Integer> mobAvoidanceRadius = new Setting<>(8);
/**
* When running a goto towards a container block (chest, ender chest, furnace, etc),
* right click and open it once you arrive.
*/
public Setting<Boolean> rightClickContainerOnArrival = new Setting<>(true);
/**
* When running a goto towards a nether portal block, walk all the way into the portal
* instead of stopping one block before.
*/
public Setting<Boolean> enterPortal = new Setting<>(true);
/** /**
* Don't repropagate cost improvements below 0.01 ticks. They're all just floating point inaccuracies, * Don't repropagate cost improvements below 0.01 ticks. They're all just floating point inaccuracies,
* and there's no point. * and there's no point.
@@ -328,6 +378,12 @@ public class Settings {
*/ */
public Setting<Boolean> renderGoalIgnoreDepth = new Setting<>(true); public Setting<Boolean> renderGoalIgnoreDepth = new Setting<>(true);
/**
* Renders X/Z type Goals with the vanilla beacon beam effect. Combining this with
* {@link #renderGoalIgnoreDepth} will cause strange render clipping.
*/
public Setting<Boolean> renderGoalXZBeacon = new Setting<>(false);
/** /**
* Ignore depth when rendering the selection boxes (to break, to place, to walk into) * Ignore depth when rendering the selection boxes (to break, to place, to walk into)
*/ */
+2 -2
View File
@@ -37,12 +37,12 @@ public interface ICachedRegion extends IBlockTypeAccess {
boolean isCached(int blockX, int blockZ); boolean isCached(int blockX, int blockZ);
/** /**
* The X coordinate of this region * @return The X coordinate of this region
*/ */
int getX(); int getX();
/** /**
* The Z coordinate of this region * @return The Z coordinate of this region
*/ */
int getZ(); int getZ();
} }
@@ -15,9 +15,8 @@
* along with Baritone. If not, see <https://www.gnu.org/licenses/>. * along with Baritone. If not, see <https://www.gnu.org/licenses/>.
*/ */
package baritone.api.behavior; package baritone.api.cache;
import baritone.api.behavior.memory.IRememberedInventory;
import net.minecraft.util.math.BlockPos; import net.minecraft.util.math.BlockPos;
import java.util.Map; import java.util.Map;
@@ -26,7 +25,7 @@ import java.util.Map;
* @author Brady * @author Brady
* @since 9/23/2018 * @since 9/23/2018
*/ */
public interface IMemoryBehavior extends IBehavior { public interface IContainerMemory {
/** /**
* Gets a remembered inventory by its block position. * Gets a remembered inventory by its block position.
@@ -15,7 +15,7 @@
* along with Baritone. If not, see <https://www.gnu.org/licenses/>. * along with Baritone. If not, see <https://www.gnu.org/licenses/>.
*/ */
package baritone.api.behavior.memory; package baritone.api.cache;
import net.minecraft.item.ItemStack; import net.minecraft.item.ItemStack;
+8 -2
View File
@@ -27,13 +27,19 @@ public interface IWorldData {
* Returns the cached world for this world. A cached world is a simplified format * Returns the cached world for this world. A cached world is a simplified format
* of a regular world, intended for use on multiplayer servers where chunks are not * of a regular world, intended for use on multiplayer servers where chunks are not
* traditionally stored to disk, allowing for long distance pathing with minimal disk usage. * traditionally stored to disk, allowing for long distance pathing with minimal disk usage.
*
* @return The cached world for this world
*/ */
ICachedWorld getCachedWorld(); ICachedWorld getCachedWorld();
/** /**
* Returns the waypoint collection for this world.
*
* @return The waypoint collection for this world * @return The waypoint collection for this world
*/ */
IWaypointCollection getWaypoints(); IWaypointCollection getWaypoints();
/**
* @return The {@link IContainerMemory} instance
* @see IContainerMemory
*/
IContainerMemory getContainerMemory();
} }
+15
View File
@@ -20,6 +20,7 @@ package baritone.api.cache;
import baritone.api.utils.IPlayerContext; import baritone.api.utils.IPlayerContext;
import net.minecraft.block.Block; import net.minecraft.block.Block;
import net.minecraft.util.math.BlockPos; import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.ChunkPos;
import java.util.List; import java.util.List;
@@ -42,4 +43,18 @@ public interface IWorldScanner {
* @return The matching block positions * @return The matching block positions
*/ */
List<BlockPos> scanChunkRadius(IPlayerContext ctx, List<Block> blocks, int max, int yLevelThreshold, int maxSearchRadius); List<BlockPos> scanChunkRadius(IPlayerContext ctx, List<Block> blocks, int max, int yLevelThreshold, int maxSearchRadius);
/**
* Scans a single chunk for the specified blocks.
*
* @param ctx The {@link IPlayerContext} containing player and world info that the
* scan is based upon
* @param blocks The blocks to scan for
* @param pos The position of the target chunk
* @param max The maximum number of blocks to scan before cutoff
* @param yLevelThreshold If a block is found within this Y level, the current result will be
* returned, if the value is negative, then this condition doesn't apply.
* @return The matching block positions
*/
List<BlockPos> scanChunk(IPlayerContext ctx, List<Block> blocks, ChunkPos pos, int max, int yLevelThreshold);
} }
@@ -20,7 +20,7 @@ package baritone.api.event.events;
import net.minecraft.util.math.BlockPos; import net.minecraft.util.math.BlockPos;
/** /**
* Called when the local player interacts with a block, can be either {@link Type#BREAK} or {@link Type#USE}. * Called when the local player interacts with a block, can be either {@link Type#START_BREAK} or {@link Type#USE}.
* *
* @author Brady * @author Brady
* @since 8/22/2018 * @since 8/22/2018
@@ -59,9 +59,9 @@ public final class BlockInteractEvent {
public enum Type { public enum Type {
/** /**
* We're breaking the target block. * We're starting to break the target block.
*/ */
BREAK, START_BREAK,
/** /**
* We're right clicking on the target block. Either placing or interacting with. * We're right clicking on the target block. Either placing or interacting with.
@@ -31,7 +31,9 @@ public final class ChunkEvent {
private final EventState state; private final EventState state;
/** /**
* The type of chunk event that occurred; * The type of chunk event that occurred
*
* @see Type
*/ */
private final Type type; private final Type type;
@@ -23,13 +23,9 @@ import net.minecraft.client.Minecraft;
import net.minecraft.client.entity.EntityPlayerSP; import net.minecraft.client.entity.EntityPlayerSP;
import net.minecraft.client.gui.GuiGameOver; import net.minecraft.client.gui.GuiGameOver;
import net.minecraft.client.multiplayer.WorldClient; import net.minecraft.client.multiplayer.WorldClient;
import net.minecraft.client.renderer.EntityRenderer;
import net.minecraft.client.settings.GameSettings; import net.minecraft.client.settings.GameSettings;
import net.minecraft.entity.Entity; import net.minecraft.entity.Entity;
import net.minecraft.entity.EntityLivingBase;
import net.minecraft.network.NetworkManager;
import net.minecraft.network.Packet; import net.minecraft.network.Packet;
import net.minecraft.util.text.ITextComponent;
/** /**
* @author Brady * @author Brady
@@ -40,6 +36,7 @@ public interface IGameEventListener {
/** /**
* Run once per game tick before screen input is handled. * Run once per game tick before screen input is handled.
* *
* @param event The event
* @see Minecraft#runTick() * @see Minecraft#runTick()
*/ */
void onTick(TickEvent event); void onTick(TickEvent event);
@@ -47,20 +44,20 @@ public interface IGameEventListener {
/** /**
* Run once per game tick from before and after the player rotation is sent to the server. * Run once per game tick from before and after the player rotation is sent to the server.
* *
* @param event The event
* @see EntityPlayerSP#onUpdate() * @see EntityPlayerSP#onUpdate()
*/ */
void onPlayerUpdate(PlayerUpdateEvent event); void onPlayerUpdate(PlayerUpdateEvent event);
/** /**
* Run once per game tick from before keybinds are processed. * Run once per game tick from before keybinds are processed.
*
* @see Minecraft#processKeyBinds()
*/ */
void onProcessKeyBinds(); void onProcessKeyBinds();
/** /**
* Runs whenever the client player sends a message to the server. * Runs whenever the client player sends a message to the server.
* *
* @param event The event
* @see EntityPlayerSP#sendChatMessage(String) * @see EntityPlayerSP#sendChatMessage(String)
*/ */
void onSendChatMessage(ChatEvent event); void onSendChatMessage(ChatEvent event);
@@ -68,6 +65,7 @@ public interface IGameEventListener {
/** /**
* Runs before and after whenever a chunk is either loaded, unloaded, or populated. * Runs before and after whenever a chunk is either loaded, unloaded, or populated.
* *
* @param event The event
* @see WorldClient#doPreChunk(int, int, boolean) * @see WorldClient#doPreChunk(int, int, boolean)
*/ */
void onChunkEvent(ChunkEvent event); void onChunkEvent(ChunkEvent event);
@@ -77,13 +75,14 @@ public interface IGameEventListener {
* <p> * <p>
* <b>Note:</b> {@link GameSettings#anaglyph} has been removed in Minecraft 1.13 * <b>Note:</b> {@link GameSettings#anaglyph} has been removed in Minecraft 1.13
* *
* @see EntityRenderer#renderWorldPass(int, float, long) * @param event The event
*/ */
void onRenderPass(RenderEvent event); void onRenderPass(RenderEvent event);
/** /**
* Runs before and after whenever a new world is loaded * Runs before and after whenever a new world is loaded
* *
* @param event The event
* @see Minecraft#loadWorld(WorldClient, String) * @see Minecraft#loadWorld(WorldClient, String)
*/ */
void onWorldEvent(WorldEvent event); void onWorldEvent(WorldEvent event);
@@ -91,7 +90,7 @@ public interface IGameEventListener {
/** /**
* Runs before a outbound packet is sent * Runs before a outbound packet is sent
* *
* @see NetworkManager#dispatchPacket(Packet, GenericFutureListener[]) * @param event The event
* @see Packet * @see Packet
* @see GenericFutureListener * @see GenericFutureListener
*/ */
@@ -100,7 +99,7 @@ public interface IGameEventListener {
/** /**
* Runs before an inbound packet is processed * Runs before an inbound packet is processed
* *
* @see NetworkManager#dispatchPacket(Packet, GenericFutureListener[]) * @param event The event
* @see Packet * @see Packet
* @see GenericFutureListener * @see GenericFutureListener
*/ */
@@ -110,31 +109,29 @@ public interface IGameEventListener {
* Run once per game tick from before and after the player's moveRelative method is called * Run once per game tick from before and after the player's moveRelative method is called
* and before and after the player jumps. * and before and after the player jumps.
* *
* @param event The event
* @see Entity#moveRelative(float, float, float, float) * @see Entity#moveRelative(float, float, float, float)
* @see EntityLivingBase#jump()
*/ */
void onPlayerRotationMove(RotationMoveEvent event); void onPlayerRotationMove(RotationMoveEvent event);
/** /**
* Called when the local player interacts with a block, whether it is breaking or opening/placing. * Called when the local player interacts with a block, whether it is breaking or opening/placing.
* *
* @see Minecraft#clickMouse() * @param event The event
* @see Minecraft#rightClickMouse()
*/ */
void onBlockInteract(BlockInteractEvent event); void onBlockInteract(BlockInteractEvent event);
/** /**
* Called when the local player dies, as indicated by the creation of the {@link GuiGameOver} screen. * Called when the local player dies, as indicated by the creation of the {@link GuiGameOver} screen.
* *
* @see GuiGameOver(ITextComponent) * @see GuiGameOver
* @see ITextComponent
*/ */
void onPlayerDeath(); void onPlayerDeath();
/** /**
* When the pathfinder's state changes * When the pathfinder's state changes
* *
* @param event * @param event The event
*/ */
void onPathEvent(PathEvent event); void onPathEvent(PathEvent event);
} }
@@ -21,7 +21,6 @@ import baritone.api.Settings;
import baritone.api.pathing.goals.Goal; import baritone.api.pathing.goals.Goal;
import baritone.api.pathing.movement.IMovement; import baritone.api.pathing.movement.IMovement;
import baritone.api.utils.BetterBlockPos; import baritone.api.utils.BetterBlockPos;
import net.minecraft.world.World;
import java.util.HashSet; import java.util.HashSet;
import java.util.List; import java.util.List;
@@ -52,6 +51,8 @@ public interface IPath {
/** /**
* This path is actually going to be executed in the world. Do whatever additional processing is required. * This path is actually going to be executed in the world. Do whatever additional processing is required.
* (as opposed to Path objects that are just constructed every frame for rendering) * (as opposed to Path objects that are just constructed every frame for rendering)
*
* @return The result of path post processing
*/ */
default IPath postProcess() { default IPath postProcess() {
throw new UnsupportedOperationException(); throw new UnsupportedOperationException();
@@ -128,10 +129,13 @@ public interface IPath {
/** /**
* Cuts off this path at the loaded chunk border, and returns the resulting path. Default * Cuts off this path at the loaded chunk border, and returns the resulting path. Default
* implementation just returns this path, without the intended functionality. * implementation just returns this path, without the intended functionality.
* <p>
* The argument is supposed to be a BlockStateInterface LOL LOL LOL LOL LOL
* *
* @param bsi The block state lookup, highly cursed
* @return The result of this cut-off operation * @return The result of this cut-off operation
*/ */
default IPath cutoffAtLoadedChunks(World world) { default IPath cutoffAtLoadedChunks(Object bsi) {
throw new UnsupportedOperationException(); throw new UnsupportedOperationException();
} }
@@ -139,6 +143,7 @@ public interface IPath {
* Cuts off this path using the min length and cutoff factor settings, and returns the resulting path. * Cuts off this path using the min length and cutoff factor settings, and returns the resulting path.
* Default implementation just returns this path, without the intended functionality. * Default implementation just returns this path, without the intended functionality.
* *
* @param destination The end goal of this path
* @return The result of this cut-off operation * @return The result of this cut-off operation
* @see Settings#pathCutoffMinimumLength * @see Settings#pathCutoffMinimumLength
* @see Settings#pathCutoffFactor * @see Settings#pathCutoffFactor
@@ -34,6 +34,9 @@ public interface IPathFinder {
/** /**
* Calculate the path in full. Will take several seconds. * Calculate the path in full. Will take several seconds.
* *
* @param primaryTimeout If a path is found, the path finder will stop after this amount of time
* @param failureTimeout If a path isn't found, the path finder will continue for this amount of time
*
* @return The final path * @return The final path
*/ */
PathCalculationResult calculate(long primaryTimeout, long failureTimeout); PathCalculationResult calculate(long primaryTimeout, long failureTimeout);
@@ -30,6 +30,10 @@ public interface Goal {
* Returns whether or not the specified position * Returns whether or not the specified position
* meets the requirement for this goal based. * meets the requirement for this goal based.
* *
* @param x The goal X position
* @param y The goal Y position
* @param z The goal Z position
*
* @return Whether or not it satisfies this goal * @return Whether or not it satisfies this goal
*/ */
boolean isInGoal(int x, int y, int z); boolean isInGoal(int x, int y, int z);
@@ -37,6 +41,10 @@ public interface Goal {
/** /**
* Estimate the number of ticks it will take to get to the goal * Estimate the number of ticks it will take to get to the goal
* *
* @param x The goal X position
* @param y The goal Y position
* @param z The goal Z position
*
* @return The estimate number of ticks to satisfy the goal * @return The estimate number of ticks to satisfy the goal
*/ */
double heuristic(int x, int y, int z); double heuristic(int x, int y, int z);
@@ -17,6 +17,10 @@
package baritone.api.utils; package baritone.api.utils;
import net.minecraft.client.network.NetHandlerPlayClient;
import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.inventory.ClickType;
import net.minecraft.item.ItemStack;
import net.minecraft.util.EnumFacing; import net.minecraft.util.EnumFacing;
import net.minecraft.util.math.BlockPos; import net.minecraft.util.math.BlockPos;
import net.minecraft.world.GameType; import net.minecraft.world.GameType;
@@ -33,6 +37,8 @@ public interface IPlayerController {
void resetBlockRemoving(); void resetBlockRemoving();
ItemStack windowClick(int windowId, int slotId, int mouseButton, ClickType type, EntityPlayer player);
void setGameType(GameType type); void setGameType(GameType type);
GameType getGameType(); GameType getGameType();
@@ -34,7 +34,9 @@ public final class RayTraceUtils {
* any entity collisions can be ignored, because this method will not recognize if an * any entity collisions can be ignored, because this method will not recognize if an
* entity is in the way or not. The local player's block reach distance will be used. * entity is in the way or not. The local player's block reach distance will be used.
* *
* @param rotation The rotation to raytrace towards * @param entity The entity representing the raytrace source
* @param rotation The rotation to raytrace towards
* @param blockReachDistance The block reach distance of the entity
* @return The calculated raytrace result * @return The calculated raytrace result
*/ */
public static RayTraceResult rayTraceTowards(Entity entity, Rotation rotation, double blockReachDistance) { public static RayTraceResult rayTraceTowards(Entity entity, Rotation rotation, double blockReachDistance) {
@@ -142,7 +142,7 @@ public class Rotation {
if (newYaw < -180F) { if (newYaw < -180F) {
newYaw += 360F; newYaw += 360F;
} }
if (newYaw >= 180F) { if (newYaw > 180F) {
newYaw -= 360F; newYaw -= 360F;
} }
return newYaw; return newYaw;
@@ -134,8 +134,9 @@ public final class RotationUtils {
* side that is reachable. The return type will be {@link Optional#empty()} if the entity is * side that is reachable. The return type will be {@link Optional#empty()} if the entity is
* unable to reach any of the sides of the block. * unable to reach any of the sides of the block.
* *
* @param entity The viewing entity * @param entity The viewing entity
* @param pos The target block position * @param pos The target block position
* @param blockReachDistance The block reach distance of the entity
* @return The optional rotation * @return The optional rotation
*/ */
public static Optional<Rotation> reachable(EntityPlayerSP entity, BlockPos pos, double blockReachDistance) { public static Optional<Rotation> reachable(EntityPlayerSP entity, BlockPos pos, double blockReachDistance) {
@@ -178,9 +179,10 @@ public final class RotationUtils {
* the given offsetted position. The return type will be {@link Optional#empty()} if * the given offsetted position. The return type will be {@link Optional#empty()} if
* the entity is unable to reach the block with the offset applied. * the entity is unable to reach the block with the offset applied.
* *
* @param entity The viewing entity * @param entity The viewing entity
* @param pos The target block position * @param pos The target block position
* @param offsetPos The position of the block with the offset applied. * @param offsetPos The position of the block with the offset applied.
* @param blockReachDistance The block reach distance of the entity
* @return The optional rotation * @return The optional rotation
*/ */
public static Optional<Rotation> reachableOffset(Entity entity, BlockPos pos, Vec3d offsetPos, double blockReachDistance) { public static Optional<Rotation> reachableOffset(Entity entity, BlockPos pos, Vec3d offsetPos, double blockReachDistance) {
@@ -202,8 +204,9 @@ public final class RotationUtils {
* Determines if the specified entity is able to reach the specified block where it is * Determines if the specified entity is able to reach the specified block where it is
* looking at the direct center of it's hitbox. * looking at the direct center of it's hitbox.
* *
* @param entity The viewing entity * @param entity The viewing entity
* @param pos The target block position * @param pos The target block position
* @param blockReachDistance The block reach distance of the entity
* @return The optional rotation * @return The optional rotation
*/ */
public static Optional<Rotation> reachableCenter(Entity entity, BlockPos pos, double blockReachDistance) { public static Optional<Rotation> reachableCenter(Entity entity, BlockPos pos, double blockReachDistance) {
@@ -36,7 +36,8 @@ public final class VecUtils {
/** /**
* Calculates the center of the block at the specified position's bounding box * Calculates the center of the block at the specified position's bounding box
* *
* @param pos The block position * @param world The world that the block is in, used to provide the bounding box
* @param pos The block position
* @return The center of the block's bounding box * @return The center of the block's bounding box
* @see #getBlockPosCenter(BlockPos) * @see #getBlockPosCenter(BlockPos)
*/ */
@@ -164,7 +164,7 @@ public class MixinMinecraft {
) )
private void onBlockBreak(CallbackInfo ci, BlockPos pos) { private void onBlockBreak(CallbackInfo ci, BlockPos pos) {
// clickMouse is only for the main player // clickMouse is only for the main player
BaritoneAPI.getProvider().getPrimaryBaritone().getGameEventHandler().onBlockInteract(new BlockInteractEvent(pos, BlockInteractEvent.Type.BREAK)); BaritoneAPI.getProvider().getPrimaryBaritone().getGameEventHandler().onBlockInteract(new BlockInteractEvent(pos, BlockInteractEvent.Type.START_BREAK));
} }
@Inject( @Inject(
+1 -1
View File
@@ -106,6 +106,7 @@ public class Baritone implements IBaritone {
pathingBehavior = new PathingBehavior(this); pathingBehavior = new PathingBehavior(this);
lookBehavior = new LookBehavior(this); lookBehavior = new LookBehavior(this);
memoryBehavior = new MemoryBehavior(this); memoryBehavior = new MemoryBehavior(this);
new InventoryBehavior(this);
inputOverrideHandler = new InputOverrideHandler(this); inputOverrideHandler = new InputOverrideHandler(this);
new ExampleBaritoneControl(this); new ExampleBaritoneControl(this);
} }
@@ -165,7 +166,6 @@ public class Baritone implements IBaritone {
return this.pathingBehavior; return this.pathingBehavior;
} }
@Override
public MemoryBehavior getMemoryBehavior() { public MemoryBehavior getMemoryBehavior() {
return this.memoryBehavior; return this.memoryBehavior;
} }
@@ -0,0 +1,93 @@
/*
* 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.behavior;
import baritone.Baritone;
import baritone.api.event.events.TickEvent;
import baritone.utils.ToolSet;
import net.minecraft.block.Block;
import net.minecraft.init.Blocks;
import net.minecraft.inventory.ClickType;
import net.minecraft.item.ItemPickaxe;
import net.minecraft.item.ItemStack;
import net.minecraft.item.ItemTool;
import net.minecraft.util.NonNullList;
public class InventoryBehavior extends Behavior {
public InventoryBehavior(Baritone baritone) {
super(baritone);
}
@Override
public void onTick(TickEvent event) {
if (!Baritone.settings().allowInventory.get()) {
return;
}
if (event.getType() == TickEvent.Type.OUT) {
return;
}
if (ctx.player().openContainer != ctx.player().inventoryContainer) {
// we have a crafting table or a chest or something open
return;
}
if (firstValidThrowaway() >= 9) { // aka there are none on the hotbar, but there are some in main inventory
swapWithHotBar(firstValidThrowaway(), 8);
}
int pick = bestToolAgainst(Blocks.STONE, ItemPickaxe.class);
if (pick >= 9) {
swapWithHotBar(pick, 0);
}
}
private void swapWithHotBar(int inInventory, int inHotbar) {
if (inInventory < 9) {
inInventory += 36;
}
ctx.playerController().windowClick(ctx.player().inventoryContainer.windowId, inInventory, inHotbar, ClickType.SWAP, ctx.player());
}
private int firstValidThrowaway() { // TODO offhand idk
NonNullList<ItemStack> invy = ctx.player().inventory.mainInventory;
for (int i = 0; i < invy.size(); i++) {
if (Baritone.settings().acceptableThrowawayItems.get().contains(invy.get(i).getItem())) {
return i;
}
}
return -1;
}
private int bestToolAgainst(Block against, Class<? extends ItemTool> klass) {
NonNullList<ItemStack> invy = ctx.player().inventory.mainInventory;
int bestInd = -1;
double bestSpeed = -1;
for (int i = 0; i < invy.size(); i++) {
ItemStack stack = invy.get(i);
if (stack.isEmpty()) {
continue;
}
if (klass.isInstance(stack.getItem())) {
double speed = ToolSet.calculateStrVsBlock(stack, against.getDefaultState()); // takes into account enchants
if (speed > bestSpeed) {
bestSpeed = speed;
bestInd = i;
}
}
}
return bestInd;
}
}
@@ -18,16 +18,17 @@
package baritone.behavior; package baritone.behavior;
import baritone.Baritone; import baritone.Baritone;
import baritone.api.behavior.IMemoryBehavior;
import baritone.api.behavior.memory.IRememberedInventory;
import baritone.api.cache.IWorldData;
import baritone.api.event.events.BlockInteractEvent; import baritone.api.event.events.BlockInteractEvent;
import baritone.api.event.events.PacketEvent; import baritone.api.event.events.PacketEvent;
import baritone.api.event.events.PlayerUpdateEvent; import baritone.api.event.events.PlayerUpdateEvent;
import baritone.api.event.events.type.EventState; import baritone.api.event.events.type.EventState;
import baritone.cache.ContainerMemory;
import baritone.cache.Waypoint; import baritone.cache.Waypoint;
import baritone.pathing.movement.CalculationContext;
import baritone.utils.BlockStateInterface; import baritone.utils.BlockStateInterface;
import net.minecraft.block.Block;
import net.minecraft.block.BlockBed; import net.minecraft.block.BlockBed;
import net.minecraft.init.Blocks;
import net.minecraft.item.ItemStack; import net.minecraft.item.ItemStack;
import net.minecraft.network.Packet; import net.minecraft.network.Packet;
import net.minecraft.network.play.client.CPacketCloseWindow; import net.minecraft.network.play.client.CPacketCloseWindow;
@@ -36,17 +37,24 @@ import net.minecraft.network.play.server.SPacketCloseWindow;
import net.minecraft.network.play.server.SPacketOpenWindow; import net.minecraft.network.play.server.SPacketOpenWindow;
import net.minecraft.tileentity.TileEntity; import net.minecraft.tileentity.TileEntity;
import net.minecraft.tileentity.TileEntityLockable; import net.minecraft.tileentity.TileEntityLockable;
import net.minecraft.util.EnumFacing;
import net.minecraft.util.math.BlockPos; import net.minecraft.util.math.BlockPos;
import net.minecraft.util.text.TextComponentTranslation;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.*; import java.util.*;
/** /**
* @author Brady * @author Brady
* @since 8/6/2018 * @since 8/6/2018
*/ */
public final class MemoryBehavior extends Behavior implements IMemoryBehavior { public final class MemoryBehavior extends Behavior {
private final Map<IWorldData, WorldDataContainer> worldDataContainers = new HashMap<>(); private final List<FutureInventory> futureInventories = new ArrayList<>(); // this is per-bot
private Integer enderChestWindowId; // nae nae
public MemoryBehavior(Baritone baritone) { public MemoryBehavior(Baritone baritone) {
super(baritone); super(baritone);
@@ -68,19 +76,30 @@ public final class MemoryBehavior extends Behavior implements IMemoryBehavior {
CPacketPlayerTryUseItemOnBlock packet = event.cast(); CPacketPlayerTryUseItemOnBlock packet = event.cast();
TileEntity tileEntity = ctx.world().getTileEntity(packet.getPos()); TileEntity tileEntity = ctx.world().getTileEntity(packet.getPos());
// if tileEntity is an ender chest, we don't need to do anything. ender chests are treated the same regardless of what coordinate right clicked
// Ensure the TileEntity is a container of some sort // Ensure the TileEntity is a container of some sort
if (tileEntity instanceof TileEntityLockable) { if (tileEntity instanceof TileEntityLockable) {
TileEntityLockable lockable = (TileEntityLockable) tileEntity; TileEntityLockable lockable = (TileEntityLockable) tileEntity;
int size = lockable.getSizeInventory(); int size = lockable.getSizeInventory();
BlockPos position = tileEntity.getPos();
BlockPos adj = neighboringConnectedBlock(position);
System.out.println(position + " " + adj);
if (adj != null) {
size *= 2; // double chest or double trapped chest
if (adj.getX() < position.getX() || adj.getZ() < position.getZ()) {
position = adj; // standardize on the lower coordinate, regardless of which side of the large chest we right clicked
}
}
this.getCurrentContainer().futureInventories.add(new FutureInventory(System.nanoTime() / 1000000L, size, lockable.getGuiID(), tileEntity.getPos())); this.futureInventories.add(new FutureInventory(System.nanoTime() / 1000000L, size, lockable.getGuiID(), position));
} }
} }
if (p instanceof CPacketCloseWindow) { if (p instanceof CPacketCloseWindow) {
updateInventory(); updateInventory();
getCurrent().save();
} }
} }
} }
@@ -92,27 +111,32 @@ public final class MemoryBehavior extends Behavior implements IMemoryBehavior {
if (event.getState() == EventState.PRE) { if (event.getState() == EventState.PRE) {
if (p instanceof SPacketOpenWindow) { if (p instanceof SPacketOpenWindow) {
SPacketOpenWindow packet = event.cast(); SPacketOpenWindow packet = event.cast();
WorldDataContainer container = this.getCurrentContainer();
// Remove any entries that were created over a second ago, this should make up for INSANE latency // Remove any entries that were created over a second ago, this should make up for INSANE latency
container.futureInventories.removeIf(i -> System.nanoTime() / 1000000L - i.time > 1000); futureInventories.removeIf(i -> System.nanoTime() / 1000000L - i.time > 1000);
container.futureInventories.stream() System.out.println("Received packet " + packet.getGuiId() + " " + packet.getEntityId() + " " + packet.getSlotCount() + " " + packet.getWindowId());
System.out.println(packet.getWindowTitle());
if (packet.getWindowTitle() instanceof TextComponentTranslation) {
// title is not customized (i.e. this isn't just a renamed shulker)
if (((TextComponentTranslation) packet.getWindowTitle()).getKey().equals("container.enderchest")) {
enderChestWindowId = packet.getWindowId();
return;
}
}
futureInventories.stream()
.filter(i -> i.type.equals(packet.getGuiId()) && i.slots == packet.getSlotCount()) .filter(i -> i.type.equals(packet.getGuiId()) && i.slots == packet.getSlotCount())
.findFirst().ifPresent(matched -> { .findFirst().ifPresent(matched -> {
// Remove the future inventory // Remove the future inventory
container.futureInventories.remove(matched); futureInventories.remove(matched);
// Setup the remembered inventory // Setup the remembered inventory
RememberedInventory inventory = container.rememberedInventories.computeIfAbsent(matched.pos, pos -> new RememberedInventory()); getCurrentContainer().setup(matched.pos, packet.getWindowId(), packet.getSlotCount());
inventory.windowId = packet.getWindowId();
inventory.size = packet.getSlotCount();
}); });
} }
if (p instanceof SPacketCloseWindow) { if (p instanceof SPacketCloseWindow) {
updateInventory(); updateInventory();
getCurrent().save();
} }
} }
} }
@@ -129,43 +153,42 @@ public final class MemoryBehavior extends Behavior implements IMemoryBehavior {
baritone.getWorldProvider().getCurrentWorld().getWaypoints().addWaypoint(new Waypoint("death", Waypoint.Tag.DEATH, ctx.playerFeet())); baritone.getWorldProvider().getCurrentWorld().getWaypoints().addWaypoint(new Waypoint("death", Waypoint.Tag.DEATH, ctx.playerFeet()));
} }
private Optional<RememberedInventory> getInventoryFromWindow(int windowId) {
return this.getCurrentContainer().rememberedInventories.values().stream().filter(i -> i.windowId == windowId).findFirst();
}
private void updateInventory() { private void updateInventory() {
getInventoryFromWindow(ctx.player().openContainer.windowId).ifPresent(inventory -> { int windowId = ctx.player().openContainer.windowId;
inventory.items.clear(); if (enderChestWindowId != null) {
inventory.items.addAll(ctx.player().openContainer.getInventory().subList(0, inventory.size)); if (windowId == enderChestWindowId) {
}); getCurrent().contents = ctx.player().openContainer.getInventory().subList(0, 27);
} else {
getCurrent().save();
enderChestWindowId = null;
}
}
if (getCurrentContainer() != null) {
getCurrentContainer().getInventoryFromWindow(windowId).ifPresent(inventory -> inventory.updateFromOpenWindow(ctx));
}
} }
private WorldDataContainer getCurrentContainer() { private ContainerMemory getCurrentContainer() {
return this.worldDataContainers.computeIfAbsent(baritone.getWorldProvider().getCurrentWorld(), data -> new WorldDataContainer()); if (baritone.getWorldProvider().getCurrentWorld() == null) {
return null;
}
return (ContainerMemory) baritone.getWorldProvider().getCurrentWorld().getContainerMemory();
} }
@Override private BlockPos neighboringConnectedBlock(BlockPos in) {
public final synchronized RememberedInventory getInventoryByPos(BlockPos pos) { BlockStateInterface bsi = new CalculationContext(baritone).bsi();
return this.getCurrentContainer().rememberedInventories.get(pos); Block block = bsi.get0(in).getBlock();
} if (block != Blocks.TRAPPED_CHEST && block != Blocks.CHEST) {
return null; // other things that have contents, but can be placed adjacent without combining
@Override }
public final synchronized Map<BlockPos, IRememberedInventory> getRememberedInventories() { for (int i = 0; i < 4; i++) {
// make a copy since this map is modified from the packet thread BlockPos adj = in.offset(EnumFacing.byHorizontalIndex(i));
return new HashMap<>(this.getCurrentContainer().rememberedInventories); if (bsi.get0(adj).getBlock() == block) {
} return adj;
}
private static final class WorldDataContainer { }
return null;
/**
* Possible future inventories that we will be able to remember
*/
private final List<FutureInventory> futureInventories = new ArrayList<>();
/**
* The current remembered inventories
*/
private final Map<BlockPos, RememberedInventory> rememberedInventories = new HashMap<>();
} }
/** /**
@@ -198,43 +221,51 @@ public final class MemoryBehavior extends Behavior implements IMemoryBehavior {
this.slots = slots; this.slots = slots;
this.type = type; this.type = type;
this.pos = pos; this.pos = pos;
System.out.println("Future inventory created " + time + " " + slots + " " + type + " " + pos);
} }
} }
/** public Optional<List<ItemStack>> echest() {
* An inventory that we are aware of. return Optional.ofNullable(getCurrent().contents).map(Collections::unmodifiableList);
* <p> }
* Associated with a {@link BlockPos} in {@link WorldDataContainer#rememberedInventories}.
*/
public static class RememberedInventory implements IRememberedInventory {
/** public EnderChestMemory getCurrent() {
* The list of items in the inventory Path path = baritone.getWorldProvider().getCurrentWorld().directory;
*/ return EnderChestMemory.getByServerAndPlayer(path.getParent(), ctx.player().getUniqueID());
private final List<ItemStack> items; }
/** public static class EnderChestMemory {
* The last known window ID of the inventory private static final Map<Path, EnderChestMemory> memory = new HashMap<>();
*/ private final Path enderChest;
private int windowId; private List<ItemStack> contents;
/** private EnderChestMemory(Path enderChest) {
* The size of the inventory this.enderChest = enderChest;
*/ System.out.println("Echest storing in " + enderChest);
private int size; try {
this.contents = ContainerMemory.readItemStacks(Files.readAllBytes(enderChest));
private RememberedInventory() { } catch (IOException e) {
this.items = new ArrayList<>(); e.printStackTrace();
System.out.println("CANNOT read echest =( =(");
this.contents = null;
}
} }
@Override public synchronized void save() {
public final List<ItemStack> getContents() { System.out.println("Saving");
return Collections.unmodifiableList(this.items); if (contents != null) {
try {
enderChest.getParent().toFile().mkdir();
Files.write(enderChest, ContainerMemory.writeItemStacks(contents));
} catch (IOException e) {
e.printStackTrace();
System.out.println("CANNOT save echest =( =(");
}
}
} }
@Override private static synchronized EnderChestMemory getByServerAndPlayer(Path serverStorage, UUID player) {
public final int getSize() { return memory.computeIfAbsent(serverStorage.resolve("echests").resolve(player.toString()), EnderChestMemory::new);
return this.size;
} }
} }
} }
@@ -37,7 +37,7 @@ import baritone.pathing.path.CutoffPath;
import baritone.pathing.path.PathExecutor; import baritone.pathing.path.PathExecutor;
import baritone.utils.Helper; import baritone.utils.Helper;
import baritone.utils.PathRenderer; import baritone.utils.PathRenderer;
import it.unimi.dsi.fastutil.longs.LongOpenHashSet; import baritone.utils.pathing.Favoring;
import net.minecraft.util.math.BlockPos; import net.minecraft.util.math.BlockPos;
import net.minecraft.world.chunk.EmptyChunk; import net.minecraft.world.chunk.EmptyChunk;
@@ -419,8 +419,8 @@ public final class PathingBehavior extends Behavior implements IPathingBehavior,
failureTimeout = Baritone.settings().planAheadFailureTimeoutMS.get(); failureTimeout = Baritone.settings().planAheadFailureTimeoutMS.get();
} }
CalculationContext context = new CalculationContext(baritone, true); // not safe to create on the other thread, it looks up a lot of stuff in minecraft CalculationContext context = new CalculationContext(baritone, true); // not safe to create on the other thread, it looks up a lot of stuff in minecraft
AbstractNodeCostSearch pathfinder = createPathfinder(start, goal, current == null ? null : current.getPath(), context, true); AbstractNodeCostSearch pathfinder = createPathfinder(start, goal, current == null ? null : current.getPath(), context);
if (!Objects.equals(pathfinder.getGoal(), goal)) { if (!Objects.equals(pathfinder.getGoal(), goal)) { // will return the exact same object if simplification didn't happen
logDebug("Simplifying " + goal.getClass() + " to GoalXZ due to distance"); logDebug("Simplifying " + goal.getClass() + " to GoalXZ due to distance");
} }
inProgress = pathfinder; inProgress = pathfinder;
@@ -433,7 +433,7 @@ public final class PathingBehavior extends Behavior implements IPathingBehavior,
Optional<IPath> path = calcResult.getPath(); Optional<IPath> path = calcResult.getPath();
if (Baritone.settings().cutoffAtLoadBoundary.get()) { if (Baritone.settings().cutoffAtLoadBoundary.get()) {
path = path.map(p -> { path = path.map(p -> {
IPath result = p.cutoffAtLoadedChunks(context.world()); IPath result = p.cutoffAtLoadedChunks(context.bsi());
if (result instanceof CutoffPath) { if (result instanceof CutoffPath) {
logDebug("Cutting off path at edge of loaded chunks"); logDebug("Cutting off path at edge of loaded chunks");
@@ -495,21 +495,16 @@ public final class PathingBehavior extends Behavior implements IPathingBehavior,
}); });
} }
public static AbstractNodeCostSearch createPathfinder(BlockPos start, Goal goal, IPath previous, CalculationContext context, boolean allowSimplifyUnloaded) { public static AbstractNodeCostSearch createPathfinder(BlockPos start, Goal goal, IPath previous, CalculationContext context) {
Goal transformed = goal; Goal transformed = goal;
if (Baritone.settings().simplifyUnloadedYCoord.get() && goal instanceof IGoalRenderPos && allowSimplifyUnloaded) { if (Baritone.settings().simplifyUnloadedYCoord.get() && goal instanceof IGoalRenderPos) {
BlockPos pos = ((IGoalRenderPos) goal).getGoalPos(); BlockPos pos = ((IGoalRenderPos) goal).getGoalPos();
if (context.world().getChunk(pos) instanceof EmptyChunk) { if (context.world().getChunk(pos) instanceof EmptyChunk) {
transformed = new GoalXZ(pos.getX(), pos.getZ()); transformed = new GoalXZ(pos.getX(), pos.getZ());
} }
} }
LongOpenHashSet favoredPositions = null; Favoring favoring = new Favoring(context.getBaritone().getPlayerContext(), previous);
if (Baritone.settings().backtrackCostFavoringCoefficient.get() != 1D && previous != null) { return new AStarPathFinder(start.getX(), start.getY(), start.getZ(), transformed, favoring, context);
LongOpenHashSet tmp = new LongOpenHashSet();
previous.positions().forEach(pos -> tmp.add(BetterBlockPos.longHash(pos)));
favoredPositions = tmp;
}
return new AStarPathFinder(start.getX(), start.getY(), start.getZ(), transformed, favoredPositions, context);
} }
@Override @Override
@@ -19,6 +19,10 @@ package baritone.bot.spec;
import baritone.api.utils.IPlayerController; import baritone.api.utils.IPlayerController;
import baritone.bot.IBaritoneUser; import baritone.bot.IBaritoneUser;
import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.inventory.ClickType;
import net.minecraft.item.ItemStack;
import net.minecraft.network.play.client.CPacketClickWindow;
import net.minecraft.util.EnumFacing; import net.minecraft.util.EnumFacing;
import net.minecraft.util.math.BlockPos; import net.minecraft.util.math.BlockPos;
import net.minecraft.world.GameType; import net.minecraft.world.GameType;
@@ -51,6 +55,11 @@ public class BotPlayerController implements IPlayerController {
} }
@Override
public ItemStack windowClick(int windowId, int slotId, int mouseButton, ClickType type, EntityPlayer player) {
return null;
}
@Override @Override
public void setGameType(GameType type) { public void setGameType(GameType type) {
this.gameType = type; this.gameType = type;
+175
View File
@@ -0,0 +1,175 @@
/*
* 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.cache;
import baritone.api.cache.IContainerMemory;
import baritone.api.cache.IRememberedInventory;
import baritone.api.utils.IPlayerContext;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import net.minecraft.item.ItemStack;
import net.minecraft.network.PacketBuffer;
import net.minecraft.util.math.BlockPos;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.*;
public class ContainerMemory implements IContainerMemory {
private final Path saveTo;
/**
* The current remembered inventories
*/
private final Map<BlockPos, RememberedInventory> inventories = new HashMap<>();
public ContainerMemory(Path saveTo) {
this.saveTo = saveTo;
try {
read(Files.readAllBytes(saveTo));
} catch (Exception ex) {
ex.printStackTrace();
inventories.clear();
}
}
private void read(byte[] bytes) throws IOException {
PacketBuffer in = new PacketBuffer(Unpooled.wrappedBuffer(bytes));
int chests = in.readInt();
for (int i = 0; i < chests; i++) {
int x = in.readInt();
int y = in.readInt();
int z = in.readInt();
RememberedInventory rem = new RememberedInventory();
rem.items.addAll(readItemStacks(in));
rem.size = rem.items.size();
rem.windowId = -1;
if (rem.items.isEmpty()) {
continue; // this only happens if the list has no elements, not if the list has elements that are all empty item stacks
}
inventories.put(new BlockPos(x, y, z), rem);
}
}
public synchronized void save() throws IOException {
ByteBuf buf = Unpooled.buffer(0, Integer.MAX_VALUE);
PacketBuffer out = new PacketBuffer(buf);
out.writeInt(inventories.size());
for (Map.Entry<BlockPos, RememberedInventory> entry : inventories.entrySet()) {
out = new PacketBuffer(out.writeInt(entry.getKey().getX()));
out = new PacketBuffer(out.writeInt(entry.getKey().getY()));
out = new PacketBuffer(out.writeInt(entry.getKey().getZ()));
out = writeItemStacks(entry.getValue().getContents(), out);
}
Files.write(saveTo, out.array());
}
public synchronized void setup(BlockPos pos, int windowId, int slotCount) {
RememberedInventory inventory = inventories.computeIfAbsent(pos, x -> new RememberedInventory());
inventory.windowId = windowId;
inventory.size = slotCount;
}
public synchronized Optional<RememberedInventory> getInventoryFromWindow(int windowId) {
return inventories.values().stream().filter(i -> i.windowId == windowId).findFirst();
}
@Override
public final synchronized RememberedInventory getInventoryByPos(BlockPos pos) {
return inventories.get(pos);
}
@Override
public final synchronized Map<BlockPos, IRememberedInventory> getRememberedInventories() {
// make a copy since this map is modified from the packet thread
return new HashMap<>(inventories);
}
public static List<ItemStack> readItemStacks(byte[] bytes) throws IOException {
PacketBuffer in = new PacketBuffer(Unpooled.wrappedBuffer(bytes));
return readItemStacks(in);
}
public static List<ItemStack> readItemStacks(PacketBuffer in) throws IOException {
int count = in.readInt();
List<ItemStack> result = new ArrayList<>();
for (int i = 0; i < count; i++) {
result.add(in.readItemStack());
}
return result;
}
public static byte[] writeItemStacks(List<ItemStack> write) {
ByteBuf buf = Unpooled.buffer(0, Integer.MAX_VALUE);
PacketBuffer out = new PacketBuffer(buf);
out = writeItemStacks(write, out);
return out.array();
}
public static PacketBuffer writeItemStacks(List<ItemStack> write, PacketBuffer out) {
out = new PacketBuffer(out.writeInt(write.size()));
for (ItemStack stack : write) {
out = out.writeItemStack(stack);
}
return out;
}
/**
* An inventory that we are aware of.
* <p>
* Associated with a {@link BlockPos} in {@link ContainerMemory#inventories}.
*/
public static class RememberedInventory implements IRememberedInventory {
/**
* The list of items in the inventory
*/
private final List<ItemStack> items;
/**
* The last known window ID of the inventory
*/
private int windowId;
/**
* The size of the inventory
*/
private int size;
private RememberedInventory() {
this.items = new ArrayList<>();
}
@Override
public final List<ItemStack> getContents() {
return Collections.unmodifiableList(this.items);
}
@Override
public final int getSize() {
return this.size;
}
public void updateFromOpenWindow(IPlayerContext ctx) {
items.clear();
items.addAll(ctx.player().openContainer.getInventory().subList(0, size));
}
}
}
@@ -32,7 +32,7 @@ import java.util.stream.Collectors;
* *
* @author leijurv * @author leijurv
*/ */
public class Waypoints implements IWaypointCollection { public class WaypointCollection implements IWaypointCollection {
/** /**
* Magic value to detect invalid waypoint files * Magic value to detect invalid waypoint files
@@ -42,7 +42,7 @@ public class Waypoints implements IWaypointCollection {
private final Path directory; private final Path directory;
private final Map<IWaypoint.Tag, Set<IWaypoint>> waypoints; private final Map<IWaypoint.Tag, Set<IWaypoint>> waypoints;
Waypoints(Path directory) { WaypointCollection(Path directory) {
this.directory = directory; this.directory = directory;
if (!Files.exists(directory)) { if (!Files.exists(directory)) {
try { try {
+20 -2
View File
@@ -19,9 +19,11 @@ package baritone.cache;
import baritone.Baritone; import baritone.Baritone;
import baritone.api.cache.ICachedWorld; import baritone.api.cache.ICachedWorld;
import baritone.api.cache.IContainerMemory;
import baritone.api.cache.IWaypointCollection; import baritone.api.cache.IWaypointCollection;
import baritone.api.cache.IWorldData; import baritone.api.cache.IWorldData;
import java.io.IOException;
import java.nio.file.Path; import java.nio.file.Path;
/** /**
@@ -32,7 +34,8 @@ import java.nio.file.Path;
public class WorldData implements IWorldData { public class WorldData implements IWorldData {
public final CachedWorld cache; public final CachedWorld cache;
private final Waypoints waypoints; private final WaypointCollection waypoints;
private final ContainerMemory containerMemory;
//public final MapData map; //public final MapData map;
public final Path directory; public final Path directory;
public final int dimension; public final int dimension;
@@ -40,7 +43,8 @@ public class WorldData implements IWorldData {
WorldData(Path directory, int dimension) { WorldData(Path directory, int dimension) {
this.directory = directory; this.directory = directory;
this.cache = new CachedWorld(directory.resolve("cache"), dimension); this.cache = new CachedWorld(directory.resolve("cache"), dimension);
this.waypoints = new Waypoints(directory.resolve("waypoints")); this.waypoints = new WaypointCollection(directory.resolve("waypoints"));
this.containerMemory = new ContainerMemory(directory.resolve("containers"));
this.dimension = dimension; this.dimension = dimension;
} }
@@ -49,6 +53,15 @@ public class WorldData implements IWorldData {
System.out.println("Started saving the world in a new thread"); System.out.println("Started saving the world in a new thread");
cache.save(); cache.save();
}); });
Baritone.getExecutor().execute(() -> {
System.out.println("Started saving saved containers in a new thread");
try {
containerMemory.save();
} catch (IOException e) {
e.printStackTrace();
System.out.println("Failed to save saved containers");
}
});
} }
@Override @Override
@@ -60,4 +73,9 @@ public class WorldData implements IWorldData {
public IWaypointCollection getWaypoints() { public IWaypointCollection getWaypoints() {
return this.waypoints; return this.waypoints;
} }
@Override
public IContainerMemory getContainerMemory() {
return this.containerMemory;
}
} }
+3 -1
View File
@@ -95,7 +95,9 @@ public class WorldProvider implements IWorldProvider, Helper {
} }
System.out.println("Baritone world data dir: " + dir); System.out.println("Baritone world data dir: " + dir);
this.currentWorld = worldCache.computeIfAbsent(dir, d -> new WorldData(d, dimension)); synchronized (worldCache) {
this.currentWorld = worldCache.computeIfAbsent(dir, d -> new WorldData(d, dimension));
}
} }
public final void closeWorld() { public final void closeWorld() {
+53 -29
View File
@@ -23,11 +23,14 @@ import net.minecraft.block.Block;
import net.minecraft.block.state.IBlockState; import net.minecraft.block.state.IBlockState;
import net.minecraft.client.multiplayer.ChunkProviderClient; import net.minecraft.client.multiplayer.ChunkProviderClient;
import net.minecraft.util.math.BlockPos; import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.ChunkPos;
import net.minecraft.world.chunk.BlockStateContainer; import net.minecraft.world.chunk.BlockStateContainer;
import net.minecraft.world.chunk.Chunk; import net.minecraft.world.chunk.Chunk;
import net.minecraft.world.chunk.storage.ExtendedBlockStorage; import net.minecraft.world.chunk.storage.ExtendedBlockStorage;
import java.util.LinkedList; import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List; import java.util.List;
public enum WorldScanner implements IWorldScanner { public enum WorldScanner implements IWorldScanner {
@@ -39,7 +42,7 @@ public enum WorldScanner implements IWorldScanner {
if (blocks.contains(null)) { if (blocks.contains(null)) {
throw new IllegalStateException("Invalid block name should have been caught earlier: " + blocks.toString()); throw new IllegalStateException("Invalid block name should have been caught earlier: " + blocks.toString());
} }
LinkedList<BlockPos> res = new LinkedList<>(); ArrayList<BlockPos> res = new ArrayList<>();
if (blocks.isEmpty()) { if (blocks.isEmpty()) {
return res; return res;
} }
@@ -69,33 +72,7 @@ public enum WorldScanner implements IWorldScanner {
continue; continue;
} }
allUnloaded = false; allUnloaded = false;
ExtendedBlockStorage[] chunkInternalStorageArray = chunk.getBlockStorageArray(); scanChunkInto(chunkX << 4, chunkZ << 4, chunk, blocks, res, max, yLevelThreshold, playerY);
chunkX = chunkX << 4;
chunkZ = chunkZ << 4;
for (int y0 = 0; y0 < 16; y0++) {
ExtendedBlockStorage extendedblockstorage = chunkInternalStorageArray[y0];
if (extendedblockstorage == null) {
continue;
}
int yReal = y0 << 4;
BlockStateContainer bsc = extendedblockstorage.getData();
// the mapping of BlockStateContainer.getIndex from xyz to index is y << 8 | z << 4 | x;
// for better cache locality, iterate in that order
for (int y = 0; y < 16; y++) {
for (int z = 0; z < 16; z++) {
for (int x = 0; x < 16; x++) {
IBlockState state = bsc.get(x, y, z);
if (blocks.contains(state.getBlock())) {
int yy = yReal | y;
res.add(new BlockPos(chunkX | x, yy, chunkZ | z));
if (Math.abs(yy - playerY) < yLevelThreshold) {
foundWithinY = true;
}
}
}
}
}
}
} }
} }
if ((allUnloaded && foundChunks) if ((allUnloaded && foundChunks)
@@ -107,4 +84,51 @@ public enum WorldScanner implements IWorldScanner {
searchRadiusSq++; searchRadiusSq++;
} }
} }
@Override
public List<BlockPos> scanChunk(IPlayerContext ctx, List<Block> blocks, ChunkPos pos, int max, int yLevelThreshold) {
if (blocks.isEmpty()) {
return Collections.emptyList();
}
ChunkProviderClient chunkProvider = (ChunkProviderClient) ctx.world().getChunkProvider();
Chunk chunk = chunkProvider.getLoadedChunk(pos.x, pos.z);
int playerY = ctx.playerFeet().getY();
if (chunk == null || chunk.isEmpty()) {
return Collections.emptyList();
}
ArrayList<BlockPos> res = new ArrayList<>();
scanChunkInto(pos.x << 4, pos.z << 4, chunk, blocks, res, max, yLevelThreshold, playerY);
return res;
}
public void scanChunkInto(int chunkX, int chunkZ, Chunk chunk, List<Block> search, Collection<BlockPos> result, int max, int yLevelThreshold, int playerY) {
ExtendedBlockStorage[] chunkInternalStorageArray = chunk.getBlockStorageArray();
for (int y0 = 0; y0 < 16; y0++) {
ExtendedBlockStorage extendedblockstorage = chunkInternalStorageArray[y0];
if (extendedblockstorage == null) {
continue;
}
int yReal = y0 << 4;
BlockStateContainer bsc = extendedblockstorage.getData();
// the mapping of BlockStateContainer.getIndex from xyz to index is y << 8 | z << 4 | x;
// for better cache locality, iterate in that order
for (int y = 0; y < 16; y++) {
for (int z = 0; z < 16; z++) {
for (int x = 0; x < 16; x++) {
IBlockState state = bsc.get(x, y, z);
if (search.contains(state.getBlock())) {
int yy = yReal | y;
result.add(new BlockPos(chunkX | x, yy, chunkZ | z));
if (result.size() >= max || Math.abs(yy - playerY) < yLevelThreshold) {
return;
}
}
}
}
}
}
}
} }
@@ -27,10 +27,9 @@ import baritone.pathing.movement.CalculationContext;
import baritone.pathing.movement.Moves; import baritone.pathing.movement.Moves;
import baritone.utils.Helper; import baritone.utils.Helper;
import baritone.utils.pathing.BetterWorldBorder; import baritone.utils.pathing.BetterWorldBorder;
import baritone.utils.pathing.Favoring;
import baritone.utils.pathing.MutableMoveResult; import baritone.utils.pathing.MutableMoveResult;
import it.unimi.dsi.fastutil.longs.LongOpenHashSet;
import java.util.HashSet;
import java.util.Optional; import java.util.Optional;
/** /**
@@ -40,12 +39,12 @@ import java.util.Optional;
*/ */
public final class AStarPathFinder extends AbstractNodeCostSearch implements Helper { public final class AStarPathFinder extends AbstractNodeCostSearch implements Helper {
private final LongOpenHashSet favoredPositions; private final Favoring favoring;
private final CalculationContext calcContext; private final CalculationContext calcContext;
public AStarPathFinder(int startX, int startY, int startZ, Goal goal, LongOpenHashSet favoredPositions, CalculationContext context) { public AStarPathFinder(int startX, int startY, int startZ, Goal goal, Favoring favoring, CalculationContext context) {
super(startX, startY, startZ, goal, context); super(startX, startY, startZ, goal, context);
this.favoredPositions = favoredPositions; this.favoring = favoring;
this.calcContext = context; this.calcContext = context;
} }
@@ -64,7 +63,7 @@ public final class AStarPathFinder extends AbstractNodeCostSearch implements Hel
bestSoFar[i] = startNode; bestSoFar[i] = startNode;
} }
MutableMoveResult res = new MutableMoveResult(); MutableMoveResult res = new MutableMoveResult();
LongOpenHashSet favored = favoredPositions; Favoring favored = favoring;
BetterWorldBorder worldBorder = new BetterWorldBorder(calcContext.world().getWorldBorder()); BetterWorldBorder worldBorder = new BetterWorldBorder(calcContext.world().getWorldBorder());
long startTime = System.nanoTime() / 1000000L; long startTime = System.nanoTime() / 1000000L;
boolean slowPath = Baritone.settings().slowPath.get(); boolean slowPath = Baritone.settings().slowPath.get();
@@ -77,14 +76,16 @@ public final class AStarPathFinder extends AbstractNodeCostSearch implements Hel
int numNodes = 0; int numNodes = 0;
int numMovementsConsidered = 0; int numMovementsConsidered = 0;
int numEmptyChunk = 0; int numEmptyChunk = 0;
boolean favoring = favored != null; boolean favoring = !favored.isEmpty();
int timeCheckInterval = 1 << 6;
int pathingMaxChunkBorderFetch = Baritone.settings().pathingMaxChunkBorderFetch.get(); // grab all settings beforehand so that changing settings during pathing doesn't cause a crash or unpredictable behavior int pathingMaxChunkBorderFetch = Baritone.settings().pathingMaxChunkBorderFetch.get(); // grab all settings beforehand so that changing settings during pathing doesn't cause a crash or unpredictable behavior
double favorCoeff = Baritone.settings().backtrackCostFavoringCoefficient.get();
boolean minimumImprovementRepropagation = Baritone.settings().minimumImprovementRepropagation.get(); boolean minimumImprovementRepropagation = Baritone.settings().minimumImprovementRepropagation.get();
while (!openSet.isEmpty() && numEmptyChunk < pathingMaxChunkBorderFetch && !cancelRequested) { while (!openSet.isEmpty() && numEmptyChunk < pathingMaxChunkBorderFetch && !cancelRequested) {
long now = System.nanoTime() / 1000000L; if ((numNodes & (timeCheckInterval - 1)) == 0) { // only call this once every 64 nodes (about half a millisecond)
if (now - failureTimeoutTime >= 0 || (!failing && now - primaryTimeoutTime >= 0)) { long now = System.nanoTime() / 1000000L; // since nanoTime is slow on windows (takes many microseconds)
break; if (now - failureTimeoutTime >= 0 || (!failing && now - primaryTimeoutTime >= 0)) {
break;
}
} }
if (slowPath) { if (slowPath) {
try { try {
@@ -123,7 +124,7 @@ public final class AStarPathFinder extends AbstractNodeCostSearch implements Hel
if (actionCost >= ActionCosts.COST_INF) { if (actionCost >= ActionCosts.COST_INF) {
continue; continue;
} }
if (actionCost <= 0) { if (actionCost <= 0 || Double.isNaN(actionCost)) {
throw new IllegalStateException(moves + " calculated implausible cost " + actionCost); throw new IllegalStateException(moves + " calculated implausible cost " + actionCost);
} }
if (moves.dynamicXZ && !worldBorder.entirelyContains(res.x, res.z)) { // see issue #218 if (moves.dynamicXZ && !worldBorder.entirelyContains(res.x, res.z)) { // see issue #218
@@ -137,9 +138,9 @@ public final class AStarPathFinder extends AbstractNodeCostSearch implements Hel
throw new IllegalStateException(moves + " " + res.y + " " + (currentNode.y + moves.yOffset)); throw new IllegalStateException(moves + " " + res.y + " " + (currentNode.y + moves.yOffset));
} }
long hashCode = BetterBlockPos.longHash(res.x, res.y, res.z); long hashCode = BetterBlockPos.longHash(res.x, res.y, res.z);
if (favoring && favored.contains(hashCode)) { if (favoring) {
// see issue #18 // see issue #18
actionCost *= favorCoeff; actionCost *= favored.calculate(hashCode);
} }
PathNode neighbor = getNodeAtPosition(res.x, res.y, res.z, hashCode); PathNode neighbor = getNodeAtPosition(res.x, res.y, res.z, hashCode);
double tentativeCost = currentNode.cost + actionCost; double tentativeCost = currentNode.cost + actionCost;
@@ -21,6 +21,7 @@ import baritone.Baritone;
import baritone.api.pathing.calc.IPath; import baritone.api.pathing.calc.IPath;
import baritone.api.pathing.calc.IPathFinder; import baritone.api.pathing.calc.IPathFinder;
import baritone.api.pathing.goals.Goal; import baritone.api.pathing.goals.Goal;
import baritone.api.utils.BetterBlockPos;
import baritone.api.utils.PathCalculationResult; import baritone.api.utils.PathCalculationResult;
import baritone.pathing.movement.CalculationContext; import baritone.pathing.movement.CalculationContext;
import baritone.utils.Helper; import baritone.utils.Helper;
@@ -135,9 +136,15 @@ public abstract class AbstractNodeCostSearch implements IPathFinder {
* for the node mapped to the specified pos. If no node is found, * for the node mapped to the specified pos. If no node is found,
* a new node is created. * a new node is created.
* *
* @param x The x position of the node
* @param y The y position of the node
* @param z The z position of the node
* @param hashCode The hash code of the node, provided by {@link BetterBlockPos#longHash(int, int, int)}
*
* @return The associated node * @return The associated node
* @see <a href="https://github.com/cabaletta/baritone/issues/107">Issue #107</a> * @see <a href="https://github.com/cabaletta/baritone/issues/107">Issue #107</a>
*/ */
protected PathNode getNodeAtPosition(int x, int y, int z, long hashCode) { protected PathNode getNodeAtPosition(int x, int y, int z, long hashCode) {
PathNode node = map.get(hashCode); PathNode node = map.get(hashCode);
if (node == null) { if (node == null) {
@@ -73,6 +73,9 @@ public final class PathNode {
this.previous = null; this.previous = null;
this.cost = ActionCosts.COST_INF; this.cost = ActionCosts.COST_INF;
this.estimatedCostToGoal = goal.heuristic(x, y, z); this.estimatedCostToGoal = goal.heuristic(x, y, z);
if (Double.isNaN(estimatedCostToGoal)) {
throw new IllegalStateException(goal + " calculated implausible heuristic");
}
this.isOpen = false; this.isOpen = false;
this.x = x; this.x = x;
this.y = y; this.y = y;
@@ -59,7 +59,7 @@ public final class BinaryHeapOpenSet implements IOpenSet {
@Override @Override
public final void insert(PathNode value) { public final void insert(PathNode value) {
if (size >= array.length - 1) { if (size >= array.length - 1) {
array = Arrays.copyOf(array, array.length * 2); array = Arrays.copyOf(array, array.length << 1);
} }
size++; size++;
value.heapPosition = size; value.heapPosition = size;
@@ -53,6 +53,10 @@ public class CalculationContext {
private final boolean canSprint; private final boolean canSprint;
private final double placeBlockCost; private final double placeBlockCost;
private final boolean allowBreak; private final boolean allowBreak;
private final boolean allowParkour;
private final boolean allowParkourPlace;
private final boolean allowJumpAt256;
private final boolean assumeWalkOnWater;
private final int maxFallHeightNoWater; private final int maxFallHeightNoWater;
private final int maxFallHeightBucket; private final int maxFallHeightBucket;
private final double waterWalkSpeed; private final double waterWalkSpeed;
@@ -76,6 +80,10 @@ public class CalculationContext {
this.canSprint = Baritone.settings().allowSprint.get() && player.getFoodStats().getFoodLevel() > 6; this.canSprint = Baritone.settings().allowSprint.get() && player.getFoodStats().getFoodLevel() > 6;
this.placeBlockCost = Baritone.settings().blockPlacementPenalty.get(); this.placeBlockCost = Baritone.settings().blockPlacementPenalty.get();
this.allowBreak = Baritone.settings().allowBreak.get(); this.allowBreak = Baritone.settings().allowBreak.get();
this.allowParkour = Baritone.settings().allowParkour.get();
this.allowParkourPlace = Baritone.settings().allowParkourPlace.get();
this.allowJumpAt256 = Baritone.settings().allowJumpAt256.get();
this.assumeWalkOnWater = Baritone.settings().assumeWalkOnWater.get();
this.maxFallHeightNoWater = Baritone.settings().maxFallHeightNoWater.get(); this.maxFallHeightNoWater = Baritone.settings().maxFallHeightNoWater.get();
this.maxFallHeightBucket = Baritone.settings().maxFallHeightBucket.get(); this.maxFallHeightBucket = Baritone.settings().maxFallHeightBucket.get();
int depth = EnchantmentHelper.getDepthStriderModifier(player); int depth = EnchantmentHelper.getDepthStriderModifier(player);
@@ -173,6 +181,22 @@ public class CalculationContext {
return allowBreak; return allowBreak;
} }
public boolean allowParkour() {
return allowParkour;
}
public boolean allowParkourPlace() {
return allowParkourPlace;
}
public boolean allowJumpAt256() {
return allowJumpAt256;
}
public boolean assumeWalkOnWater() {
return assumeWalkOnWater;
}
public int maxFallHeightNoWater() { public int maxFallHeightNoWater() {
return maxFallHeightNoWater; return maxFallHeightNoWater;
} }
@@ -36,6 +36,7 @@ import java.util.Optional;
public abstract class Movement implements IMovement, MovementHelper { public abstract class Movement implements IMovement, MovementHelper {
protected static final EnumFacing[] HORIZONTALS = {EnumFacing.NORTH, EnumFacing.SOUTH, EnumFacing.EAST, EnumFacing.WEST}; protected static final EnumFacing[] HORIZONTALS = {EnumFacing.NORTH, EnumFacing.SOUTH, EnumFacing.EAST, EnumFacing.WEST};
protected static final EnumFacing[] HORIZONTALS_BUT_ALSO_DOWN____SO_EVERY_DIRECTION_EXCEPT_UP = {EnumFacing.NORTH, EnumFacing.SOUTH, EnumFacing.EAST, EnumFacing.WEST, EnumFacing.DOWN};
protected final IBaritone baritone; protected final IBaritone baritone;
protected final IPlayerContext ctx; protected final IPlayerContext ctx;
@@ -205,10 +206,10 @@ public abstract class Movement implements IMovement, MovementHelper {
} }
/** /**
* Calculate latest movement state. * Calculate latest movement state. Gets called once a tick.
* Gets called once a tick.
* *
* @return * @param state The current state
* @return The new state
*/ */
public MovementState updateState(MovementState state) { public MovementState updateState(MovementState state) {
if (!prepared(state)) { if (!prepared(state)) {
@@ -35,8 +35,6 @@ import net.minecraft.item.ItemStack;
import net.minecraft.util.EnumFacing; import net.minecraft.util.EnumFacing;
import net.minecraft.util.NonNullList; import net.minecraft.util.NonNullList;
import net.minecraft.util.math.BlockPos; import net.minecraft.util.math.BlockPos;
import net.minecraft.world.World;
import net.minecraft.world.chunk.EmptyChunk;
/** /**
* Static helpers for cost calculation * Static helpers for cost calculation
@@ -126,7 +124,11 @@ public interface MovementHelper extends ActionCosts, Helper {
* canWalkThrough but also won't impede movement at all. so not including doors or fence gates (we'd have to right click), * canWalkThrough but also won't impede movement at all. so not including doors or fence gates (we'd have to right click),
* not including water, and not including ladders or vines or cobwebs (they slow us down) * not including water, and not including ladders or vines or cobwebs (they slow us down)
* *
* @return * @param context Calculation context to provide block state lookup
* @param x The block's x position
* @param y The block's y position
* @param z The block's z position
* @return Whether or not the block at the specified position
*/ */
static boolean fullyPassable(CalculationContext context, int x, int y, int z) { static boolean fullyPassable(CalculationContext context, int x, int y, int z) {
return fullyPassable(context.get(x, y, z)); return fullyPassable(context.get(x, y, z));
@@ -156,7 +158,7 @@ public interface MovementHelper extends ActionCosts, Helper {
return block.isPassable(null, null); return block.isPassable(null, null);
} }
static boolean isReplacable(int x, int y, int z, IBlockState state, World world) { static boolean isReplacable(int x, int y, int z, IBlockState state, BlockStateInterface bsi) {
// for MovementTraverse and MovementAscend // for MovementTraverse and MovementAscend
// block double plant defaults to true when the block doesn't match, so don't need to check that case // block double plant defaults to true when the block doesn't match, so don't need to check that case
// all other overrides just return true or false // all other overrides just return true or false
@@ -168,9 +170,13 @@ public interface MovementHelper extends ActionCosts, Helper {
* } * }
*/ */
Block block = state.getBlock(); Block block = state.getBlock();
if (block == Blocks.AIR || isWater(block)) {
// early return for common cases hehe
return true;
}
if (block instanceof BlockSnow) { if (block instanceof BlockSnow) {
// as before, default to true (mostly because it would otherwise make long distance pathing through snowy biomes impossible) // as before, default to true (mostly because it would otherwise make long distance pathing through snowy biomes impossible)
if (world.getChunk(x >> 4, z >> 4) instanceof EmptyChunk) { if (!bsi.worldContainsLoadedChunk(x, z)) {
return true; return true;
} }
return state.getValue(BlockSnow.LAYERS) == 1; return state.getValue(BlockSnow.LAYERS) == 1;
@@ -243,7 +249,12 @@ public interface MovementHelper extends ActionCosts, Helper {
* through? Includes water because we know that we automatically jump on * through? Includes water because we know that we automatically jump on
* water * water
* *
* @return * @param bsi Block state provider
* @param x The block's x position
* @param y The block's y position
* @param z The block's z position
* @param state The state of the block at the specified location
* @return Whether or not the specified block can be walked on
*/ */
static boolean canWalkOn(BlockStateInterface bsi, int x, int y, int z, IBlockState state) { static boolean canWalkOn(BlockStateInterface bsi, int x, int y, int z, IBlockState state) {
Block block = state.getBlock(); Block block = state.getBlock();
@@ -367,7 +378,8 @@ public interface MovementHelper extends ActionCosts, Helper {
/** /**
* AutoTool for a specific block * AutoTool for a specific block
* *
* @param b the blockstate to mine * @param ctx The player context
* @param b the blockstate to mine
*/ */
static void switchToBestToolFor(IPlayerContext ctx, IBlockState b) { static void switchToBestToolFor(IPlayerContext ctx, IBlockState b) {
switchToBestToolFor(ctx, b, new ToolSet(ctx.player())); switchToBestToolFor(ctx, b, new ToolSet(ctx.player()));
@@ -376,8 +388,9 @@ public interface MovementHelper extends ActionCosts, Helper {
/** /**
* AutoTool for a specific block with precomputed ToolSet data * AutoTool for a specific block with precomputed ToolSet data
* *
* @param b the blockstate to mine * @param ctx The player context
* @param ts previously calculated ToolSet * @param b the blockstate to mine
* @param ts previously calculated ToolSet
*/ */
static void switchToBestToolFor(IPlayerContext ctx, IBlockState b, ToolSet ts) { static void switchToBestToolFor(IPlayerContext ctx, IBlockState b, ToolSet ts) {
ctx.player().inventory.currentItem = ts.getBestSlot(b.getBlock()); ctx.player().inventory.currentItem = ts.getBestSlot(b.getBlock());
@@ -220,51 +220,59 @@ public enum Moves {
} }
}, },
DIAGONAL_NORTHEAST(+1, 0, -1) { DIAGONAL_NORTHEAST(+1, 0, -1, false, true) {
@Override @Override
public Movement apply0(CalculationContext context, BetterBlockPos src) { public Movement apply0(CalculationContext context, BetterBlockPos src) {
return new MovementDiagonal(context.getBaritone(), src, EnumFacing.NORTH, EnumFacing.EAST); MutableMoveResult res = new MutableMoveResult();
apply(context, src.x, src.y, src.z, res);
return new MovementDiagonal(context.getBaritone(), src, EnumFacing.NORTH, EnumFacing.EAST, res.y - src.y);
} }
@Override @Override
public double cost(CalculationContext context, int x, int y, int z) { public void apply(CalculationContext context, int x, int y, int z, MutableMoveResult result) {
return MovementDiagonal.cost(context, x, y, z, x + 1, z - 1); MovementDiagonal.cost(context, x, y, z, x + 1, z - 1, result);
} }
}, },
DIAGONAL_NORTHWEST(-1, 0, -1) { DIAGONAL_NORTHWEST(-1, 0, -1, false, true) {
@Override @Override
public Movement apply0(CalculationContext context, BetterBlockPos src) { public Movement apply0(CalculationContext context, BetterBlockPos src) {
return new MovementDiagonal(context.getBaritone(), src, EnumFacing.NORTH, EnumFacing.WEST); MutableMoveResult res = new MutableMoveResult();
apply(context, src.x, src.y, src.z, res);
return new MovementDiagonal(context.getBaritone(), src, EnumFacing.NORTH, EnumFacing.WEST, res.y - src.y);
} }
@Override @Override
public double cost(CalculationContext context, int x, int y, int z) { public void apply(CalculationContext context, int x, int y, int z, MutableMoveResult result) {
return MovementDiagonal.cost(context, x, y, z, x - 1, z - 1); MovementDiagonal.cost(context, x, y, z, x - 1, z - 1, result);
} }
}, },
DIAGONAL_SOUTHEAST(+1, 0, +1) { DIAGONAL_SOUTHEAST(+1, 0, +1, false, true) {
@Override @Override
public Movement apply0(CalculationContext context, BetterBlockPos src) { public Movement apply0(CalculationContext context, BetterBlockPos src) {
return new MovementDiagonal(context.getBaritone(), src, EnumFacing.SOUTH, EnumFacing.EAST); MutableMoveResult res = new MutableMoveResult();
apply(context, src.x, src.y, src.z, res);
return new MovementDiagonal(context.getBaritone(), src, EnumFacing.SOUTH, EnumFacing.EAST, res.y - src.y);
} }
@Override @Override
public double cost(CalculationContext context, int x, int y, int z) { public void apply(CalculationContext context, int x, int y, int z, MutableMoveResult result) {
return MovementDiagonal.cost(context, x, y, z, x + 1, z + 1); MovementDiagonal.cost(context, x, y, z, x + 1, z + 1, result);
} }
}, },
DIAGONAL_SOUTHWEST(-1, 0, +1) { DIAGONAL_SOUTHWEST(-1, 0, +1, false, true) {
@Override @Override
public Movement apply0(CalculationContext context, BetterBlockPos src) { public Movement apply0(CalculationContext context, BetterBlockPos src) {
return new MovementDiagonal(context.getBaritone(), src, EnumFacing.SOUTH, EnumFacing.WEST); MutableMoveResult res = new MutableMoveResult();
apply(context, src.x, src.y, src.z, res);
return new MovementDiagonal(context.getBaritone(), src, EnumFacing.SOUTH, EnumFacing.WEST, res.y - src.y);
} }
@Override @Override
public double cost(CalculationContext context, int x, int y, int z) { public void apply(CalculationContext context, int x, int y, int z, MutableMoveResult result) {
return MovementDiagonal.cost(context, x, y, z, x - 1, z + 1); MovementDiagonal.cost(context, x, y, z, x - 1, z + 1, result);
} }
}, },
@@ -57,36 +57,23 @@ public class MovementAscend extends Movement {
} }
public static double cost(CalculationContext context, int x, int y, int z, int destX, int destZ) { public static double cost(CalculationContext context, int x, int y, int z, int destX, int destZ) {
IBlockState srcDown = context.get(x, y - 1, z);
if (srcDown.getBlock() == Blocks.LADDER || srcDown.getBlock() == Blocks.VINE) {
return COST_INF;
}
// we can jump from soul sand, but not from a bottom slab
boolean jumpingFromBottomSlab = MovementHelper.isBottomSlab(srcDown);
IBlockState toPlace = context.get(destX, y, destZ); IBlockState toPlace = context.get(destX, y, destZ);
boolean jumpingToBottomSlab = MovementHelper.isBottomSlab(toPlace);
if (jumpingFromBottomSlab && !jumpingToBottomSlab) {
return COST_INF;// the only thing we can ascend onto from a bottom slab is another bottom slab
}
boolean hasToPlace = false; boolean hasToPlace = false;
if (!MovementHelper.canWalkOn(context.bsi(), destX, y, destZ, toPlace)) { if (!MovementHelper.canWalkOn(context.bsi(), destX, y, destZ, toPlace)) {
if (!context.canPlaceThrowawayAt(destX, y, destZ)) { if (!context.canPlaceThrowawayAt(destX, y, destZ)) {
return COST_INF; return COST_INF;
} }
if (toPlace.getBlock() != Blocks.AIR && !MovementHelper.isWater(toPlace.getBlock()) && !MovementHelper.isReplacable(destX, y, destZ, toPlace, context.world())) { if (!MovementHelper.isReplacable(destX, y, destZ, toPlace, context.bsi())) {
return COST_INF; return COST_INF;
} }
// TODO: add ability to place against .down() as well as the cardinal directions for (int i = 0; i < 5; i++) {
// useful for when you are starting a staircase without anything to place against int againstX = destX + HORIZONTALS_BUT_ALSO_DOWN____SO_EVERY_DIRECTION_EXCEPT_UP[i].getXOffset();
// Counterpoint to the above TODO ^ you should move then pillar instead of ascend int againstY = y + HORIZONTALS_BUT_ALSO_DOWN____SO_EVERY_DIRECTION_EXCEPT_UP[i].getYOffset();
for (int i = 0; i < 4; i++) { int againstZ = destZ + HORIZONTALS_BUT_ALSO_DOWN____SO_EVERY_DIRECTION_EXCEPT_UP[i].getZOffset();
int againstX = destX + HORIZONTALS[i].getXOffset(); if (againstX == x && againstZ == z) { // we might be able to backplace now, but it doesn't matter because it will have been broken by the time we'd need to use it
int againstZ = destZ + HORIZONTALS[i].getZOffset();
if (againstX == x && againstZ == z) {
continue; continue;
} }
if (MovementHelper.canPlaceAgainst(context.bsi(), againstX, y, againstZ)) { if (MovementHelper.canPlaceAgainst(context.bsi(), againstX, againstY, againstZ)) {
hasToPlace = true; hasToPlace = true;
break; break;
} }
@@ -95,8 +82,8 @@ public class MovementAscend extends Movement {
return COST_INF; return COST_INF;
} }
} }
IBlockState srcUp2 = null; IBlockState srcUp2 = context.get(x, y + 2, z); // used lower down anyway
if (context.get(x, y + 3, z).getBlock() instanceof BlockFalling && (MovementHelper.canWalkThrough(context.bsi(), x, y + 1, z) || !((srcUp2 = context.get(x, y + 2, z)).getBlock() instanceof BlockFalling))) {//it would fall on us and possibly suffocate us if (context.get(x, y + 3, z).getBlock() instanceof BlockFalling && (MovementHelper.canWalkThrough(context.bsi(), x, y + 1, z) || !(srcUp2.getBlock() instanceof BlockFalling))) {//it would fall on us and possibly suffocate us
// HOWEVER, we assume that we're standing in the start position // HOWEVER, we assume that we're standing in the start position
// that means that src and src.up(1) are both air // that means that src and src.up(1) are both air
// maybe they aren't now, but they will be by the time this starts // maybe they aren't now, but they will be by the time this starts
@@ -114,6 +101,16 @@ public class MovementAscend extends Movement {
// it's possible srcUp is AIR from the start, and srcUp2 is falling // it's possible srcUp is AIR from the start, and srcUp2 is falling
// and in that scenario, when we arrive and break srcUp2, that lets srcUp3 fall on us and suffocate us // and in that scenario, when we arrive and break srcUp2, that lets srcUp3 fall on us and suffocate us
} }
IBlockState srcDown = context.get(x, y - 1, z);
if (srcDown.getBlock() == Blocks.LADDER || srcDown.getBlock() == Blocks.VINE) {
return COST_INF;
}
// we can jump from soul sand, but not from a bottom slab
boolean jumpingFromBottomSlab = MovementHelper.isBottomSlab(srcDown);
boolean jumpingToBottomSlab = MovementHelper.isBottomSlab(toPlace);
if (jumpingFromBottomSlab && !jumpingToBottomSlab) {
return COST_INF;// the only thing we can ascend onto from a bottom slab is another bottom slab
}
double walk; double walk;
if (jumpingToBottomSlab) { if (jumpingToBottomSlab) {
if (jumpingFromBottomSlab) { if (jumpingFromBottomSlab) {
@@ -136,10 +133,9 @@ public class MovementAscend extends Movement {
if (hasToPlace) { if (hasToPlace) {
totalCost += context.placeBlockCost(); totalCost += context.placeBlockCost();
} }
if (srcUp2 == null) { // start with srcUp2 since we already have its state
srcUp2 = context.get(x, y + 2, z); // includeFalling isn't needed because of the falling check above -- if srcUp3 is falling we will have already exited with COST_INF if we'd actually have to break it
} totalCost += MovementHelper.getMiningDurationTicks(context, x, y + 2, z, srcUp2, false);
totalCost += MovementHelper.getMiningDurationTicks(context, x, y + 2, z, srcUp2, false); // TODO MAKE ABSOLUTELY SURE we don't need includeFalling here, from the falling check above
if (totalCost >= COST_INF) { if (totalCost >= COST_INF) {
return COST_INF; return COST_INF;
} }
@@ -166,8 +162,8 @@ public class MovementAscend extends Movement {
IBlockState jumpingOnto = BlockStateInterface.get(ctx, positionToPlace); IBlockState jumpingOnto = BlockStateInterface.get(ctx, positionToPlace);
if (!MovementHelper.canWalkOn(ctx, positionToPlace, jumpingOnto)) { if (!MovementHelper.canWalkOn(ctx, positionToPlace, jumpingOnto)) {
for (int i = 0; i < 4; i++) { for (int i = 0; i < 5; i++) {
BlockPos anAgainst = positionToPlace.offset(HORIZONTALS[i]); BlockPos anAgainst = positionToPlace.offset(HORIZONTALS_BUT_ALSO_DOWN____SO_EVERY_DIRECTION_EXCEPT_UP[i]);
if (anAgainst.equals(src)) { if (anAgainst.equals(src)) {
continue; continue;
} }
@@ -17,7 +17,6 @@
package baritone.pathing.movement.movements; package baritone.pathing.movement.movements;
import baritone.Baritone;
import baritone.api.IBaritone; import baritone.api.IBaritone;
import baritone.api.pathing.movement.MovementStatus; import baritone.api.pathing.movement.MovementStatus;
import baritone.api.utils.BetterBlockPos; import baritone.api.utils.BetterBlockPos;
@@ -63,11 +62,6 @@ public class MovementDescend extends Movement {
} }
public static void cost(CalculationContext context, int x, int y, int z, int destX, int destZ, MutableMoveResult res) { public static void cost(CalculationContext context, int x, int y, int z, int destX, int destZ, MutableMoveResult res) {
Block fromDown = context.get(x, y - 1, z).getBlock();
if (fromDown == Blocks.LADDER || fromDown == Blocks.VINE) {
return;
}
double totalCost = 0; double totalCost = 0;
IBlockState destDown = context.get(destX, y - 1, destZ); IBlockState destDown = context.get(destX, y - 1, destZ);
totalCost += MovementHelper.getMiningDurationTicks(context, destX, y - 1, destZ, destDown, false); totalCost += MovementHelper.getMiningDurationTicks(context, destX, y - 1, destZ, destDown, false);
@@ -83,6 +77,11 @@ public class MovementDescend extends Movement {
return; return;
} }
Block fromDown = context.get(x, y - 1, z).getBlock();
if (fromDown == Blocks.LADDER || fromDown == Blocks.VINE) {
return;
}
// A // A
//SA //SA
// A // A
@@ -107,7 +106,7 @@ public class MovementDescend extends Movement {
double walk = WALK_OFF_BLOCK_COST; double walk = WALK_OFF_BLOCK_COST;
if (fromDown == Blocks.SOUL_SAND) { if (fromDown == Blocks.SOUL_SAND) {
// use this ratio to apply the soul sand speed penalty to our 0.8 block distance // use this ratio to apply the soul sand speed penalty to our 0.8 block distance
walk = WALK_ONE_OVER_SOUL_SAND_COST; walk *= WALK_ONE_OVER_SOUL_SAND_COST / WALK_ONE_BLOCK_COST;
} }
totalCost += walk + Math.max(FALL_N_BLOCKS_COST[1], CENTER_AFTER_FALL_COST); totalCost += walk + Math.max(FALL_N_BLOCKS_COST[1], CENTER_AFTER_FALL_COST);
res.x = destX; res.x = destX;
@@ -116,66 +115,84 @@ public class MovementDescend extends Movement {
res.cost = totalCost; res.cost = totalCost;
} }
public static void dynamicFallCost(CalculationContext context, int x, int y, int z, int destX, int destZ, double frontBreak, IBlockState below, MutableMoveResult res) { public static boolean dynamicFallCost(CalculationContext context, int x, int y, int z, int destX, int destZ, double frontBreak, IBlockState below, MutableMoveResult res) {
if (frontBreak != 0 && context.get(destX, y + 2, destZ).getBlock() instanceof BlockFalling) { if (frontBreak != 0 && context.get(destX, y + 2, destZ).getBlock() instanceof BlockFalling) {
// if frontBreak is 0 we can actually get through this without updating the falling block and making it actually fall // if frontBreak is 0 we can actually get through this without updating the falling block and making it actually fall
// but if frontBreak is nonzero, we're breaking blocks in front, so don't let anything fall through this column, // but if frontBreak is nonzero, we're breaking blocks in front, so don't let anything fall through this column,
// and potentially replace the water we're going to fall into // and potentially replace the water we're going to fall into
return; return false;
} }
if (!MovementHelper.canWalkThrough(context.bsi(), destX, y - 2, destZ, below) && below.getBlock() != Blocks.WATER) { if (!MovementHelper.canWalkThrough(context.bsi(), destX, y - 2, destZ, below) && below.getBlock() != Blocks.WATER) {
return; return false;
} }
double costSoFar = 0;
int effectiveStartHeight = y;
for (int fallHeight = 3; true; fallHeight++) { for (int fallHeight = 3; true; fallHeight++) {
int newY = y - fallHeight; int newY = y - fallHeight;
if (newY < 0) { if (newY < 0) {
// when pathing in the end, where you could plausibly fall into the void // when pathing in the end, where you could plausibly fall into the void
// this check prevents it from getting the block at y=-1 and crashing // this check prevents it from getting the block at y=-1 and crashing
return; return false;
} }
IBlockState ontoBlock = context.get(destX, newY, destZ); IBlockState ontoBlock = context.get(destX, newY, destZ);
double tentativeCost = WALK_OFF_BLOCK_COST + FALL_N_BLOCKS_COST[fallHeight] + frontBreak; int unprotectedFallHeight = fallHeight - (y - effectiveStartHeight); // equal to fallHeight - y + effectiveFallHeight, which is equal to -newY + effectiveFallHeight, which is equal to effectiveFallHeight - newY
if (ontoBlock.getBlock() == Blocks.WATER && !MovementHelper.isFlowing(ontoBlock) && context.getBlock(destX, newY + 1, destZ) != Blocks.WATERLILY) { // TODO flowing check required here? double tentativeCost = WALK_OFF_BLOCK_COST + FALL_N_BLOCKS_COST[unprotectedFallHeight] + frontBreak + costSoFar;
if (ontoBlock.getBlock() == Blocks.WATER && context.getBlock(destX, newY + 1, destZ) != Blocks.WATERLILY) {
// lilypads are canWalkThrough, but we can't end a fall that should be broken by water if it's covered by a lilypad // lilypads are canWalkThrough, but we can't end a fall that should be broken by water if it's covered by a lilypad
// however, don't return impossible in the lilypad scenario, because we could still jump right on it (water that's below a lilypad is canWalkOn so it works) // however, don't return impossible in the lilypad scenario, because we could still jump right on it (water that's below a lilypad is canWalkOn so it works)
if (Baritone.settings().assumeWalkOnWater.get()) { if (context.assumeWalkOnWater()) {
return; // TODO fix return false; // TODO fix
}
if (MovementHelper.isFlowing(ontoBlock)) {
return false; // TODO flowing check required here?
}
if (!MovementHelper.canWalkOn(context.bsi(), destX, newY - 1, destZ)) {
// we could punch right through the water into something else
return false;
} }
// found a fall into water // found a fall into water
res.x = destX; res.x = destX;
res.y = newY; res.y = newY;
res.z = destZ; res.z = destZ;
res.cost = tentativeCost;// TODO incorporate water swim up cost? res.cost = tentativeCost;// TODO incorporate water swim up cost?
return; return false;
} }
if (ontoBlock.getBlock() == Blocks.FLOWING_WATER) { if (ontoBlock.getBlock() == Blocks.FLOWING_WATER) {
return; return false;
}
if (unprotectedFallHeight <= 11 && (ontoBlock.getBlock() == Blocks.VINE || ontoBlock.getBlock() == Blocks.LADDER)) {
// if fall height is greater than or equal to 11, we don't actually grab on to vines or ladders. the more you know
// this effectively "resets" our falling speed
costSoFar += FALL_N_BLOCKS_COST[unprotectedFallHeight - 1];// we fall until the top of this block (not including this block)
costSoFar += LADDER_DOWN_ONE_COST;
effectiveStartHeight = newY;
continue;
} }
if (MovementHelper.canWalkThrough(context.bsi(), destX, newY, destZ, ontoBlock)) { if (MovementHelper.canWalkThrough(context.bsi(), destX, newY, destZ, ontoBlock)) {
continue; continue;
} }
if (!MovementHelper.canWalkOn(context.bsi(), destX, newY, destZ, ontoBlock)) { if (!MovementHelper.canWalkOn(context.bsi(), destX, newY, destZ, ontoBlock)) {
return; return false;
} }
if (MovementHelper.isBottomSlab(ontoBlock)) { if (MovementHelper.isBottomSlab(ontoBlock)) {
return; // falling onto a half slab is really glitchy, and can cause more fall damage than we'd expect return false; // falling onto a half slab is really glitchy, and can cause more fall damage than we'd expect
} }
if (context.hasWaterBucket() && fallHeight <= context.maxFallHeightBucket() + 1) { if (context.hasWaterBucket() && unprotectedFallHeight <= context.maxFallHeightBucket() + 1) {
res.x = destX; res.x = destX;
res.y = newY + 1;// this is the block we're falling onto, so dest is +1 res.y = newY + 1;// this is the block we're falling onto, so dest is +1
res.z = destZ; res.z = destZ;
res.cost = tentativeCost + context.placeBlockCost(); res.cost = tentativeCost + context.placeBlockCost();
return; return true;
} }
if (fallHeight <= context.maxFallHeightNoWater() + 1) { if (unprotectedFallHeight <= context.maxFallHeightNoWater() + 1) {
// fallHeight = 4 means onto.up() is 3 blocks down, which is the max // fallHeight = 4 means onto.up() is 3 blocks down, which is the max
res.x = destX; res.x = destX;
res.y = newY + 1; res.y = newY + 1;
res.z = destZ; res.z = destZ;
res.cost = tentativeCost; res.cost = tentativeCost;
return; return false;
} else { } else {
return; return false;
} }
} }
} }
@@ -26,6 +26,7 @@ import baritone.pathing.movement.Movement;
import baritone.pathing.movement.MovementHelper; import baritone.pathing.movement.MovementHelper;
import baritone.pathing.movement.MovementState; import baritone.pathing.movement.MovementState;
import baritone.utils.BlockStateInterface; import baritone.utils.BlockStateInterface;
import baritone.utils.pathing.MutableMoveResult;
import net.minecraft.block.Block; import net.minecraft.block.Block;
import net.minecraft.block.state.IBlockState; import net.minecraft.block.state.IBlockState;
import net.minecraft.init.Blocks; import net.minecraft.init.Blocks;
@@ -39,13 +40,13 @@ public class MovementDiagonal extends Movement {
private static final double SQRT_2 = Math.sqrt(2); private static final double SQRT_2 = Math.sqrt(2);
public MovementDiagonal(IBaritone baritone, BetterBlockPos start, EnumFacing dir1, EnumFacing dir2) { public MovementDiagonal(IBaritone baritone, BetterBlockPos start, EnumFacing dir1, EnumFacing dir2, int dy) {
this(baritone, start, start.offset(dir1), start.offset(dir2), dir2); this(baritone, start, start.offset(dir1), start.offset(dir2), dir2, dy);
// super(start, start.offset(dir1).offset(dir2), new BlockPos[]{start.offset(dir1), start.offset(dir1).up(), start.offset(dir2), start.offset(dir2).up(), start.offset(dir1).offset(dir2), start.offset(dir1).offset(dir2).up()}, new BlockPos[]{start.offset(dir1).offset(dir2).down()}); // super(start, start.offset(dir1).offset(dir2), new BlockPos[]{start.offset(dir1), start.offset(dir1).up(), start.offset(dir2), start.offset(dir2).up(), start.offset(dir1).offset(dir2), start.offset(dir1).offset(dir2).up()}, new BlockPos[]{start.offset(dir1).offset(dir2).down()});
} }
private MovementDiagonal(IBaritone baritone, BetterBlockPos start, BetterBlockPos dir1, BetterBlockPos dir2, EnumFacing drr2) { private MovementDiagonal(IBaritone baritone, BetterBlockPos start, BetterBlockPos dir1, BetterBlockPos dir2, EnumFacing drr2, int dy) {
this(baritone, start, dir1.offset(drr2), dir1, dir2); this(baritone, start, dir1.offset(drr2).up(dy), dir1, dir2);
} }
private MovementDiagonal(IBaritone baritone, BetterBlockPos start, BetterBlockPos end, BetterBlockPos dir1, BetterBlockPos dir2) { private MovementDiagonal(IBaritone baritone, BetterBlockPos start, BetterBlockPos end, BetterBlockPos dir1, BetterBlockPos dir2) {
@@ -54,37 +55,46 @@ public class MovementDiagonal extends Movement {
@Override @Override
protected double calculateCost(CalculationContext context) { protected double calculateCost(CalculationContext context) {
return cost(context, src.x, src.y, src.z, dest.x, dest.z); MutableMoveResult result = new MutableMoveResult();
cost(context, src.x, src.y, src.z, dest.x, dest.z, result);
if (result.y != dest.y) {
return COST_INF; // doesn't apply to us, this position is incorrect
}
return result.cost;
} }
public static double cost(CalculationContext context, int x, int y, int z, int destX, int destZ) { public static void cost(CalculationContext context, int x, int y, int z, int destX, int destZ, MutableMoveResult res) {
Block fromDown = context.get(x, y - 1, z).getBlock();
if (fromDown == Blocks.LADDER || fromDown == Blocks.VINE) {
return COST_INF;
}
IBlockState destInto = context.get(destX, y, destZ); IBlockState destInto = context.get(destX, y, destZ);
if (!MovementHelper.canWalkThrough(context.bsi(), destX, y, destZ, destInto) || !MovementHelper.canWalkThrough(context.bsi(), destX, y + 1, destZ)) { if (!MovementHelper.canWalkThrough(context.bsi(), destX, y, destZ, destInto) || !MovementHelper.canWalkThrough(context.bsi(), destX, y + 1, destZ)) {
return COST_INF; return;
} }
IBlockState destWalkOn = context.get(destX, y - 1, destZ); IBlockState destWalkOn = context.get(destX, y - 1, destZ);
boolean descend = false;
if (!MovementHelper.canWalkOn(context.bsi(), destX, y - 1, destZ, destWalkOn)) { if (!MovementHelper.canWalkOn(context.bsi(), destX, y - 1, destZ, destWalkOn)) {
return COST_INF; descend = true;
if (!MovementHelper.canWalkOn(context.bsi(), destX, y - 2, destZ) || !MovementHelper.canWalkThrough(context.bsi(), destX, y - 1, destZ, destWalkOn)) {
return;
}
} }
double multiplier = WALK_ONE_BLOCK_COST; double multiplier = WALK_ONE_BLOCK_COST;
// For either possible soul sand, that affects half of our walking // For either possible soul sand, that affects half of our walking
if (destWalkOn.getBlock() == Blocks.SOUL_SAND) { if (destWalkOn.getBlock() == Blocks.SOUL_SAND) {
multiplier += (WALK_ONE_OVER_SOUL_SAND_COST - WALK_ONE_BLOCK_COST) / 2; multiplier += (WALK_ONE_OVER_SOUL_SAND_COST - WALK_ONE_BLOCK_COST) / 2;
} }
Block fromDown = context.get(x, y - 1, z).getBlock();
if (fromDown == Blocks.LADDER || fromDown == Blocks.VINE) {
return;
}
if (fromDown == Blocks.SOUL_SAND) { if (fromDown == Blocks.SOUL_SAND) {
multiplier += (WALK_ONE_OVER_SOUL_SAND_COST - WALK_ONE_BLOCK_COST) / 2; multiplier += (WALK_ONE_OVER_SOUL_SAND_COST - WALK_ONE_BLOCK_COST) / 2;
} }
Block cuttingOver1 = context.get(x, y - 1, destZ).getBlock(); Block cuttingOver1 = context.get(x, y - 1, destZ).getBlock();
if (cuttingOver1 == Blocks.MAGMA || MovementHelper.isLava(cuttingOver1)) { if (cuttingOver1 == Blocks.MAGMA || MovementHelper.isLava(cuttingOver1)) {
return COST_INF; return;
} }
Block cuttingOver2 = context.get(destX, y - 1, z).getBlock(); Block cuttingOver2 = context.get(destX, y - 1, z).getBlock();
if (cuttingOver2 == Blocks.MAGMA || MovementHelper.isLava(cuttingOver2)) { if (cuttingOver2 == Blocks.MAGMA || MovementHelper.isLava(cuttingOver2)) {
return COST_INF; return;
} }
IBlockState pb0 = context.get(x, y, destZ); IBlockState pb0 = context.get(x, y, destZ);
IBlockState pb2 = context.get(destX, y, z); IBlockState pb2 = context.get(destX, y, z);
@@ -93,27 +103,27 @@ public class MovementDiagonal extends Movement {
if (optionA != 0 && optionB != 0) { if (optionA != 0 && optionB != 0) {
// check these one at a time -- if pb0 and pb2 were nonzero, we already know that (optionA != 0 && optionB != 0) // check these one at a time -- if pb0 and pb2 were nonzero, we already know that (optionA != 0 && optionB != 0)
// so no need to check pb1 as well, might as well return early here // so no need to check pb1 as well, might as well return early here
return COST_INF; return;
} }
IBlockState pb1 = context.get(x, y + 1, destZ); IBlockState pb1 = context.get(x, y + 1, destZ);
optionA += MovementHelper.getMiningDurationTicks(context, x, y + 1, destZ, pb1, true); optionA += MovementHelper.getMiningDurationTicks(context, x, y + 1, destZ, pb1, true);
if (optionA != 0 && optionB != 0) { if (optionA != 0 && optionB != 0) {
// same deal, if pb1 makes optionA nonzero and option B already was nonzero, pb3 can't affect the result // same deal, if pb1 makes optionA nonzero and option B already was nonzero, pb3 can't affect the result
return COST_INF; return;
} }
IBlockState pb3 = context.get(destX, y + 1, z); IBlockState pb3 = context.get(destX, y + 1, z);
if (optionA == 0 && ((MovementHelper.avoidWalkingInto(pb2.getBlock()) && pb2.getBlock() != Blocks.WATER) || (MovementHelper.avoidWalkingInto(pb3.getBlock()) && pb3.getBlock() != Blocks.WATER))) { if (optionA == 0 && ((MovementHelper.avoidWalkingInto(pb2.getBlock()) && pb2.getBlock() != Blocks.WATER) || (MovementHelper.avoidWalkingInto(pb3.getBlock()) && pb3.getBlock() != Blocks.WATER))) {
// at this point we're done calculating optionA, so we can check if it's actually possible to edge around in that direction // at this point we're done calculating optionA, so we can check if it's actually possible to edge around in that direction
return COST_INF; return;
} }
optionB += MovementHelper.getMiningDurationTicks(context, destX, y + 1, z, pb3, true); optionB += MovementHelper.getMiningDurationTicks(context, destX, y + 1, z, pb3, true);
if (optionA != 0 && optionB != 0) { if (optionA != 0 && optionB != 0) {
// and finally, if the cost is nonzero for both ways to approach this diagonal, it's not possible // and finally, if the cost is nonzero for both ways to approach this diagonal, it's not possible
return COST_INF; return;
} }
if (optionB == 0 && ((MovementHelper.avoidWalkingInto(pb0.getBlock()) && pb0.getBlock() != Blocks.WATER) || (MovementHelper.avoidWalkingInto(pb1.getBlock()) && pb1.getBlock() != Blocks.WATER))) { if (optionB == 0 && ((MovementHelper.avoidWalkingInto(pb0.getBlock()) && pb0.getBlock() != Blocks.WATER) || (MovementHelper.avoidWalkingInto(pb1.getBlock()) && pb1.getBlock() != Blocks.WATER))) {
// and now that option B is fully calculated, see if we can edge around that way // and now that option B is fully calculated, see if we can edge around that way
return COST_INF; return;
} }
boolean water = false; boolean water = false;
Block startIn = context.getBlock(x, y, z); Block startIn = context.getBlock(x, y, z);
@@ -128,16 +138,26 @@ public class MovementDiagonal extends Movement {
multiplier *= SQRT_2 - 0.001; // TODO tune multiplier *= SQRT_2 - 0.001; // TODO tune
if (startIn == Blocks.LADDER || startIn == Blocks.VINE) { if (startIn == Blocks.LADDER || startIn == Blocks.VINE) {
// edging around doesn't work if doing so would climb a ladder or vine instead of moving sideways // edging around doesn't work if doing so would climb a ladder or vine instead of moving sideways
return COST_INF; return;
}
} else {
// only can sprint if not edging around
if (context.canSprint() && !water) {
// If we aren't edging around anything, and we aren't in water
// We can sprint =D
// Don't check for soul sand, since we can sprint on that too
multiplier *= SPRINT_MULTIPLIER;
} }
} }
if (context.canSprint() && !water) { res.cost = multiplier * SQRT_2;
// If we aren't edging around anything, and we aren't in water if (descend) {
// We can sprint =D res.cost += Math.max(FALL_N_BLOCKS_COST[1], CENTER_AFTER_FALL_COST);
// Don't check for soul sand, since we can sprint on that too res.y = y - 1;
multiplier *= SPRINT_MULTIPLIER; } else {
res.y = y;
} }
return multiplier * SQRT_2; res.x = destX;
res.z = destZ;
} }
@Override @Override
@@ -51,14 +51,13 @@ public class MovementDownward extends Movement {
if (!MovementHelper.canWalkOn(context.bsi(), x, y - 2, z)) { if (!MovementHelper.canWalkOn(context.bsi(), x, y - 2, z)) {
return COST_INF; return COST_INF;
} }
IBlockState d = context.get(x, y - 1, z); IBlockState down = context.get(x, y - 1, z);
Block td = d.getBlock(); Block downBlock = down.getBlock();
boolean ladder = td == Blocks.LADDER || td == Blocks.VINE; if (downBlock == Blocks.LADDER || downBlock == Blocks.VINE) {
if (ladder) {
return LADDER_DOWN_ONE_COST; return LADDER_DOWN_ONE_COST;
} else { } else {
// we're standing on it, while it might be block falling, it'll be air by the time we get here in the movement // we're standing on it, while it might be block falling, it'll be air by the time we get here in the movement
return FALL_N_BLOCKS_COST[1] + MovementHelper.getMiningDurationTicks(context, x, y - 1, z, d, false); return FALL_N_BLOCKS_COST[1] + MovementHelper.getMiningDurationTicks(context, x, y - 1, z, down, false);
} }
} }
@@ -17,7 +17,6 @@
package baritone.pathing.movement.movements; package baritone.pathing.movement.movements;
import baritone.Baritone;
import baritone.api.IBaritone; import baritone.api.IBaritone;
import baritone.api.pathing.movement.MovementStatus; import baritone.api.pathing.movement.MovementStatus;
import baritone.api.utils.BetterBlockPos; import baritone.api.utils.BetterBlockPos;
@@ -31,12 +30,19 @@ import baritone.pathing.movement.MovementHelper;
import baritone.pathing.movement.MovementState; import baritone.pathing.movement.MovementState;
import baritone.pathing.movement.MovementState.MovementTarget; import baritone.pathing.movement.MovementState.MovementTarget;
import baritone.utils.pathing.MutableMoveResult; import baritone.utils.pathing.MutableMoveResult;
import net.minecraft.block.BlockLadder;
import net.minecraft.block.state.IBlockState;
import net.minecraft.entity.player.InventoryPlayer; import net.minecraft.entity.player.InventoryPlayer;
import net.minecraft.init.Blocks;
import net.minecraft.init.Items; import net.minecraft.init.Items;
import net.minecraft.item.ItemStack; import net.minecraft.item.ItemStack;
import net.minecraft.util.EnumFacing;
import net.minecraft.util.math.BlockPos; import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.RayTraceResult; import net.minecraft.util.math.RayTraceResult;
import net.minecraft.util.math.Vec3d; import net.minecraft.util.math.Vec3d;
import net.minecraft.util.math.Vec3i;
import java.util.Optional;
public class MovementFall extends Movement { public class MovementFall extends Movement {
@@ -57,6 +63,12 @@ public class MovementFall extends Movement {
return result.cost; return result.cost;
} }
private boolean willPlaceBucket() {
CalculationContext context = new CalculationContext(baritone);
MutableMoveResult result = new MutableMoveResult();
return MovementDescend.dynamicFallCost(context, src.x, src.y, src.z, dest.x, dest.z, 0, context.get(dest.x, src.y - 2, dest.z), result);
}
@Override @Override
public MovementState updateState(MovementState state) { public MovementState updateState(MovementState state) {
super.updateState(state); super.updateState(state);
@@ -67,7 +79,7 @@ public class MovementFall extends Movement {
BlockPos playerFeet = ctx.playerFeet(); BlockPos playerFeet = ctx.playerFeet();
Rotation toDest = RotationUtils.calcRotationFromVec3d(ctx.playerHead(), VecUtils.getBlockPosCenter(dest)); Rotation toDest = RotationUtils.calcRotationFromVec3d(ctx.playerHead(), VecUtils.getBlockPosCenter(dest));
Rotation targetRotation = null; Rotation targetRotation = null;
if (!MovementHelper.isWater(ctx, dest) && src.getY() - dest.getY() > Baritone.settings().maxFallHeightNoWater.get() && !playerFeet.equals(dest)) { if (!MovementHelper.isWater(ctx, dest) && willPlaceBucket() && !playerFeet.equals(dest)) {
if (!InventoryPlayer.isHotbar(ctx.player().inventory.getSlotFor(STACK_BUCKET_WATER)) || ctx.world().provider.isNether()) { if (!InventoryPlayer.isHotbar(ctx.player().inventory.getSlotFor(STACK_BUCKET_WATER)) || ctx.world().provider.isNether()) {
return state.setStatus(MovementStatus.UNREACHABLE); return state.setStatus(MovementStatus.UNREACHABLE);
} }
@@ -78,7 +90,7 @@ public class MovementFall extends Movement {
targetRotation = new Rotation(toDest.getYaw(), 90.0F); targetRotation = new Rotation(toDest.getYaw(), 90.0F);
RayTraceResult trace = ctx.objectMouseOver(); RayTraceResult trace = ctx.objectMouseOver();
if (trace != null && trace.typeOfHit == RayTraceResult.Type.BLOCK && ctx.player().rotationPitch > 89.0F) { if (trace != null && trace.typeOfHit == RayTraceResult.Type.BLOCK && (trace.getBlockPos().equals(dest) || trace.getBlockPos().equals(dest.down()))) {
state.setInput(Input.CLICK_RIGHT, true); state.setInput(Input.CLICK_RIGHT, true);
} }
} }
@@ -107,12 +119,40 @@ public class MovementFall extends Movement {
} }
} }
Vec3d destCenter = VecUtils.getBlockPosCenter(dest); // we are moving to the 0.5 center not the edge (like if we were falling on a ladder) Vec3d destCenter = VecUtils.getBlockPosCenter(dest); // we are moving to the 0.5 center not the edge (like if we were falling on a ladder)
if (Math.abs(ctx.player().posX - destCenter.x) > 0.15 || Math.abs(ctx.player().posZ - destCenter.z) > 0.15) { if (Math.abs(ctx.player().posX + ctx.player().motionX - destCenter.x) > 0.1 || Math.abs(ctx.player().posZ + ctx.player().motionZ - destCenter.z) > 0.1) {
if (!ctx.player().onGround && Math.abs(ctx.player().motionY) > 0.4) {
state.setInput(Input.SNEAK, true);
}
state.setInput(Input.MOVE_FORWARD, true); state.setInput(Input.MOVE_FORWARD, true);
} }
Vec3i avoid = Optional.ofNullable(avoid()).map(EnumFacing::getDirectionVec).orElse(null);
if (avoid == null) {
avoid = src.subtract(dest);
} else {
double dist = Math.abs(avoid.getX() * (destCenter.x - avoid.getX() / 2.0 - ctx.player().posX)) + Math.abs(avoid.getZ() * (destCenter.z - avoid.getZ() / 2.0 - ctx.player().posZ));
if (dist < 0.6) {
state.setInput(Input.MOVE_FORWARD, true);
} else {
state.setInput(Input.SNEAK, false);
}
}
if (targetRotation == null) {
Vec3d destCenterOffset = new Vec3d(destCenter.x + 0.125 * avoid.getX(), destCenter.y, destCenter.z + 0.125 * avoid.getZ());
state.setTarget(new MovementTarget(RotationUtils.calcRotationFromVec3d(ctx.playerHead(), destCenterOffset), false));
}
return state; return state;
} }
private EnumFacing avoid() {
for (int i = 0; i < 15; i++) {
IBlockState state = ctx.world().getBlockState(ctx.playerFeet().down(i));
if (state.getBlock() == Blocks.LADDER) {
return state.getValue(BlockLadder.FACING);
}
}
return null;
}
@Override @Override
public boolean safeToCancel(MovementState state) { public boolean safeToCancel(MovementState state) {
// if we haven't started walking off the edge yet, or if we're in the process of breaking blocks before doing the fall // if we haven't started walking off the edge yet, or if we're in the process of breaking blocks before doing the fall
@@ -17,7 +17,6 @@
package baritone.pathing.movement.movements; package baritone.pathing.movement.movements;
import baritone.Baritone;
import baritone.api.IBaritone; import baritone.api.IBaritone;
import baritone.api.pathing.movement.MovementStatus; import baritone.api.pathing.movement.MovementStatus;
import baritone.api.utils.BetterBlockPos; import baritone.api.utils.BetterBlockPos;
@@ -30,7 +29,6 @@ import baritone.pathing.movement.Movement;
import baritone.pathing.movement.MovementHelper; import baritone.pathing.movement.MovementHelper;
import baritone.pathing.movement.MovementState; import baritone.pathing.movement.MovementState;
import baritone.utils.BlockStateInterface; import baritone.utils.BlockStateInterface;
import baritone.utils.Helper;
import baritone.utils.pathing.MutableMoveResult; import baritone.utils.pathing.MutableMoveResult;
import net.minecraft.block.Block; import net.minecraft.block.Block;
import net.minecraft.block.BlockStairs; import net.minecraft.block.BlockStairs;
@@ -41,11 +39,8 @@ import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.RayTraceResult; import net.minecraft.util.math.RayTraceResult;
import net.minecraft.util.math.Vec3d; import net.minecraft.util.math.Vec3d;
import java.util.Objects;
public class MovementParkour extends Movement { public class MovementParkour extends Movement {
private static final EnumFacing[] HORIZONTALS_BUT_ALSO_DOWN____SO_EVERY_DIRECTION_EXCEPT_UP = {EnumFacing.NORTH, EnumFacing.SOUTH, EnumFacing.EAST, EnumFacing.WEST, EnumFacing.DOWN};
private static final BetterBlockPos[] EMPTY = new BetterBlockPos[]{}; private static final BetterBlockPos[] EMPTY = new BetterBlockPos[]{};
private final EnumFacing direction; private final EnumFacing direction;
@@ -65,24 +60,25 @@ public class MovementParkour extends Movement {
} }
public static void cost(CalculationContext context, int x, int y, int z, EnumFacing dir, MutableMoveResult res) { public static void cost(CalculationContext context, int x, int y, int z, EnumFacing dir, MutableMoveResult res) {
if (!Baritone.settings().allowParkour.get()) { if (!context.allowParkour()) {
return; return;
} }
IBlockState standingOn = context.get(x, y - 1, z); if (y == 256 && !context.allowJumpAt256()) {
if (standingOn.getBlock() == Blocks.VINE || standingOn.getBlock() == Blocks.LADDER || standingOn.getBlock() instanceof BlockStairs || MovementHelper.isBottomSlab(standingOn)) {
return;
}
int xDiff = dir.getXOffset();
int zDiff = dir.getZOffset();
IBlockState adj = context.get(x + xDiff, y - 1, z + zDiff);
if (MovementHelper.avoidWalkingInto(adj.getBlock()) && adj.getBlock() != Blocks.WATER && adj.getBlock() != Blocks.FLOWING_WATER) { // magma sucks
return;
}
if (MovementHelper.canWalkOn(context.bsi(), x + xDiff, y - 1, z + zDiff, adj)) { // don't parkour if we could just traverse (for now)
return; return;
} }
int xDiff = dir.getXOffset();
int zDiff = dir.getZOffset();
if (!MovementHelper.fullyPassable(context, x + xDiff, y, z + zDiff)) { if (!MovementHelper.fullyPassable(context, x + xDiff, y, z + zDiff)) {
// most common case at the top -- the adjacent block isn't air
return;
}
IBlockState adj = context.get(x + xDiff, y - 1, z + zDiff);
if (MovementHelper.canWalkOn(context.bsi(), x + xDiff, y - 1, z + zDiff, adj)) { // don't parkour if we could just traverse (for now)
// second most common case -- we could just traverse not parkour
return;
}
if (MovementHelper.avoidWalkingInto(adj.getBlock()) && adj.getBlock() != Blocks.WATER && adj.getBlock() != Blocks.FLOWING_WATER) { // magma sucks
return; return;
} }
if (!MovementHelper.fullyPassable(context, x + xDiff, y + 1, z + zDiff)) { if (!MovementHelper.fullyPassable(context, x + xDiff, y + 1, z + zDiff)) {
@@ -94,6 +90,10 @@ public class MovementParkour extends Movement {
if (!MovementHelper.fullyPassable(context, x, y + 2, z)) { if (!MovementHelper.fullyPassable(context, x, y + 2, z)) {
return; return;
} }
IBlockState standingOn = context.get(x, y - 1, z);
if (standingOn.getBlock() == Blocks.VINE || standingOn.getBlock() == Blocks.LADDER || standingOn.getBlock() instanceof BlockStairs || MovementHelper.isBottomSlab(standingOn)) {
return;
}
int maxJump; int maxJump;
if (standingOn.getBlock() == Blocks.SOUL_SAND) { if (standingOn.getBlock() == Blocks.SOUL_SAND) {
maxJump = 2; // 1 block gap maxJump = 2; // 1 block gap
@@ -122,29 +122,26 @@ public class MovementParkour extends Movement {
if (maxJump != 4) { if (maxJump != 4) {
return; return;
} }
if (!Baritone.settings().allowParkourPlace.get()) { if (!context.allowParkourPlace()) {
return;
}
if (!Baritone.settings().allowPlace.get()) {
Helper.HELPER.logDirect("allowParkourPlace enabled but allowPlace disabled?");
return; return;
} }
int destX = x + 4 * xDiff; int destX = x + 4 * xDiff;
int destZ = z + 4 * zDiff; int destZ = z + 4 * zDiff;
IBlockState toPlace = context.get(destX, y - 1, destZ);
if (!context.canPlaceThrowawayAt(destX, y - 1, destZ)) { if (!context.canPlaceThrowawayAt(destX, y - 1, destZ)) {
return; return;
} }
if (toPlace.getBlock() != Blocks.AIR && !MovementHelper.isWater(toPlace.getBlock()) && !MovementHelper.isReplacable(destX, y - 1, destZ, toPlace, context.world())) { IBlockState toReplace = context.get(destX, y - 1, destZ);
if (!MovementHelper.isReplacable(destX, y - 1, destZ, toReplace, context.bsi())) {
return; return;
} }
for (int i = 0; i < 5; i++) { for (int i = 0; i < 5; i++) {
int againstX = destX + HORIZONTALS_BUT_ALSO_DOWN____SO_EVERY_DIRECTION_EXCEPT_UP[i].getXOffset(); int againstX = destX + HORIZONTALS_BUT_ALSO_DOWN____SO_EVERY_DIRECTION_EXCEPT_UP[i].getXOffset();
int againstY = y - 1 + HORIZONTALS_BUT_ALSO_DOWN____SO_EVERY_DIRECTION_EXCEPT_UP[i].getYOffset();
int againstZ = destZ + HORIZONTALS_BUT_ALSO_DOWN____SO_EVERY_DIRECTION_EXCEPT_UP[i].getZOffset(); int againstZ = destZ + HORIZONTALS_BUT_ALSO_DOWN____SO_EVERY_DIRECTION_EXCEPT_UP[i].getZOffset();
if (againstX == x + xDiff * 3 && againstZ == z + zDiff * 3) { // we can't turn around that fast if (againstX == x + xDiff * 3 && againstZ == z + zDiff * 3) { // we can't turn around that fast
continue; continue;
} }
if (MovementHelper.canPlaceAgainst(context.bsi(), againstX, y - 1, againstZ)) { if (MovementHelper.canPlaceAgainst(context.bsi(), againstX, againstY, againstZ)) {
res.x = destX; res.x = destX;
res.y = y; res.y = y;
res.z = destZ; res.z = destZ;
@@ -215,7 +212,7 @@ public class MovementParkour extends Movement {
if (!MovementHelper.canWalkOn(ctx, dest.down()) && !ctx.player().onGround) { if (!MovementHelper.canWalkOn(ctx, dest.down()) && !ctx.player().onGround) {
BlockPos positionToPlace = dest.down(); BlockPos positionToPlace = dest.down();
for (int i = 0; i < 5; i++) { for (int i = 4; i >= 0; i--) { // go in the opposite order to check DOWN before all horizontals -- down is preferable because you don't have to look to the side while in midair, which could mess up the trajectory
BlockPos against1 = positionToPlace.offset(HORIZONTALS_BUT_ALSO_DOWN____SO_EVERY_DIRECTION_EXCEPT_UP[i]); BlockPos against1 = positionToPlace.offset(HORIZONTALS_BUT_ALSO_DOWN____SO_EVERY_DIRECTION_EXCEPT_UP[i]);
if (against1.up().equals(src.offset(direction, 3))) { // we can't turn around that fast if (against1.up().equals(src.offset(direction, 3))) { // we can't turn around that fast
continue; continue;
@@ -231,15 +228,16 @@ public class MovementParkour extends Movement {
RayTraceResult res = RayTraceUtils.rayTraceTowards(ctx.player(), place, ctx.playerController().getBlockReachDistance()); RayTraceResult res = RayTraceUtils.rayTraceTowards(ctx.player(), place, ctx.playerController().getBlockReachDistance());
if (res != null && res.typeOfHit == RayTraceResult.Type.BLOCK && res.getBlockPos().equals(against1) && res.getBlockPos().offset(res.sideHit).equals(dest.down())) { if (res != null && res.typeOfHit == RayTraceResult.Type.BLOCK && res.getBlockPos().equals(against1) && res.getBlockPos().offset(res.sideHit).equals(dest.down())) {
state.setTarget(new MovementState.MovementTarget(place, true)); state.setTarget(new MovementState.MovementTarget(place, true));
break;
} }
ctx.getSelectedBlock().ifPresent(selectedBlock -> {
EnumFacing side = ctx.objectMouseOver().sideHit;
if (Objects.equals(selectedBlock, against1) && selectedBlock.offset(side).equals(dest.down())) {
state.setInput(Input.CLICK_RIGHT, true);
}
});
} }
} }
ctx.getSelectedBlock().ifPresent(selectedBlock -> {
EnumFacing side = ctx.objectMouseOver().sideHit;
if (MovementHelper.canPlaceAgainst(ctx, selectedBlock) && selectedBlock.offset(side).equals(dest.down())) {
state.setInput(Input.CLICK_RIGHT, true);
}
});
} }
if (dist == 3) { // this is a 2 block gap, dest = src + direction * 3 if (dist == 3) { // this is a 2 block gap, dest = src + direction * 3
double xDiff = (src.x + 0.5) - ctx.player().posX; double xDiff = (src.x + 0.5) - ctx.player().posX;
@@ -47,33 +47,39 @@ public class MovementPillar extends Movement {
} }
public static double cost(CalculationContext context, int x, int y, int z) { public static double cost(CalculationContext context, int x, int y, int z) {
Block fromDown = context.get(x, y, z).getBlock(); Block from = context.get(x, y, z).getBlock();
boolean ladder = fromDown instanceof BlockLadder || fromDown instanceof BlockVine; boolean ladder = from == Blocks.LADDER || from == Blocks.VINE;
IBlockState fromDownDown = context.get(x, y - 1, z); IBlockState fromDown = context.get(x, y - 1, z);
if (!ladder) { if (!ladder) {
if (fromDownDown.getBlock() instanceof BlockLadder || fromDownDown.getBlock() instanceof BlockVine) { if (fromDown.getBlock() == Blocks.LADDER || fromDown.getBlock() == Blocks.VINE) {
return COST_INF; return COST_INF; // can't pillar from a ladder or vine onto something that isn't also climbable
} }
if (fromDownDown.getBlock() instanceof BlockSlab && !((BlockSlab) fromDownDown.getBlock()).isDouble() && fromDownDown.getValue(BlockSlab.HALF) == BlockSlab.EnumBlockHalf.BOTTOM) { if (fromDown.getBlock() instanceof BlockSlab && !((BlockSlab) fromDown.getBlock()).isDouble() && fromDown.getValue(BlockSlab.HALF) == BlockSlab.EnumBlockHalf.BOTTOM) {
return COST_INF; // can't pillar up from a bottom slab onto a non ladder return COST_INF; // can't pillar up from a bottom slab onto a non ladder
} }
} }
if (fromDown instanceof BlockVine && !hasAgainst(context, x, y, z)) { if (from instanceof BlockVine && !hasAgainst(context, x, y, z)) { // TODO this vine can't be climbed, but we could place a pillar still since vines are replacable, no? perhaps the pillar jump would be impossible because of the slowdown actually.
return COST_INF; return COST_INF;
} }
IBlockState toBreak = context.get(x, y + 2, z); IBlockState toBreak = context.get(x, y + 2, z);
Block toBreakBlock = toBreak.getBlock(); Block toBreakBlock = toBreak.getBlock();
if (toBreakBlock instanceof BlockFenceGate) { if (toBreakBlock instanceof BlockFenceGate) { // see issue #172
return COST_INF; return COST_INF;
} }
Block srcUp = null; Block srcUp = null;
if (MovementHelper.isWater(toBreakBlock) && MovementHelper.isWater(fromDown)) { if (MovementHelper.isWater(toBreakBlock) && MovementHelper.isWater(from)) { // TODO should this also be allowed if toBreakBlock is air?
srcUp = context.get(x, y + 1, z).getBlock(); srcUp = context.get(x, y + 1, z).getBlock();
if (MovementHelper.isWater(srcUp)) { if (MovementHelper.isWater(srcUp)) {
return LADDER_UP_ONE_COST; return LADDER_UP_ONE_COST; // allow ascending pillars of water, but only if we're already in one
} }
} }
if (!ladder && !context.canPlaceThrowawayAt(x, y, z)) { if (!ladder && !context.canPlaceThrowawayAt(x, y, z)) { // we need to place a block where we started to jump on it
return COST_INF;
}
if (from instanceof BlockLiquid || (fromDown.getBlock() instanceof BlockLiquid && context.assumeWalkOnWater())) {
// otherwise, if we're standing in water, we cannot pillar
// if we're standing on water and assumeWalkOnWater is true, we cannot pillar
// if we're standing on water and assumeWalkOnWater is false, we must have ascended to here, or sneak backplaced, so it is possible to pillar again
return COST_INF; return COST_INF;
} }
double hardness = MovementHelper.getMiningDurationTicks(context, x, y + 2, z, toBreak, true); double hardness = MovementHelper.getMiningDurationTicks(context, x, y + 2, z, toBreak, true);
@@ -81,10 +87,10 @@ public class MovementPillar extends Movement {
return COST_INF; return COST_INF;
} }
if (hardness != 0) { if (hardness != 0) {
if (toBreakBlock instanceof BlockLadder || toBreakBlock instanceof BlockVine) { if (toBreakBlock == Blocks.LADDER || toBreakBlock == Blocks.VINE) {
hardness = 0; // we won't actually need to break the ladder / vine because we're going to use it hardness = 0; // we won't actually need to break the ladder / vine because we're going to use it
} else { } else {
IBlockState check = context.get(x, y + 3, z); IBlockState check = context.get(x, y + 3, z); // the block on top of the one we're going to break, could it fall on us?
if (check.getBlock() instanceof BlockFalling) { if (check.getBlock() instanceof BlockFalling) {
// see MovementAscend's identical check for breaking a falling block above our head // see MovementAscend's identical check for breaking a falling block above our head
if (srcUp == null) { if (srcUp == null) {
@@ -103,9 +109,6 @@ public class MovementPillar extends Movement {
//} //}
} }
} }
if (fromDown instanceof BlockLiquid || fromDownDown.getBlock() instanceof BlockLiquid) {//can't pillar on water or in water
return COST_INF;
}
if (ladder) { if (ladder) {
return LADDER_UP_ONE_COST + hardness * 5; return LADDER_UP_ONE_COST + hardness * 5;
} else { } else {
@@ -80,11 +80,11 @@ public class MovementTraverse extends Movement {
WC += (WALK_ONE_OVER_SOUL_SAND_COST - WALK_ONE_BLOCK_COST) / 2; WC += (WALK_ONE_OVER_SOUL_SAND_COST - WALK_ONE_BLOCK_COST) / 2;
} }
} }
double hardness1 = MovementHelper.getMiningDurationTicks(context, destX, y + 1, destZ, pb0, true); double hardness1 = MovementHelper.getMiningDurationTicks(context, destX, y, destZ, pb1, false);
if (hardness1 >= COST_INF) { if (hardness1 >= COST_INF) {
return COST_INF; return COST_INF;
} }
double hardness2 = MovementHelper.getMiningDurationTicks(context, destX, y, destZ, pb1, false); double hardness2 = MovementHelper.getMiningDurationTicks(context, destX, y + 1, destZ, pb0, true); // only include falling on the upper block to break
if (hardness1 == 0 && hardness2 == 0) { if (hardness1 == 0 && hardness2 == 0) {
if (!water && context.canSprint()) { if (!water && context.canSprint()) {
// If there's nothing in the way, and this isn't water, and we aren't sneak placing // If there's nothing in the way, and this isn't water, and we aren't sneak placing
@@ -103,42 +103,43 @@ public class MovementTraverse extends Movement {
if (srcDown == Blocks.LADDER || srcDown == Blocks.VINE) { if (srcDown == Blocks.LADDER || srcDown == Blocks.VINE) {
return COST_INF; return COST_INF;
} }
if (destOn.getBlock().equals(Blocks.AIR) || MovementHelper.isReplacable(destX, y - 1, destZ, destOn, context.world())) { if (MovementHelper.isReplacable(destX, y - 1, destZ, destOn, context.bsi())) {
boolean throughWater = MovementHelper.isWater(pb0.getBlock()) || MovementHelper.isWater(pb1.getBlock()); boolean throughWater = MovementHelper.isWater(pb0.getBlock()) || MovementHelper.isWater(pb1.getBlock());
if (MovementHelper.isWater(destOn.getBlock()) && throughWater) { if (MovementHelper.isWater(destOn.getBlock()) && throughWater) {
// this happens when assume walk on water is true and this is a traverse in water, which isn't allowed
return COST_INF; return COST_INF;
} }
if (!context.canPlaceThrowawayAt(destX, y - 1, destZ)) { if (!context.canPlaceThrowawayAt(destX, y - 1, destZ)) {
return COST_INF; return COST_INF;
} }
double hardness1 = MovementHelper.getMiningDurationTicks(context, destX, y, destZ, pb0, false); double hardness1 = MovementHelper.getMiningDurationTicks(context, destX, y, destZ, pb1, false);
if (hardness1 >= COST_INF) { if (hardness1 >= COST_INF) {
return COST_INF; return COST_INF;
} }
double hardness2 = MovementHelper.getMiningDurationTicks(context, destX, y + 1, destZ, pb1, true); double hardness2 = MovementHelper.getMiningDurationTicks(context, destX, y + 1, destZ, pb0, true); // only include falling on the upper block to break
double WC = throughWater ? context.waterWalkSpeed() : WALK_ONE_BLOCK_COST; double WC = throughWater ? context.waterWalkSpeed() : WALK_ONE_BLOCK_COST;
for (int i = 0; i < 4; i++) { for (int i = 0; i < 5; i++) {
int againstX = destX + HORIZONTALS[i].getXOffset(); int againstX = destX + HORIZONTALS_BUT_ALSO_DOWN____SO_EVERY_DIRECTION_EXCEPT_UP[i].getXOffset();
int againstZ = destZ + HORIZONTALS[i].getZOffset(); int againstY = y - 1 + HORIZONTALS_BUT_ALSO_DOWN____SO_EVERY_DIRECTION_EXCEPT_UP[i].getYOffset();
if (againstX == x && againstZ == z) { int againstZ = destZ + HORIZONTALS_BUT_ALSO_DOWN____SO_EVERY_DIRECTION_EXCEPT_UP[i].getZOffset();
if (againstX == x && againstZ == z) { // this would be a backplace
continue; continue;
} }
if (MovementHelper.canPlaceAgainst(context.bsi(), againstX, y - 1, againstZ)) { if (MovementHelper.canPlaceAgainst(context.bsi(), againstX, againstY, againstZ)) { // found a side place option
return WC + context.placeBlockCost() + hardness1 + hardness2; return WC + context.placeBlockCost() + hardness1 + hardness2;
} }
} }
// now that we've checked all possible directions to side place, we actually need to backplace
if (srcDown == Blocks.SOUL_SAND || (srcDown instanceof BlockSlab && !((BlockSlab) srcDown).isDouble())) { if (srcDown == Blocks.SOUL_SAND || (srcDown instanceof BlockSlab && !((BlockSlab) srcDown).isDouble())) {
return COST_INF; // can't sneak and backplace against soul sand or half slabs =/ return COST_INF; // can't sneak and backplace against soul sand or half slabs =/
} }
if (srcDown == Blocks.FLOWING_WATER || srcDown == Blocks.WATER) { if (srcDown == Blocks.FLOWING_WATER || srcDown == Blocks.WATER) {
return COST_INF; // this is obviously impossible return COST_INF; // this is obviously impossible
} }
WC = WC * SNEAK_ONE_BLOCK_COST / WALK_ONE_BLOCK_COST;//since we are placing, we are sneaking WC = WC * SNEAK_ONE_BLOCK_COST / WALK_ONE_BLOCK_COST;//since we are sneak backplacing, we are sneaking lol
return WC + context.placeBlockCost() + hardness1 + hardness2; return WC + context.placeBlockCost() + hardness1 + hardness2;
} }
return COST_INF; return COST_INF;
// Out.log("Can't walk on " + Baritone.get(positionsToPlace[0]).getBlock());
} }
} }
@@ -241,8 +242,8 @@ public class MovementTraverse extends Movement {
return state; return state;
} else { } else {
wasTheBridgeBlockAlwaysThere = false; wasTheBridgeBlockAlwaysThere = false;
for (int i = 0; i < 4; i++) { for (int i = 0; i < 5; i++) {
BlockPos against1 = dest.offset(HORIZONTALS[i]); BlockPos against1 = dest.offset(HORIZONTALS_BUT_ALSO_DOWN____SO_EVERY_DIRECTION_EXCEPT_UP[i]);
if (against1.equals(src)) { if (against1.equals(src)) {
continue; continue;
} }
@@ -35,6 +35,7 @@ import baritone.pathing.movement.MovementHelper;
import baritone.pathing.movement.movements.*; import baritone.pathing.movement.movements.*;
import baritone.utils.BlockStateInterface; import baritone.utils.BlockStateInterface;
import baritone.utils.Helper; import baritone.utils.Helper;
import net.minecraft.block.BlockLiquid;
import net.minecraft.init.Blocks; import net.minecraft.init.Blocks;
import net.minecraft.util.Tuple; import net.minecraft.util.Tuple;
import net.minecraft.util.math.BlockPos; import net.minecraft.util.math.BlockPos;
@@ -347,16 +348,26 @@ public class PathExecutor implements IPathExecutor, Helper {
/** /**
* Regardless of current path position, snap to the current player feet if possible * Regardless of current path position, snap to the current player feet if possible
*
* @return Whether or not it was possible to snap to the current player feet
*/ */
public boolean snipsnapifpossible() { public boolean snipsnapifpossible() {
if (!ctx.player().onGround) { if (!ctx.player().onGround && !(ctx.world().getBlockState(ctx.playerFeet()).getBlock() instanceof BlockLiquid)) {
// if we're falling in the air, and not in water, don't splice
return false; return false;
} else {
// we are either onGround or in liquid
if (ctx.player().motionY < -0.1) {
// if we are strictly moving downwards (not stationary)
// we could be falling through water, which could be unsafe to splice
return false; // so don't
}
} }
int index = path.positions().indexOf(ctx.playerFeet()); int index = path.positions().indexOf(ctx.playerFeet());
if (index == -1) { if (index == -1) {
return false; return false;
} }
pathPosition = index; pathPosition = index; // jump directly to current position
clearKeys(); clearKeys();
return true; return true;
} }
@@ -71,9 +71,6 @@ public class SplicedPath extends PathBase {
if (second == null || first == null) { if (second == null || first == null) {
return Optional.empty(); return Optional.empty();
} }
if (!Objects.equals(first.getGoal(), second.getGoal())) {
return Optional.empty();
}
if (!first.getDest().equals(second.getSrc())) { if (!first.getDest().equals(second.getSrc())) {
return Optional.empty(); return Optional.empty();
} }
@@ -52,7 +52,9 @@ public class CustomGoalProcess extends BaritoneProcessHelper implements ICustomG
@Override @Override
public void setGoal(Goal goal) { public void setGoal(Goal goal) {
this.goal = goal; this.goal = goal;
this.state = State.GOAL_SET; if (this.state == State.NONE) {
this.state = State.GOAL_SET;
}
} }
@Override @Override
@@ -74,7 +76,7 @@ public class CustomGoalProcess extends BaritoneProcessHelper implements ICustomG
public PathingCommand onTick(boolean calcFailed, boolean isSafeToCancel) { public PathingCommand onTick(boolean calcFailed, boolean isSafeToCancel) {
switch (this.state) { switch (this.state) {
case GOAL_SET: case GOAL_SET:
if (!baritone.getPathingBehavior().isPathing() && Objects.equals(baritone.getPathingBehavior().getGoal(), this.goal)) { if (!baritone.getPathingBehavior().isPathing() && Objects.equals(baritone.getPathingBehavior().getGoal() + "", this.goal + "")) {
this.state = State.NONE; this.state = State.NONE;
} }
return new PathingCommand(this.goal, PathingCommandType.CANCEL_AND_SET_GOAL); return new PathingCommand(this.goal, PathingCommandType.CANCEL_AND_SET_GOAL);
@@ -79,7 +79,7 @@ public final class FollowProcess extends BaritoneProcessHelper implements IFollo
if (entity.equals(ctx.player())) { if (entity.equals(ctx.player())) {
return false; return false;
} }
return ctx.world().loadedEntityList.contains(entity) || ctx.world().playerEntities.contains(entity); return ctx.world().loadedEntityList.contains(entity);
} }
private void scanWorld() { private void scanWorld() {
@@ -21,19 +21,27 @@ import baritone.Baritone;
import baritone.api.pathing.goals.Goal; import baritone.api.pathing.goals.Goal;
import baritone.api.pathing.goals.GoalComposite; import baritone.api.pathing.goals.GoalComposite;
import baritone.api.pathing.goals.GoalGetToBlock; import baritone.api.pathing.goals.GoalGetToBlock;
import baritone.api.pathing.goals.GoalTwoBlocks;
import baritone.api.process.IGetToBlockProcess; import baritone.api.process.IGetToBlockProcess;
import baritone.api.process.PathingCommand; import baritone.api.process.PathingCommand;
import baritone.api.process.PathingCommandType; import baritone.api.process.PathingCommandType;
import baritone.api.utils.Rotation;
import baritone.api.utils.RotationUtils;
import baritone.api.utils.input.Input;
import baritone.pathing.movement.CalculationContext; import baritone.pathing.movement.CalculationContext;
import baritone.utils.BaritoneProcessHelper; import baritone.utils.BaritoneProcessHelper;
import net.minecraft.block.Block; import net.minecraft.block.Block;
import net.minecraft.init.Blocks;
import net.minecraft.inventory.ContainerPlayer;
import net.minecraft.util.math.BlockPos; import net.minecraft.util.math.BlockPos;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collections; import java.util.Collections;
import java.util.List; import java.util.List;
import java.util.Optional;
public class GetToBlockProcess extends BaritoneProcessHelper implements IGetToBlockProcess { public class GetToBlockProcess extends BaritoneProcessHelper implements IGetToBlockProcess {
private Block gettingTo; private Block gettingTo;
private List<BlockPos> knownLocations; private List<BlockPos> knownLocations;
@@ -45,8 +53,8 @@ public class GetToBlockProcess extends BaritoneProcessHelper implements IGetToBl
@Override @Override
public void getToBlock(Block block) { public void getToBlock(Block block) {
onLostControl();
gettingTo = block; gettingTo = block;
knownLocations = null;
rescan(new ArrayList<>(), new CalculationContext(baritone)); rescan(new ArrayList<>(), new CalculationContext(baritone));
} }
@@ -80,9 +88,16 @@ public class GetToBlockProcess extends BaritoneProcessHelper implements IGetToBl
CalculationContext context = new CalculationContext(baritone, true); CalculationContext context = new CalculationContext(baritone, true);
Baritone.getExecutor().execute(() -> rescan(current, context)); Baritone.getExecutor().execute(() -> rescan(current, context));
} }
Goal goal = new GoalComposite(knownLocations.stream().map(GoalGetToBlock::new).toArray(Goal[]::new)); Goal goal = new GoalComposite(knownLocations.stream().map(this::createGoal).toArray(Goal[]::new));
if (goal.isInGoal(ctx.playerFeet())) { if (goal.isInGoal(ctx.playerFeet()) && isSafeToCancel) {
onLostControl(); // we're there
if (rightClickOnArrival(gettingTo)) {
if (rightClick()) {
onLostControl();
}
} else {
onLostControl();
}
} }
return new PathingCommand(goal, PathingCommandType.REVALIDATE_GOAL_AND_PATH); return new PathingCommand(goal, PathingCommandType.REVALIDATE_GOAL_AND_PATH);
} }
@@ -91,6 +106,7 @@ public class GetToBlockProcess extends BaritoneProcessHelper implements IGetToBl
public void onLostControl() { public void onLostControl() {
gettingTo = null; gettingTo = null;
knownLocations = null; knownLocations = null;
baritone.getInputOverrideHandler().clearAllKeys();
} }
@Override @Override
@@ -101,4 +117,41 @@ public class GetToBlockProcess extends BaritoneProcessHelper implements IGetToBl
private void rescan(List<BlockPos> known, CalculationContext context) { private void rescan(List<BlockPos> known, CalculationContext context) {
knownLocations = MineProcess.searchWorld(context, Collections.singletonList(gettingTo), 64, known); knownLocations = MineProcess.searchWorld(context, Collections.singletonList(gettingTo), 64, known);
} }
private Goal createGoal(BlockPos pos) {
return walkIntoInsteadOfAdjacent(gettingTo) ? new GoalTwoBlocks(pos) : new GoalGetToBlock(pos);
}
private boolean rightClick() {
for (BlockPos pos : knownLocations) {
Optional<Rotation> reachable = RotationUtils.reachable(ctx.player(), pos, ctx.playerController().getBlockReachDistance());
if (reachable.isPresent()) {
baritone.getLookBehavior().updateTarget(reachable.get(), true);
if (knownLocations.contains(ctx.getSelectedBlock().orElse(null))) {
baritone.getInputOverrideHandler().setInputForceState(Input.CLICK_RIGHT, true); // TODO find some way to right click even if we're in an ESC menu
System.out.println(ctx.player().openContainer);
if (!(ctx.player().openContainer instanceof ContainerPlayer)) {
return true;
}
}
return false; // trying to right click, will do it next tick or so
}
}
logDirect("Arrived but failed to right click open");
return true;
}
private boolean walkIntoInsteadOfAdjacent(Block block) {
if (!Baritone.settings().enterPortal.get()) {
return false;
}
return block == Blocks.PORTAL;
}
private boolean rightClickOnArrival(Block block) {
if (!Baritone.settings().rightClickContainerOnArrival.get()) {
return false;
}
return block == Blocks.CRAFTING_TABLE || block == Blocks.FURNACE || block == Blocks.ENDER_CHEST || block == Blocks.CHEST || block == Blocks.TRAPPED_CHEST;
}
} }
@@ -39,7 +39,6 @@ import net.minecraft.item.Item;
import net.minecraft.item.ItemStack; import net.minecraft.item.ItemStack;
import net.minecraft.util.math.BlockPos; import net.minecraft.util.math.BlockPos;
import net.minecraft.world.World; import net.minecraft.world.World;
import net.minecraft.world.chunk.EmptyChunk;
import java.util.*; import java.util.*;
import java.util.stream.Collectors; import java.util.stream.Collectors;
@@ -255,7 +254,8 @@ public final class MineProcess extends BaritoneProcessHelper implements IMinePro
.distinct() .distinct()
// remove any that are within loaded chunks that aren't actually what we want // remove any that are within loaded chunks that aren't actually what we want
.filter(pos -> ctx.world().getChunk(pos) instanceof EmptyChunk || mining.contains(ctx.getBlock(pos.getX(), pos.getY(), pos.getZ())) || dropped.contains(pos))
.filter(pos -> !ctx.bsi().isLoaded(pos.getX(), pos.getZ()) || mining.contains(ctx.getBlock(pos.getX(), pos.getY(), pos.getZ())) || dropped.contains(pos))
// remove any that are implausible to mine (encased in bedrock, or touching lava) // remove any that are implausible to mine (encased in bedrock, or touching lava)
.filter(pos -> MineProcess.plausibleToBreak(ctx.bsi(), pos)) .filter(pos -> MineProcess.plausibleToBreak(ctx.bsi(), pos))
@@ -39,8 +39,8 @@ public class BaritoneAutoTest implements AbstractGameEventListener, Helper {
public static final boolean ENABLE_AUTO_TEST = "true".equals(System.getenv("BARITONE_AUTO_TEST")); public static final boolean ENABLE_AUTO_TEST = "true".equals(System.getenv("BARITONE_AUTO_TEST"));
private static final long TEST_SEED = -928872506371745L; private static final long TEST_SEED = -928872506371745L;
private static final BlockPos STARTING_POSITION = new BlockPos(0, 65, 0); private static final BlockPos STARTING_POSITION = new BlockPos(0, 65, 0);
private static final Goal GOAL = new GoalBlock(69, 121, 420); private static final Goal GOAL = new GoalBlock(69, 69, 420);
private static final int MAX_TICKS = 3500; private static final int MAX_TICKS = 3300;
/** /**
* Called right after the {@link GameSettings} object is created in the {@link Minecraft} instance. * Called right after the {@link GameSettings} object is created in the {@link Minecraft} instance.
@@ -19,6 +19,7 @@ package baritone.utils;
import baritone.Baritone; import baritone.Baritone;
import baritone.api.Settings; import baritone.api.Settings;
import baritone.api.cache.IRememberedInventory;
import baritone.api.cache.IWaypoint; import baritone.api.cache.IWaypoint;
import baritone.api.event.events.ChatEvent; import baritone.api.event.events.ChatEvent;
import baritone.api.pathing.goals.*; import baritone.api.pathing.goals.*;
@@ -41,6 +42,7 @@ import net.minecraft.client.multiplayer.ChunkProviderClient;
import net.minecraft.entity.Entity; import net.minecraft.entity.Entity;
import net.minecraft.entity.player.EntityPlayer; import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.util.Session; import net.minecraft.util.Session;
import net.minecraft.item.ItemStack;
import net.minecraft.util.math.BlockPos; import net.minecraft.util.math.BlockPos;
import net.minecraft.world.chunk.Chunk; import net.minecraft.world.chunk.Chunk;
@@ -288,6 +290,33 @@ public class ExampleBaritoneControl extends Behavior implements Helper {
logDirect("Baritone settings reset"); logDirect("Baritone settings reset");
return true; return true;
} }
if (msg.equals("echest")) {
Optional<List<ItemStack>> contents = baritone.getMemoryBehavior().echest();
if (contents.isPresent()) {
logDirect("echest contents:");
log(contents.get());
} else {
logDirect("echest contents unknown");
}
return true;
}
if (msg.equals("chests")) {
System.out.println(baritone.getWorldProvider());
System.out.println(baritone.getWorldProvider().getCurrentWorld());
System.out.println(baritone.getWorldProvider().getCurrentWorld().getContainerMemory());
System.out.println(baritone.getWorldProvider().getCurrentWorld().getContainerMemory().getRememberedInventories());
System.out.println(baritone.getWorldProvider().getCurrentWorld().getContainerMemory().getRememberedInventories().entrySet());
System.out.println(baritone.getWorldProvider().getCurrentWorld().getContainerMemory().getRememberedInventories().entrySet());
for (Map.Entry<BlockPos, IRememberedInventory> entry : baritone.getWorldProvider().getCurrentWorld().getContainerMemory().getRememberedInventories().entrySet()) {
logDirect(entry.getKey() + "");
log(entry.getValue().getContents());
}
return true;
}
if (msg.startsWith("followplayers")) { if (msg.startsWith("followplayers")) {
baritone.getFollowProcess().follow(EntityPlayer.class::isInstance); // O P P A baritone.getFollowProcess().follow(EntityPlayer.class::isInstance); // O P P A
logDirect("Following any players"); logDirect("Following any players");
@@ -521,4 +550,12 @@ public class ExampleBaritoneControl extends Behavior implements Helper {
} }
return false; return false;
} }
private void log(List<ItemStack> stacks) {
for (ItemStack stack : stacks) {
if (!stack.isEmpty()) {
logDirect(stack.getCount() + "x " + stack.getDisplayName() + "@" + stack.getItemDamage());
}
}
}
} }
+2 -2
View File
@@ -47,7 +47,7 @@ public interface Helper {
/** /**
* Send a message to chat only if chatDebug is on * Send a message to chat only if chatDebug is on
* *
* @param message * @param message The message to display in chat
*/ */
default void logDebug(String message) { default void logDebug(String message) {
if (!Baritone.settings().chatDebug.get()) { if (!Baritone.settings().chatDebug.get()) {
@@ -61,7 +61,7 @@ public interface Helper {
/** /**
* Send a message to chat regardless of chatDebug (should only be used for critically important messages, or as a direct response to a chat command) * Send a message to chat regardless of chatDebug (should only be used for critically important messages, or as a direct response to a chat command)
* *
* @param message * @param message The message to display in chat
*/ */
default void logDirect(String message) { default void logDirect(String message) {
ITextComponent component = MESSAGE_PREFIX.createCopy(); ITextComponent component = MESSAGE_PREFIX.createCopy();
+29 -6
View File
@@ -21,10 +21,7 @@ import baritone.Baritone;
import baritone.api.BaritoneAPI; import baritone.api.BaritoneAPI;
import baritone.api.event.events.RenderEvent; import baritone.api.event.events.RenderEvent;
import baritone.api.pathing.calc.IPath; import baritone.api.pathing.calc.IPath;
import baritone.api.pathing.goals.Goal; import baritone.api.pathing.goals.*;
import baritone.api.pathing.goals.GoalComposite;
import baritone.api.pathing.goals.GoalTwoBlocks;
import baritone.api.pathing.goals.GoalXZ;
import baritone.api.utils.BetterBlockPos; import baritone.api.utils.BetterBlockPos;
import baritone.api.utils.interfaces.IGoalRenderPos; import baritone.api.utils.interfaces.IGoalRenderPos;
import baritone.behavior.PathingBehavior; import baritone.behavior.PathingBehavior;
@@ -33,6 +30,7 @@ import net.minecraft.block.state.IBlockState;
import net.minecraft.client.renderer.BufferBuilder; import net.minecraft.client.renderer.BufferBuilder;
import net.minecraft.client.renderer.GlStateManager; import net.minecraft.client.renderer.GlStateManager;
import net.minecraft.client.renderer.Tessellator; import net.minecraft.client.renderer.Tessellator;
import net.minecraft.client.renderer.tileentity.TileEntityBeaconRenderer;
import net.minecraft.client.renderer.vertex.DefaultVertexFormats; import net.minecraft.client.renderer.vertex.DefaultVertexFormats;
import net.minecraft.entity.Entity; import net.minecraft.entity.Entity;
import net.minecraft.init.Blocks; import net.minecraft.init.Blocks;
@@ -284,14 +282,14 @@ public final class PathRenderer implements Helper {
minZ = goalPos.getZ() + 0.002 - renderPosZ; minZ = goalPos.getZ() + 0.002 - renderPosZ;
maxZ = goalPos.getZ() + 1 - 0.002 - renderPosZ; maxZ = goalPos.getZ() + 1 - 0.002 - renderPosZ;
double y = MathHelper.cos((float) (((float) ((System.nanoTime() / 100000L) % 20000L)) / 20000F * Math.PI * 2)); double y = MathHelper.cos((float) (((float) ((System.nanoTime() / 100000L) % 20000L)) / 20000F * Math.PI * 2));
if (goal instanceof GoalTwoBlocks) { if (goal instanceof GoalGetToBlock || goal instanceof GoalTwoBlocks) {
y /= 2; y /= 2;
} }
y1 = 1 + y + goalPos.getY() - renderPosY; y1 = 1 + y + goalPos.getY() - renderPosY;
y2 = 1 - y + goalPos.getY() - renderPosY; y2 = 1 - y + goalPos.getY() - renderPosY;
minY = goalPos.getY() - renderPosY; minY = goalPos.getY() - renderPosY;
maxY = minY + 2; maxY = minY + 2;
if (goal instanceof GoalTwoBlocks) { if (goal instanceof GoalGetToBlock || goal instanceof GoalTwoBlocks) {
y1 -= 0.5; y1 -= 0.5;
y2 -= 0.5; y2 -= 0.5;
maxY--; maxY--;
@@ -299,6 +297,31 @@ public final class PathRenderer implements Helper {
} else if (goal instanceof GoalXZ) { } else if (goal instanceof GoalXZ) {
GoalXZ goalPos = (GoalXZ) goal; GoalXZ goalPos = (GoalXZ) goal;
if (Baritone.settings().renderGoalXZBeacon.get()) {
mc.getTextureManager().bindTexture(TileEntityBeaconRenderer.TEXTURE_BEACON_BEAM);
if (Baritone.settings().renderGoalIgnoreDepth.get()) {
GlStateManager.disableDepth();
}
TileEntityBeaconRenderer.renderBeamSegment(
goalPos.getX() - renderPosX,
-renderPosY,
goalPos.getZ() - renderPosZ,
partialTicks,
1.0,
player.world.getTotalWorldTime(),
0,
256,
color.getColorComponents(null)
);
if (Baritone.settings().renderGoalIgnoreDepth.get()) {
GlStateManager.enableDepth();
}
return;
}
minX = goalPos.getX() + 0.002 - renderPosX; minX = goalPos.getX() + 0.002 - renderPosX;
maxX = goalPos.getX() + 1 - 0.002 - renderPosX; maxX = goalPos.getX() + 1 - 0.002 - renderPosX;
minZ = goalPos.getZ() + 0.002 - renderPosZ; minZ = goalPos.getZ() + 0.002 - renderPosZ;
+1 -1
View File
@@ -138,7 +138,7 @@ public class ToolSet {
* @param state the blockstate to be mined * @param state the blockstate to be mined
* @return how long it would take in ticks * @return how long it would take in ticks
*/ */
private double calculateStrVsBlock(ItemStack item, IBlockState state) { public static double calculateStrVsBlock(ItemStack item, IBlockState state) {
float hardness = state.getBlockHardness(null, null); float hardness = state.getBlockHardness(null, null);
if (hardness < 0) { if (hardness < 0) {
return -1; return -1;
@@ -0,0 +1,87 @@
/*
* 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.utils.pathing;
import baritone.Baritone;
import baritone.api.utils.BetterBlockPos;
import baritone.api.utils.IPlayerContext;
import it.unimi.dsi.fastutil.longs.Long2DoubleOpenHashMap;
import net.minecraft.entity.monster.EntityMob;
import net.minecraft.util.math.BlockPos;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
public class Avoidance {
private final int centerX;
private final int centerY;
private final int centerZ;
private final double coefficient;
private final int radius;
private final int radiusSq;
public Avoidance(BlockPos center, double coefficient, int radius) {
this(center.getX(), center.getY(), center.getZ(), coefficient, radius);
}
public Avoidance(int centerX, int centerY, int centerZ, double coefficient, int radius) {
this.centerX = centerX;
this.centerY = centerY;
this.centerZ = centerZ;
this.coefficient = coefficient;
this.radius = radius;
this.radiusSq = radius * radius;
}
public double coefficient(int x, int y, int z) {
int xDiff = x - centerX;
int yDiff = y - centerY;
int zDiff = z - centerZ;
return xDiff * xDiff + yDiff * yDiff + zDiff * zDiff <= radiusSq ? coefficient : 1.0D;
}
public static List<Avoidance> create(IPlayerContext ctx) {
if (!Baritone.settings().avoidance.get()) {
return Collections.emptyList();
}
List<Avoidance> res = new ArrayList<>();
double mobSpawnerCoeff = Baritone.settings().mobSpawnerAvoidanceCoefficient.get();
double mobCoeff = Baritone.settings().mobAvoidanceCoefficient.get();
if (mobSpawnerCoeff != 1.0D) {
ctx.worldData().getCachedWorld().getLocationsOf("mob_spawner", 1, ctx.playerFeet().x, ctx.playerFeet().z, 2).forEach(mobspawner -> res.add(new Avoidance(mobspawner, mobSpawnerCoeff, Baritone.settings().mobSpawnerAvoidanceRadius.get())));
}
if (mobCoeff != 1.0D) {
ctx.world().loadedEntityList.stream().filter(entity -> entity instanceof EntityMob).forEach(entity -> res.add(new Avoidance(new BlockPos(entity), mobCoeff, Baritone.settings().mobAvoidanceRadius.get())));
}
return res;
}
public void applySpherical(Long2DoubleOpenHashMap map) {
for (int x = -radius; x <= radius; x++) {
for (int y = -radius; y <= radius; y++) {
for (int z = -radius; z <= radius; z++) {
if (x * x + y * y + z * z <= radius * radius) {
long hash = BetterBlockPos.longHash(centerX + x, centerY + y, centerZ + z);
map.put(hash, map.get(hash) * coefficient);
}
}
}
}
}
}
@@ -0,0 +1,57 @@
/*
* 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.utils.pathing;
import baritone.Baritone;
import baritone.api.pathing.calc.IPath;
import baritone.api.utils.BetterBlockPos;
import baritone.api.utils.IPlayerContext;
import it.unimi.dsi.fastutil.longs.Long2DoubleOpenHashMap;
import java.util.List;
public final class Favoring {
private List<Avoidance> avoidances;
private final Long2DoubleOpenHashMap favorings;
public Favoring(IPlayerContext ctx, IPath previous) {
this(previous);
avoidances = Avoidance.create(ctx);
for (Avoidance avoid : avoidances) {
avoid.applySpherical(favorings);
}
System.out.println("Favoring size: " + favorings.size());
}
public Favoring(IPath previous) { // create one just from previous path, no mob avoidances
favorings = new Long2DoubleOpenHashMap();
favorings.defaultReturnValue(1.0D);
double coeff = Baritone.settings().backtrackCostFavoringCoefficient.get();
if (coeff != 1D && previous != null) {
previous.positions().forEach(pos -> favorings.put(BetterBlockPos.longHash(pos), coeff));
}
}
public boolean isEmpty() {
return favorings.isEmpty();
}
public double calculate(long hash) {
return favorings.get(hash);
}
}
@@ -21,16 +21,16 @@ import baritone.api.BaritoneAPI;
import baritone.api.pathing.calc.IPath; import baritone.api.pathing.calc.IPath;
import baritone.api.pathing.goals.Goal; import baritone.api.pathing.goals.Goal;
import baritone.pathing.path.CutoffPath; import baritone.pathing.path.CutoffPath;
import baritone.utils.BlockStateInterface;
import net.minecraft.util.math.BlockPos; import net.minecraft.util.math.BlockPos;
import net.minecraft.world.World;
import net.minecraft.world.chunk.EmptyChunk;
public abstract class PathBase implements IPath { public abstract class PathBase implements IPath {
@Override @Override
public IPath cutoffAtLoadedChunks(World world) { public PathBase cutoffAtLoadedChunks(Object bsi0) {
BlockStateInterface bsi = (BlockStateInterface) bsi0;
for (int i = 0; i < positions().size(); i++) { for (int i = 0; i < positions().size(); i++) {
BlockPos pos = positions().get(i); BlockPos pos = positions().get(i);
if (world.getChunk(pos) instanceof EmptyChunk) { if (!bsi.worldContainsLoadedChunk(pos.getX(), pos.getZ())) {
return new CutoffPath(this, i); return new CutoffPath(this, i);
} }
} }
@@ -38,15 +38,16 @@ public abstract class PathBase implements IPath {
} }
@Override @Override
public IPath staticCutoff(Goal destination) { public PathBase staticCutoff(Goal destination) {
if (length() < BaritoneAPI.getSettings().pathCutoffMinimumLength.get()) { int min = BaritoneAPI.getSettings().pathCutoffMinimumLength.get();
if (length() < min) {
return this; return this;
} }
if (destination == null || destination.isInGoal(getDest())) { if (destination == null || destination.isInGoal(getDest())) {
return this; return this;
} }
double factor = BaritoneAPI.getSettings().pathCutoffFactor.get(); double factor = BaritoneAPI.getSettings().pathCutoffFactor.get();
int newLength = (int) ((length() - 1) * factor); int newLength = (int) ((length() - min) * factor) + min - 1;
return new CutoffPath(this, newLength); return new CutoffPath(this, newLength);
} }
} }
@@ -22,8 +22,8 @@ import baritone.api.pathing.calc.IPath;
import baritone.api.pathing.goals.Goal; import baritone.api.pathing.goals.Goal;
import baritone.api.utils.BetterBlockPos; import baritone.api.utils.BetterBlockPos;
import baritone.api.utils.PathCalculationResult; import baritone.api.utils.PathCalculationResult;
import baritone.behavior.PathingBehavior;
import baritone.cache.CachedWorld; import baritone.cache.CachedWorld;
import baritone.pathing.calc.AStarPathFinder;
import baritone.pathing.calc.AbstractNodeCostSearch; import baritone.pathing.calc.AbstractNodeCostSearch;
import baritone.pathing.movement.CalculationContext; import baritone.pathing.movement.CalculationContext;
import baritone.pathing.path.SplicedPath; import baritone.pathing.path.SplicedPath;
@@ -87,7 +87,7 @@ public class SegmentedCalculator {
private PathCalculationResult segment(Optional<IPath> previous) { private PathCalculationResult segment(Optional<IPath> previous) {
BetterBlockPos segmentStart = previous.map(IPath::getDest).orElse(start); // <-- e p i c BetterBlockPos segmentStart = previous.map(IPath::getDest).orElse(start); // <-- e p i c
AbstractNodeCostSearch search = PathingBehavior.createPathfinder(segmentStart, goal, previous.orElse(null), context, false); AbstractNodeCostSearch search = new AStarPathFinder(segmentStart.x, segmentStart.y, segmentStart.z, goal, new Favoring(previous.orElse(null)), context); // this is on another thread, so cannot include mob avoidances.
return search.calculate(Baritone.settings().primaryTimeoutMS.get(), Baritone.settings().failureTimeoutMS.get()); // use normal time settings, not the plan ahead settings, so as to not overwhelm the computer return search.calculate(Baritone.settings().primaryTimeoutMS.get(), Baritone.settings().failureTimeoutMS.get()); // use normal time settings, not the plan ahead settings, so as to not overwhelm the computer
} }
@@ -19,6 +19,9 @@ package baritone.utils.player;
import baritone.api.utils.IPlayerController; import baritone.api.utils.IPlayerController;
import baritone.utils.Helper; import baritone.utils.Helper;
import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.inventory.ClickType;
import net.minecraft.item.ItemStack;
import net.minecraft.util.EnumFacing; import net.minecraft.util.EnumFacing;
import net.minecraft.util.math.BlockPos; import net.minecraft.util.math.BlockPos;
import net.minecraft.world.GameType; import net.minecraft.world.GameType;
@@ -46,6 +49,11 @@ public enum PrimaryPlayerController implements IPlayerController, Helper {
mc.playerController.resetBlockRemoving(); mc.playerController.resetBlockRemoving();
} }
@Override
public ItemStack windowClick(int windowId, int slotId, int mouseButton, ClickType type, EntityPlayer player) {
return mc.playerController.windowClick(windowId, slotId, mouseButton, type, player);
}
@Override @Override
public void setGameType(GameType type) { public void setGameType(GameType type) {
mc.playerController.setGameType(type); mc.playerController.setGameType(type);