Compare commits

...

9 Commits

Author SHA1 Message Date
scorbett123 e91241102c
Merge 292eaef25e into 0c09441154 2024-04-26 07:03:47 -04:00
leijurv 0c09441154
Merge pull request #4324 from ZacSharp/pr/1.19.4/setting/fixRecursiveParserType
Fix recursive setting parsers
2024-04-24 12:50:21 -07:00
leijurv 69d3bc0c2e
Merge pull request #4320 from rfresh2/instabreak-fix
don't apply block break delay to insta-breaks
2024-04-24 12:50:06 -07:00
leijurv b31e0a87cb
Merge pull request #4333 from ZacSharp/pr/1.19.4/misc/toolset/fixHardnessCrash
Fix NPE in break time calculation
2024-04-24 12:49:15 -07:00
ZacSharp 4572b75db7
Fix NPE in break time calculation 2024-04-10 14:03:48 +02:00
rfresh2 15fdbb2312
revert API renaming 2024-04-07 20:30:16 -07:00
ZacSharp 848b7c6de0
Fix recursive parsers
Does not affect existing parsers since all nested parameterized setting types
are `Map<String, List<String>>` so there is no difference between the first
parameter of the inner type and the first parameter of the outer type
2024-04-02 21:32:01 +02:00
rfresh2 2b96a2e463
don't apply block break delay to insta-breaks 2024-04-01 15:21:24 -07:00
scorbett123 292eaef25e Add support for nether vines 2023-08-22 16:52:53 +01:00
9 changed files with 84 additions and 67 deletions

View File

