Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
import baritone.pathing.movement.MovementState;
import baritone.pathing.movement.MovementState.MovementTarget;
import baritone.utils.pathing.MutableMoveResult;
import baritone.utils.reflection.InteractabilityHelper;
import java.util.HashSet;
import java.util.Optional;
import java.util.Set;
Expand Down Expand Up @@ -105,6 +106,9 @@ public MovementState updateState(MovementState state) {

targetRotation = new Rotation(toDest.getYaw(), 90.0F);

boolean clickable = InteractabilityHelper.hasRightClickAction(ctx.world().getBlockState(dest.below()));
state.setInput(Input.SNEAK, clickable);

if (ctx.isLookingAt(dest) || ctx.isLookingAt(dest.below())) {
state.setInput(Input.CLICK_RIGHT, true);
}
Expand All @@ -119,7 +123,12 @@ public MovementState updateState(MovementState state) {
if (isWater) { // only match water, not flowing water (which we cannot pick up with a bucket)
if (Inventory.isHotbarSlot(ctx.player().getInventory().findSlotMatchingItem(STACK_BUCKET_EMPTY))) {
ctx.player().getInventory().selected = ctx.player().getInventory().findSlotMatchingItem(STACK_BUCKET_EMPTY);
if (ctx.player().getDeltaMovement().y >= 0) {

// Note: this can not happen if there is water below, so no risk of drowning
boolean clickable = InteractabilityHelper.hasRightClickAction(ctx.world().getBlockState(dest.below()));
state.setInput(Input.SNEAK, clickable);

if (ctx.player().getDeltaMovement().y >= 0 || ctx.player().isCrouching()) {
return state.setInput(Input.CLICK_RIGHT, true);
} else {
return state;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
import baritone.pathing.movement.MovementState;
import baritone.utils.BlockStateInterface;
import baritone.utils.pathing.MutableMoveResult;
import baritone.utils.reflection.InteractabilityHelper;
import net.minecraft.core.Direction;
import net.minecraft.world.level.block.Block;
import net.minecraft.world.level.block.Blocks;
Expand Down Expand Up @@ -190,7 +191,11 @@ public static void cost(CalculationContext context, int x, int y, int z, Directi
if (againstX == destX - xDiff && againstZ == destZ - zDiff) { // we can't turn around that fast
continue;
}
if (MovementHelper.canPlaceAgainst(context.bsi, againstX, againstY, againstZ)) {
if (!MovementHelper.canPlaceAgainst(context.bsi, againstX, againstY, againstZ)) {
continue;
}
// TODO Reflection during movement evaluation! This is BAD!
if (!InteractabilityHelper.hasRightClickAction(context.bsi.get0(againstX, againstY, againstZ))) {
res.x = destX;
res.y = y;
res.z = destZ;
Expand Down
17 changes: 11 additions & 6 deletions src/main/java/baritone/process/BuilderProcess.java
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@
import baritone.utils.BaritoneProcessHelper;
import baritone.utils.BlockStateInterface;
import baritone.utils.PathingCommandContext;
import baritone.utils.reflection.InteractabilityHelper;
import baritone.utils.schematic.MapArtSchematic;
import baritone.utils.schematic.SelectionSchematic;
import baritone.utils.schematic.SchematicSystem;
Expand Down Expand Up @@ -315,12 +316,14 @@ public static class Placement {
private final BlockPos placeAgainst;
private final Direction side;
private final Rotation rot;
private final boolean sneak;

public Placement(int hotbarSelection, BlockPos placeAgainst, Direction side, Rotation rot) {
public Placement(int hotbarSelection, BlockPos placeAgainst, Direction side, Rotation rot, boolean sneak) {
this.hotbarSelection = hotbarSelection;
this.placeAgainst = placeAgainst;
this.side = side;
this.rot = rot;
this.sneak = sneak;
}
}

Expand Down Expand Up @@ -375,18 +378,20 @@ private Optional<Placement> possibleToPlace(BlockState toPlace, int x, int y, in
if (shape.isEmpty()) {
continue;
}
boolean wouldSneak = InteractabilityHelper.hasRightClickAction(placeAgainstState);
Vec3 eyePosition = wouldSneak ? RayTraceUtils.inferSneakingEyePosition(ctx.player()) : ctx.playerHead();
AABB aabb = shape.bounds();
for (Vec3 placementMultiplier : aabbSideMultipliers(against)) {
double placeX = placeAgainstPos.x + aabb.minX * placementMultiplier.x + aabb.maxX * (1 - placementMultiplier.x);
double placeY = placeAgainstPos.y + aabb.minY * placementMultiplier.y + aabb.maxY * (1 - placementMultiplier.y);
double placeZ = placeAgainstPos.z + aabb.minZ * placementMultiplier.z + aabb.maxZ * (1 - placementMultiplier.z);
Rotation rot = RotationUtils.calcRotationFromVec3d(RayTraceUtils.inferSneakingEyePosition(ctx.player()), new Vec3(placeX, placeY, placeZ), ctx.playerRotations());
Rotation rot = RotationUtils.calcRotationFromVec3d(eyePosition, new Vec3(placeX, placeY, placeZ), ctx.playerRotations());
Rotation actualRot = baritone.getLookBehavior().getAimProcessor().peekRotation(rot);
HitResult result = RayTraceUtils.rayTraceTowards(ctx.player(), actualRot, ctx.playerController().getBlockReachDistance(), true);
HitResult result = RayTraceUtils.rayTraceTowards(ctx.player(), actualRot, ctx.playerController().getBlockReachDistance(), wouldSneak);
if (result != null && result.getType() == HitResult.Type.BLOCK && ((BlockHitResult) result).getBlockPos().equals(placeAgainstPos) && ((BlockHitResult) result).getDirection() == against.getOpposite()) {
OptionalInt hotbar = hasAnyItemThatWouldPlace(toPlace, result, actualRot);
if (hotbar.isPresent()) {
return Optional.of(new Placement(hotbar.getAsInt(), placeAgainstPos, against.getOpposite(), rot));
return Optional.of(new Placement(hotbar.getAsInt(), placeAgainstPos, against.getOpposite(), rot, wouldSneak));
}
}
}
Expand Down Expand Up @@ -569,8 +574,8 @@ public int lengthZ() {
Rotation rot = toPlace.get().rot;
baritone.getLookBehavior().updateTarget(rot, true);
ctx.player().getInventory().selected = toPlace.get().hotbarSelection;
baritone.getInputOverrideHandler().setInputForceState(Input.SNEAK, true);
if ((ctx.isLookingAt(toPlace.get().placeAgainst) && ((BlockHitResult) ctx.objectMouseOver()).getDirection().equals(toPlace.get().side)) || ctx.playerRotations().isReallyCloseTo(rot)) {
baritone.getInputOverrideHandler().setInputForceState(Input.SNEAK, toPlace.get().sneak);
if (ctx.player().isCrouching() == toPlace.get().sneak && (ctx.isLookingAt(toPlace.get().placeAgainst) && ((BlockHitResult) ctx.objectMouseOver()).getDirection().equals(toPlace.get().side)) || ctx.playerRotations().isReallyCloseTo(rot)) {
baritone.getInputOverrideHandler().setInputForceState(Input.CLICK_RIGHT, true);
}
return new PathingCommand(null, PathingCommandType.CANCEL_AND_SET_GOAL);
Expand Down
56 changes: 56 additions & 0 deletions src/main/java/baritone/utils/reflection/InteractabilityHelper.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
package baritone.utils.reflection;

import net.minecraft.world.level.block.Block;
import net.minecraft.world.level.block.state.BlockBehaviour;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.InteractionHand;
import net.minecraft.world.InteractionResult;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.level.Level;
import net.minecraft.world.phys.BlockHitResult;
import net.minecraft.core.BlockPos;

import java.lang.reflect.Method;
import java.util.Map;

/**
* @author ZacSharp
* @since 3/26/2025
*/
public class InteractabilityHelper {

// Note that this can/must not be invoked (type error)
private static final Method BLOCK_USE;

static {
Map<String, Method> blockMethods = ReflectionHelper.getMarkedMethods(DummyBlock.class);
BLOCK_USE = blockMethods.get("onInteract");
}

private InteractabilityHelper() {}

public static boolean hasRightClickAction(BlockState state) {
return hasRightClickAction(state.getBlock());
}

public static boolean hasRightClickAction(Block block) {
return ReflectionHelper.getBySignature(block.getClass(), BLOCK_USE).getDeclaringClass() != BlockBehaviour.class;
}

private static <T, E extends Throwable> T raise(E ex) throws E {
throw ex;
}

private static final class DummyBlock extends Block {
private DummyBlock() {
super(raise(new UnsupportedOperationException()));
}

@Override
@ReflectionHelper.Marker("onInteract")
public InteractionResult use(BlockState p_60503_, Level p_60504_, BlockPos p_60505_, Player p_60506_, InteractionHand p_60507_, BlockHitResult p_60508_) {
throw new UnsupportedOperationException();
}
}
}
60 changes: 60 additions & 0 deletions src/main/java/baritone/utils/reflection/ReflectionHelper.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
package baritone.utils.reflection;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.HashMap;
import java.util.Map;

/**
* @author ZacSharp
* @since 3/26/2025
*/
public class ReflectionHelper {

private ReflectionHelper() {}

// Note: This approach works with public/protected/package-private methods only.
// If we need fields or private methods that should be possible with some mixin
// trickery like a custom injection point with the name we need as its target.
/**
* Get a map with all declared in {@code class} with the {@code Marker} annotation
* using the annotation value as the map key.
*/
public static Map<String, Method> getMarkedMethods(Class<? extends Object> cls) {
Map<String, Method> methods = new HashMap<>();
for (Method method : cls.getDeclaredMethods()) {
Marker marker = method.getAnnotation(Marker.class);
if (marker == null) {
continue;
}
methods.put(marker.value(), method);
}
return methods;
}

/**
* Gets a public method of {@code cls} with the same name and parameter
* types as {@code sig}, or null if such a method does not exist.
*
* @param the class to search in
* @param a method with the same signature as the sought method
* @return the found method or null
*/
public static Method getBySignature(Class<? extends Object> cls, Method sig) {
try {
return cls.getMethod(sig.getName(), sig.getParameterTypes());
} catch (NoSuchMethodException ex) {
return null;
}
}

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public static @interface Marker {
String value();
}
}