@ -387,7 +387,7 @@ public final class Settings {
/** /**
* How many ticks between breaking a block and starting to break the next block. Default in game is 6 ticks. * How many ticks between breaking a block and starting to break the next block. Default in game is 6 ticks.
* Values under 2 will be clamped. * Values under 1 will be clamped. The delay only applies to non-instant (1-tick) breaks.
*/ */
public final Setting<Integer> blockBreakSpeed = new Setting<>(6); public final Setting<Integer> blockBreakSpeed = new Setting<>(6);

View File

@ -149,7 +149,7 @@ public class SettingsUtil {
throw new IllegalStateException("Missing " + setting.getValueClass() + " " + setting.getName()); throw new IllegalStateException("Missing " + setting.getValueClass() + " " + setting.getName());
} }
return io.toString(new ParserContext(setting), value); return io.toString(setting.getType(), value);
} }
public static String settingValueToString(Settings.Setting setting) throws IllegalArgumentException { public static String settingValueToString(Settings.Setting setting) throws IllegalArgumentException {
@ -196,7 +196,7 @@ public class SettingsUtil {
} }
Class intendedType = setting.getValueClass(); Class intendedType = setting.getValueClass();
ISettingParser ioMethod = Parser.getParser(setting.getType()); ISettingParser ioMethod = Parser.getParser(setting.getType());
Object parsed = ioMethod.parse(new ParserContext(setting), settingValue); Object parsed = ioMethod.parse(setting.getType(), settingValue);
if (!intendedType.isInstance(parsed)) { if (!intendedType.isInstance(parsed)) {
throw new IllegalStateException(ioMethod + " parser returned incorrect type, expected " + intendedType + " got " + parsed + " which is " + parsed.getClass()); throw new IllegalStateException(ioMethod + " parser returned incorrect type, expected " + intendedType + " got " + parsed + " which is " + parsed.getClass());
} }
@ -205,26 +205,13 @@ public class SettingsUtil {
private interface ISettingParser<T> { private interface ISettingParser<T> {
T parse(ParserContext context, String raw); T parse(Type type, String raw);
String toString(ParserContext context, T value); String toString(Type type, T value);
boolean accepts(Type type); boolean accepts(Type type);
} }
private static class ParserContext {
private final Settings.Setting<?> setting;
private ParserContext(Settings.Setting<?> setting) {
this.setting = setting;
}
private Settings.Setting<?> getSetting() {
return this.setting;
}
}
private enum Parser implements ISettingParser { private enum Parser implements ISettingParser {
DOUBLE(Double.class, Double::parseDouble), DOUBLE(Double.class, Double::parseDouble),
@ -256,21 +243,21 @@ public class SettingsUtil {
), ),
LIST() { LIST() {
@Override @Override
public Object parse(ParserContext context, String raw) { public Object parse(Type type, String raw) {
Type type = ((ParameterizedType) context.getSetting().getType()).getActualTypeArguments()[0]; Type elementType = ((ParameterizedType) type).getActualTypeArguments()[0];
Parser parser = Parser.getParser(type); Parser parser = Parser.getParser(elementType);
return Stream.of(raw.split(",")) return Stream.of(raw.split(","))
.map(s -> parser.parse(context, s)) .map(s -> parser.parse(elementType, s))
.collect(Collectors.toList()); .collect(Collectors.toList());
} }
@Override @Override
public String toString(ParserContext context, Object value) { public String toString(Type type, Object value) {
Type type = ((ParameterizedType) context.getSetting().getType()).getActualTypeArguments()[0]; Type elementType = ((ParameterizedType) type).getActualTypeArguments()[0];
Parser parser = Parser.getParser(type); Parser parser = Parser.getParser(elementType);
return ((List<?>) value).stream() return ((List<?>) value).stream()
.map(o -> parser.toString(context, o)) .map(o -> parser.toString(elementType, o))
.collect(Collectors.joining(",")); .collect(Collectors.joining(","));
} }
@ -281,26 +268,26 @@ public class SettingsUtil {
}, },
MAPPING() { MAPPING() {
@Override @Override
public Object parse(ParserContext context, String raw) { public Object parse(Type type, String raw) {
Type keyType = ((ParameterizedType) context.getSetting().getType()).getActualTypeArguments()[0]; Type keyType = ((ParameterizedType) type).getActualTypeArguments()[0];
Type valueType = ((ParameterizedType) context.getSetting().getType()).getActualTypeArguments()[1]; Type valueType = ((ParameterizedType) type).getActualTypeArguments()[1];
Parser keyParser = Parser.getParser(keyType); Parser keyParser = Parser.getParser(keyType);
Parser valueParser = Parser.getParser(valueType); Parser valueParser = Parser.getParser(valueType);
return Stream.of(raw.split(",(?=[^,]*->)")) return Stream.of(raw.split(",(?=[^,]*->)"))
.map(s -> s.split("->")) .map(s -> s.split("->"))
.collect(Collectors.toMap(s -> keyParser.parse(context, s[0]), s -> valueParser.parse(context, s[1]))); .collect(Collectors.toMap(s -> keyParser.parse(keyType, s[0]), s -> valueParser.parse(valueType, s[1])));
} }
@Override @Override
public String toString(ParserContext context, Object value) { public String toString(Type type, Object value) {
Type keyType = ((ParameterizedType) context.getSetting().getType()).getActualTypeArguments()[0]; Type keyType = ((ParameterizedType) type).getActualTypeArguments()[0];
Type valueType = ((ParameterizedType) context.getSetting().getType()).getActualTypeArguments()[1]; Type valueType = ((ParameterizedType) type).getActualTypeArguments()[1];
Parser keyParser = Parser.getParser(keyType); Parser keyParser = Parser.getParser(keyType);
Parser valueParser = Parser.getParser(valueType); Parser valueParser = Parser.getParser(valueType);
return ((Map<?, ?>) value).entrySet().stream() return ((Map<?, ?>) value).entrySet().stream()
.map(o -> keyParser.toString(context, o.getKey()) + "->" + valueParser.toString(context, o.getValue())) .map(o -> keyParser.toString(keyType, o.getKey()) + "->" + valueParser.toString(valueType, o.getValue()))
.collect(Collectors.joining(",")); .collect(Collectors.joining(","));
} }
@ -331,14 +318,14 @@ public class SettingsUtil {
} }
@Override @Override
public Object parse(ParserContext context, String raw) { public Object parse(Type type, String raw) {
Object parsed = this.parser.apply(raw); Object parsed = this.parser.apply(raw);
Objects.requireNonNull(parsed); Objects.requireNonNull(parsed);
return parsed; return parsed;
} }
@Override @Override
public String toString(ParserContext context, Object value) { public String toString(Type type, Object value) {
return this.toString.apply(value); return this.toString.apply(value);
} }

View File

@ -31,6 +31,10 @@ public abstract class MixinPlayerController implements IPlayerControllerMP {
@Override @Override
public abstract void setIsHittingBlock(boolean isHittingBlock); public abstract void setIsHittingBlock(boolean isHittingBlock);
@Accessor("isDestroying")
@Override
public abstract boolean isHittingBlock();
@Accessor("destroyBlockPos") @Accessor("destroyBlockPos")
@Override @Override
public abstract BlockPos getCurrentBlock(); public abstract BlockPos getCurrentBlock();
@ -38,4 +42,8 @@ public abstract class MixinPlayerController implements IPlayerControllerMP {
@Invoker("ensureHasSentCarriedItem") @Invoker("ensureHasSentCarriedItem")
@Override @Override
public abstract void callSyncCurrentPlayItem(); public abstract void callSyncCurrentPlayItem();
@Accessor("destroyDelay")
@Override
public abstract void setDestroyDelay(int destroyDelay);
} }

View File

@ -65,7 +65,7 @@ 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) {
BlockState fromState = context.get(x, y, z); BlockState fromState = context.get(x, y, z);
Block from = fromState.getBlock(); Block from = fromState.getBlock();
boolean ladder = from == Blocks.LADDER || from == Blocks.VINE; boolean ladder = from == Blocks.LADDER || from == Blocks.VINE || from == Blocks.WEEPING_VINES || from == Blocks.TWISTING_VINES || from == Blocks.WEEPING_VINES_PLANT || from == Blocks.TWISTING_VINES_PLANT;
BlockState fromDown = context.get(x, y - 1, z); BlockState fromDown = context.get(x, y - 1, z);
if (!ladder) { if (!ladder) {
if (fromDown.getBlock() == Blocks.LADDER || fromDown.getBlock() == Blocks.VINE) { if (fromDown.getBlock() == Blocks.LADDER || fromDown.getBlock() == Blocks.VINE) {
@ -116,7 +116,7 @@ public class MovementPillar extends Movement {
return COST_INF; return COST_INF;
} }
if (hardness != 0) { if (hardness != 0) {
if (toBreakBlock == Blocks.LADDER || toBreakBlock == Blocks.VINE) { if (toBreakBlock == Blocks.LADDER || toBreakBlock == Blocks.VINE || toBreakBlock == Blocks.WEEPING_VINES || toBreakBlock == Blocks.TWISTING_VINES) {
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 {
BlockState check = context.get(x, y + 3, z); // the block on top of the one we're going to break, could it fall on us? BlockState check = context.get(x, y + 3, z); // the block on top of the one we're going to break, could it fall on us?
@ -194,10 +194,12 @@ public class MovementPillar extends Movement {
} }
boolean ladder = fromDown.getBlock() == Blocks.LADDER || fromDown.getBlock() == Blocks.VINE; boolean ladder = fromDown.getBlock() == Blocks.LADDER || fromDown.getBlock() == Blocks.VINE;
boolean vine = fromDown.getBlock() == Blocks.VINE; boolean vine = fromDown.getBlock() == Blocks.VINE;
boolean nether_vine = fromDown.getBlock() == Blocks.WEEPING_VINES || fromDown.getBlock() == Blocks.TWISTING_VINES || fromDown.getBlock() == Blocks.WEEPING_VINES_PLANT || fromDown.getBlock() == Blocks.TWISTING_VINES_PLANT;
Rotation rotation = RotationUtils.calcRotationFromVec3d(ctx.playerHead(), Rotation rotation = RotationUtils.calcRotationFromVec3d(ctx.playerHead(),
VecUtils.getBlockPosCenter(positionToPlace), VecUtils.getBlockPosCenter(positionToPlace),
ctx.playerRotations()); ctx.playerRotations());
if (!ladder) { if (!(ladder || nether_vine)) {
state.setTarget(new MovementState.MovementTarget(ctx.playerRotations().withPitch(rotation.getPitch()), true)); state.setTarget(new MovementState.MovementTarget(ctx.playerRotations().withPitch(rotation.getPitch()), true));
} }
@ -223,6 +225,13 @@ public class MovementPillar extends Movement {
MovementHelper.moveTowards(ctx, state, against); MovementHelper.moveTowards(ctx, state, against);
return state; return state;
} else if (nether_vine) {
if (ctx.playerFeet().equals(dest)) {
return state.setStatus(MovementStatus.SUCCESS);
}
MovementHelper.moveTowards(ctx, state, dest);
state.setInput(Input.JUMP, true);
return state;
} else { } else {
// Get ready to place a throwaway block // Get ready to place a throwaway block
if (!((Baritone) baritone).getInventoryBehavior().selectThrowawayForLocation(true, src.x, src.y, src.z)) { if (!((Baritone) baritone).getInventoryBehavior().selectThrowawayForLocation(true, src.x, src.y, src.z)) {

View File

@ -114,13 +114,13 @@ public class MovementTraverse extends Movement {
} }
return WC; return WC;
} }
if (srcDownBlock == Blocks.LADDER || srcDownBlock == Blocks.VINE) { if (srcDownBlock == Blocks.LADDER || srcDownBlock == Blocks.VINE || srcDownBlock == Blocks.WEEPING_VINES || srcDownBlock == Blocks.TWISTING_VINES || srcDownBlock == Blocks.WEEPING_VINES_PLANT || srcDownBlock == Blocks.TWISTING_VINES_PLANT) {
hardness1 *= 5; hardness1 *= 5;
hardness2 *= 5; hardness2 *= 5;
} }
return WC + hardness1 + hardness2; return WC + hardness1 + hardness2;
} else {//this is a bridge, so we need to place a block } else {//this is a bridge, so we need to place a block
if (srcDownBlock == Blocks.LADDER || srcDownBlock == Blocks.VINE) { if (srcDownBlock == Blocks.LADDER || srcDownBlock == Blocks.VINE || srcDownBlock == Blocks.WEEPING_VINES || srcDownBlock == Blocks.TWISTING_VINES || srcDownBlock == Blocks.WEEPING_VINES_PLANT || srcDownBlock == Blocks.TWISTING_VINES_PLANT) {
return COST_INF; return COST_INF;
} }
if (MovementHelper.isReplaceable(destX, y - 1, destZ, destOn, context.bsi)) { if (MovementHelper.isReplaceable(destX, y - 1, destZ, destOn, context.bsi)) {
@ -217,7 +217,7 @@ public class MovementTraverse extends Movement {
state.setInput(Input.SNEAK, false); state.setInput(Input.SNEAK, false);
Block fd = BlockStateInterface.get(ctx, src.below()).getBlock(); Block fd = BlockStateInterface.get(ctx, src.below()).getBlock();
boolean ladder = fd == Blocks.LADDER || fd == Blocks.VINE; boolean ladder = fd == Blocks.LADDER || fd == Blocks.VINE || fd == Blocks.WEEPING_VINES || fd == Blocks.TWISTING_VINES || fd == Blocks.WEEPING_VINES_PLANT || fd == Blocks.TWISTING_VINES_PLANT;
if (pb0.getBlock() instanceof DoorBlock || pb1.getBlock() instanceof DoorBlock) { if (pb0.getBlock() instanceof DoorBlock || pb1.getBlock() instanceof DoorBlock) {
boolean notPassable = pb0.getBlock() instanceof DoorBlock && !MovementHelper.isDoorPassable(ctx, src, dest) || pb1.getBlock() instanceof DoorBlock && !MovementHelper.isDoorPassable(ctx, dest, src); boolean notPassable = pb0.getBlock() instanceof DoorBlock && !MovementHelper.isDoorPassable(ctx, src, dest) || pb1.getBlock() instanceof DoorBlock && !MovementHelper.isDoorPassable(ctx, dest, src);
@ -370,7 +370,7 @@ public class MovementTraverse extends Movement {
protected boolean prepared(MovementState state) { protected boolean prepared(MovementState state) {
if (ctx.playerFeet().equals(src) || ctx.playerFeet().equals(src.below())) { if (ctx.playerFeet().equals(src) || ctx.playerFeet().equals(src.below())) {
Block block = BlockStateInterface.getBlock(ctx, src.below()); Block block = BlockStateInterface.getBlock(ctx, src.below());
if (block == Blocks.LADDER || block == Blocks.VINE) { if (block == Blocks.LADDER || block == Blocks.VINE || block == Blocks.WEEPING_VINES || block == Blocks.TWISTING_VINES || block == Blocks.WEEPING_VINES_PLANT || block == Blocks.TWISTING_VINES_PLANT) {
state.setInput(Input.SNEAK, true); state.setInput(Input.SNEAK, true);
} }
} }

View File

@ -19,6 +19,7 @@ package baritone.utils;
import baritone.api.BaritoneAPI; import baritone.api.BaritoneAPI;
import baritone.api.utils.IPlayerContext; import baritone.api.utils.IPlayerContext;
import baritone.utils.accessor.IPlayerControllerMP;
import net.minecraft.world.InteractionHand; import net.minecraft.world.InteractionHand;
import net.minecraft.world.phys.BlockHitResult; import net.minecraft.world.phys.BlockHitResult;
import net.minecraft.world.phys.HitResult; import net.minecraft.world.phys.HitResult;
@ -29,10 +30,10 @@ import net.minecraft.world.phys.HitResult;
*/ */
public final class BlockBreakHelper { public final class BlockBreakHelper {
// base ticks between block breaks caused by tick logic // base ticks between block breaks caused by tick logic
private static final int BASE_BREAK_DELAY = 2; private static final int BASE_BREAK_DELAY = 1;
private final IPlayerContext ctx; private final IPlayerContext ctx;
private boolean didBreakLastTick; private boolean wasHitting;
private int breakDelayTimer = 0; private int breakDelayTimer = 0;
BlockBreakHelper(IPlayerContext ctx) { BlockBreakHelper(IPlayerContext ctx) {
@ -41,13 +42,10 @@ public final class BlockBreakHelper {
public void stopBreakingBlock() { public void stopBreakingBlock() {
// The player controller will never be null, but the player can be // The player controller will never be null, but the player can be
if (ctx.player() != null && didBreakLastTick) { if (ctx.player() != null && wasHitting) {
if (!ctx.playerController().hasBrokenBlock()) { ctx.playerController().setHittingBlock(false);
// insane bypass to check breaking succeeded
ctx.playerController().setHittingBlock(true);
}
ctx.playerController().resetBlockRemoving(); ctx.playerController().resetBlockRemoving();
didBreakLastTick = false; wasHitting = false;
} }
} }
@ -60,24 +58,30 @@ public final class BlockBreakHelper {
boolean isBlockTrace = trace != null && trace.getType() == HitResult.Type.BLOCK; boolean isBlockTrace = trace != null && trace.getType() == HitResult.Type.BLOCK;
if (isLeftClick && isBlockTrace) { if (isLeftClick && isBlockTrace) {
if (!didBreakLastTick) { ctx.playerController().setHittingBlock(wasHitting);
if (ctx.playerController().hasBrokenBlock()) {
ctx.playerController().syncHeldItem(); ctx.playerController().syncHeldItem();
ctx.playerController().clickBlock(((BlockHitResult) trace).getBlockPos(), ((BlockHitResult) trace).getDirection()); ctx.playerController().clickBlock(((BlockHitResult) trace).getBlockPos(), ((BlockHitResult) trace).getDirection());
ctx.player().swing(InteractionHand.MAIN_HAND); ctx.player().swing(InteractionHand.MAIN_HAND);
} else {
if (ctx.playerController().onPlayerDamageBlock(((BlockHitResult) trace).getBlockPos(), ((BlockHitResult) trace).getDirection())) {
ctx.player().swing(InteractionHand.MAIN_HAND);
}
if (ctx.playerController().hasBrokenBlock()) { // block broken this tick
// break delay timer only applies for multi-tick block breaks like vanilla
breakDelayTimer = BaritoneAPI.getSettings().blockBreakSpeed.value - BASE_BREAK_DELAY;
// must reset controller's destroy delay to prevent the client from delaying itself unnecessarily
((IPlayerControllerMP) ctx.minecraft().gameMode).setDestroyDelay(0);
}
} }
// if true, we're breaking a block. if false, we broke the block this tick
// Attempt to break the block wasHitting = !ctx.playerController().hasBrokenBlock();
if (ctx.playerController().onPlayerDamageBlock(((BlockHitResult) trace).getBlockPos(), ((BlockHitResult) trace).getDirection())) { // this value will be reset by the MC client handling mouse keys
ctx.player().swing(InteractionHand.MAIN_HAND); // since we're not spoofing the click keybind to the client, the client will stop the break if isDestroyingBlock is true
} // we store and restore this value on the next tick to determine if we're breaking a block
ctx.playerController().setHittingBlock(false); ctx.playerController().setHittingBlock(false);
} else {
didBreakLastTick = true; wasHitting = false;
} else if (didBreakLastTick) {
stopBreakingBlock();
breakDelayTimer = BaritoneAPI.getSettings().blockBreakSpeed.value - BASE_BREAK_DELAY;
didBreakLastTick = false;
} }
} }
} }

View File

@ -177,7 +177,13 @@ public class ToolSet {
* @return how long it would take in ticks * @return how long it would take in ticks
*/ */
public static double calculateSpeedVsBlock(ItemStack item, BlockState state) { public static double calculateSpeedVsBlock(ItemStack item, BlockState state) {
float hardness = state.getDestroySpeed(null, null); float hardness;
try {
hardness = state.getDestroySpeed(null, null);
} catch (NullPointerException npe) {
// can't easily determine the hardness so treat it as unbreakable
return -1;
}
if (hardness < 0) { if (hardness < 0) {
return -1; return -1;
} }

View File

@ -23,7 +23,11 @@ public interface IPlayerControllerMP {
void setIsHittingBlock(boolean isHittingBlock); void setIsHittingBlock(boolean isHittingBlock);
boolean isHittingBlock();
BlockPos getCurrentBlock(); BlockPos getCurrentBlock();
void callSyncCurrentPlayItem(); void callSyncCurrentPlayItem();
void setDestroyDelay(int destroyDelay);
} }

View File

@ -20,7 +20,6 @@ package baritone.utils.player;
import baritone.api.utils.IPlayerController; import baritone.api.utils.IPlayerController;
import baritone.utils.accessor.IPlayerControllerMP; import baritone.utils.accessor.IPlayerControllerMP;
import net.minecraft.client.Minecraft; import net.minecraft.client.Minecraft;
import net.minecraft.client.multiplayer.ClientLevel;
import net.minecraft.client.player.LocalPlayer; import net.minecraft.client.player.LocalPlayer;
import net.minecraft.core.BlockPos; import net.minecraft.core.BlockPos;
import net.minecraft.core.Direction; import net.minecraft.core.Direction;
@ -54,7 +53,7 @@ public final class BaritonePlayerController implements IPlayerController {
@Override @Override
public boolean hasBrokenBlock() { public boolean hasBrokenBlock() {
return ((IPlayerControllerMP) mc.gameMode).getCurrentBlock().getY() == -1; return !((IPlayerControllerMP) mc.gameMode).isHittingBlock();
} }
@Override @Override