diff --git a/buildSrc/src/main/java/baritone/gradle/task/ProguardTask.java b/buildSrc/src/main/java/baritone/gradle/task/ProguardTask.java index 01e56177..1789435f 100644 --- a/buildSrc/src/main/java/baritone/gradle/task/ProguardTask.java +++ b/buildSrc/src/main/java/baritone/gradle/task/ProguardTask.java @@ -209,8 +209,8 @@ public class ProguardTask extends BaritoneGradleTask { // Setup the template that will be used to derive the API and Standalone configs List template = Files.readAllLines(getTemporaryFile(PROGUARD_CONFIG_DEST)); - template.add(0, "-injars " + this.artifactPath.toString()); - template.add(1, "-outjars " + this.getTemporaryFile(PROGUARD_EXPORT_PATH)); + template.add(0, "-injars '" + this.artifactPath.toString() + "'"); + template.add(1, "-outjars '" + this.getTemporaryFile(PROGUARD_EXPORT_PATH) + "'"); // Acquire the RT jar using "java -verbose". This doesn't work on Java 9+ Process p = new ProcessBuilder(this.getJavaBinPathForProguard(), "-verbose").start(); @@ -405,9 +405,15 @@ public class ProguardTask extends BaritoneGradleTask { Files.delete(this.proguardOut); } - Path proguardJar = getTemporaryFile(PROGUARD_JAR); + // Make paths relative to work directory; fixes spaces in path to config, @"" doesn't work + Path workingDirectory = getTemporaryFile(""); + Path proguardJar = workingDirectory.relativize(getTemporaryFile(PROGUARD_JAR)); + config = workingDirectory.relativize(config); + + // Honestly, if you still have spaces in your path at this point, you're SOL. + Process p = new ProcessBuilder("java", "-jar", proguardJar.toString(), "@" + config.toString()) - .directory(getTemporaryFile("").toFile()) // Set the working directory to the temporary folder] + .directory(workingDirectory.toFile()) // Set the working directory to the temporary folder] .start(); // We can't do output inherit process I/O with gradle for some reason and have it work, so we have to do this diff --git a/scripts/proguard.pro b/scripts/proguard.pro index 517494f4..cc313008 100644 --- a/scripts/proguard.pro +++ b/scripts/proguard.pro @@ -42,8 +42,10 @@ #try to keep usage of schematica in separate classes -keep class baritone.utils.schematic.schematica.** +-keep class baritone.utils.schematic.litematica.** #proguard doesnt like it when it cant find our fake schematica classes -dontwarn baritone.utils.schematic.schematica.** +-dontwarn baritone.utils.schematic.litematica.** # copy all necessary libraries into tempLibraries to build diff --git a/src/api/java/baritone/api/Settings.java b/src/api/java/baritone/api/Settings.java index 0cc80c38..dd16cc07 100644 --- a/src/api/java/baritone/api/Settings.java +++ b/src/api/java/baritone/api/Settings.java @@ -34,8 +34,8 @@ import java.lang.reflect.ParameterizedType; import java.lang.reflect.Type; import java.util.List; import java.util.*; -import java.util.function.Consumer; import java.util.function.BiConsumer; +import java.util.function.Consumer; /** * Baritone's settings. Settings apply to all Baritone instances. @@ -198,7 +198,7 @@ public final class Settings { * Blocks that Baritone is not allowed to break */ public final Setting> blocksToDisallowBreaking = new Setting<>(new ArrayList<>( - // Leave Empty by Default + // Leave Empty by Default )); /** @@ -281,6 +281,12 @@ public final class Settings { */ public final Setting buildIgnoreDirection = new Setting<>(false); + /** + * A list of names of block properties the builder will ignore. + */ + public final Setting> buildIgnoreProperties = new Setting<>(new ArrayList<>(Arrays.asList( + ))); + /** * If this setting is true, Baritone will never break a block that is adjacent to an unsupported falling block. *

@@ -914,7 +920,7 @@ public final class Settings { /** * Only build the selected part of schematics */ - public final Setting buildOnlySelection = new Setting<>(false); + public final Setting buildOnlySelection = new Setting<>(false); /** * How far to move before repeating the build. 0 to disable repeating on a certain axis, 0,0,0 to disable entirely diff --git a/src/api/java/baritone/api/command/datatypes/RelativeCoordinate.java b/src/api/java/baritone/api/command/datatypes/RelativeCoordinate.java index 7d77a96c..b8f30a77 100644 --- a/src/api/java/baritone/api/command/datatypes/RelativeCoordinate.java +++ b/src/api/java/baritone/api/command/datatypes/RelativeCoordinate.java @@ -26,7 +26,8 @@ import java.util.stream.Stream; public enum RelativeCoordinate implements IDatatypePost { INSTANCE; - private static Pattern PATTERN = Pattern.compile("^(~?)([+-]?(?:\\d+(?:\\.\\d*)?|\\.\\d+)([k-k]?)|)$"); + private static String ScalesAliasRegex = "[kKmM]"; + private static Pattern PATTERN = Pattern.compile("^(~?)([+-]?(?:\\d+(?:\\.\\d*)?|\\.\\d+)("+ScalesAliasRegex+"?)|)$"); @Override public Double apply(IDatatypeContext ctx, Double origin) throws CommandException { @@ -41,11 +42,15 @@ public enum RelativeCoordinate implements IDatatypePost { boolean isRelative = !matcher.group(1).isEmpty(); - double offset = matcher.group(2).isEmpty() ? 0 : Double.parseDouble(matcher.group(2).replaceAll("k", "")); - - if (matcher.group(2).contains("k")) { + double offset = matcher.group(2).isEmpty() ? 0 : Double.parseDouble(matcher.group(2).replaceAll(ScalesAliasRegex, "")); + + if (matcher.group(2).toLowerCase().contains("k")) { offset *= 1000; } + if (matcher.group(2).toLowerCase().contains("m")) { + offset *= 1000000; + } + if (isRelative) { return origin + offset; diff --git a/src/api/java/baritone/api/command/registry/Registry.java b/src/api/java/baritone/api/command/registry/Registry.java index 06779169..b571484b 100644 --- a/src/api/java/baritone/api/command/registry/Registry.java +++ b/src/api/java/baritone/api/command/registry/Registry.java @@ -84,7 +84,7 @@ public class Registry { * @param entry The entry to unregister. */ public void unregister(V entry) { - if (registered(entry)) { + if (!registered(entry)) { return; } _entries.remove(entry); diff --git a/src/api/java/baritone/api/process/IBuilderProcess.java b/src/api/java/baritone/api/process/IBuilderProcess.java index 9063b990..c63113cd 100644 --- a/src/api/java/baritone/api/process/IBuilderProcess.java +++ b/src/api/java/baritone/api/process/IBuilderProcess.java @@ -58,6 +58,8 @@ public interface IBuilderProcess extends IBaritoneProcess { void buildOpenSchematic(); + void buildOpenLitematic(int i); + void pause(); boolean isPaused(); diff --git a/src/api/java/baritone/api/utils/SettingsUtil.java b/src/api/java/baritone/api/utils/SettingsUtil.java index 0e95965f..8b4b90b1 100644 --- a/src/api/java/baritone/api/utils/SettingsUtil.java +++ b/src/api/java/baritone/api/utils/SettingsUtil.java @@ -176,7 +176,7 @@ public class SettingsUtil { /** * This should always be the same as whether the setting can be parsed from or serialized to a string * - * @param the setting + * @param setting The Setting * @return true if the setting can not be set or read by the user */ public static boolean javaOnlySetting(Settings.Setting setting) { diff --git a/src/launch/java/baritone/launch/mixins/MixinNBTTagLongArray.java b/src/launch/java/baritone/launch/mixins/MixinNBTTagLongArray.java new file mode 100644 index 00000000..2c05e544 --- /dev/null +++ b/src/launch/java/baritone/launch/mixins/MixinNBTTagLongArray.java @@ -0,0 +1,35 @@ +/* + * 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 . + */ + +package baritone.launch.mixins; + +import baritone.utils.accessor.INBTTagLongArray; +import net.minecraft.nbt.NBTTagLongArray; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.gen.Accessor; + +/** + * @author rycbar + * @since 26.09.2022 + */ +@Mixin(NBTTagLongArray.class) +public abstract class MixinNBTTagLongArray implements INBTTagLongArray { + + @Accessor("data") + @Override + public abstract long[] getLongArray(); +} \ No newline at end of file diff --git a/src/launch/resources/mixins.baritone.json b/src/launch/resources/mixins.baritone.json index fdcd14b9..98273663 100644 --- a/src/launch/resources/mixins.baritone.json +++ b/src/launch/resources/mixins.baritone.json @@ -23,6 +23,7 @@ "MixinItemStack", "MixinItemTool", "MixinMinecraft", + "MixinNBTTagLongArray", "MixinNetHandlerPlayClient", "MixinNetworkManager", "MixinPlayerControllerMP", diff --git a/src/main/java/baritone/behavior/PathingBehavior.java b/src/main/java/baritone/behavior/PathingBehavior.java index f9c56c5a..33ef14ef 100644 --- a/src/main/java/baritone/behavior/PathingBehavior.java +++ b/src/main/java/baritone/behavior/PathingBehavior.java @@ -99,6 +99,7 @@ public final class PathingBehavior extends Behavior implements IPathingBehavior, baritone.getPathingControlManager().cancelEverything(); return; } + expectedSegmentStart = pathStart(); baritone.getPathingControlManager().preTick(); tickPath(); diff --git a/src/main/java/baritone/command/defaults/DefaultCommands.java b/src/main/java/baritone/command/defaults/DefaultCommands.java index e998dcc9..901fda71 100644 --- a/src/main/java/baritone/command/defaults/DefaultCommands.java +++ b/src/main/java/baritone/command/defaults/DefaultCommands.java @@ -43,6 +43,7 @@ public final class DefaultCommands { new RepackCommand(baritone), new BuildCommand(baritone), new SchematicaCommand(baritone), + new LitematicaCommand(baritone), new ComeCommand(baritone), new AxisCommand(baritone), new ForceCancelCommand(baritone), diff --git a/src/main/java/baritone/command/defaults/ExecutionControlCommands.java b/src/main/java/baritone/command/defaults/ExecutionControlCommands.java index eaab7528..6f6293cc 100644 --- a/src/main/java/baritone/command/defaults/ExecutionControlCommands.java +++ b/src/main/java/baritone/command/defaults/ExecutionControlCommands.java @@ -56,6 +56,7 @@ public class ExecutionControlCommands { @Override public PathingCommand onTick(boolean calcFailed, boolean isSafeToCancel) { + baritone.getInputOverrideHandler().clearAllKeys(); return new PathingCommand(null, PathingCommandType.REQUEST_PAUSE); } diff --git a/src/main/java/baritone/command/defaults/LitematicaCommand.java b/src/main/java/baritone/command/defaults/LitematicaCommand.java new file mode 100644 index 00000000..bfe0079b --- /dev/null +++ b/src/main/java/baritone/command/defaults/LitematicaCommand.java @@ -0,0 +1,71 @@ +/* + * 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 . + */ + +package baritone.command.defaults; + +import baritone.api.IBaritone; +import baritone.api.command.Command; +import baritone.api.command.argument.IArgConsumer; +import baritone.api.command.exception.CommandException; + +import java.util.Arrays; +import java.util.List; +import java.util.stream.Stream; + +public class LitematicaCommand extends Command { + + public LitematicaCommand(IBaritone baritone) { + super(baritone, "litematica"); + } + + @Override + public void execute(String label, IArgConsumer args) throws CommandException { + int schematic = 0; + if (args.hasAny()) { + args.requireMax(1); + if (args.is(Integer.class)) { + schematic = args.getAs(Integer.class) - 1; + } + } + try { + baritone.getBuilderProcess().buildOpenLitematic(schematic); + } catch (IndexOutOfBoundsException e) { + logDirect("Pleas provide a valid index."); + } + } + + @Override + public Stream tabComplete(String label, IArgConsumer args) { + return Stream.empty(); + } + + @Override + public String getShortDesc() { + return "Builds the loaded schematic"; + } + + @Override + public List getLongDesc() { + return Arrays.asList( + "Build a schematic currently open in Litematica.", + "", + "Usage:", + "> litematica", + "> litematica <#>" + ); + } +} \ No newline at end of file diff --git a/src/main/java/baritone/command/defaults/SetCommand.java b/src/main/java/baritone/command/defaults/SetCommand.java index e9e35d41..fd9bb045 100644 --- a/src/main/java/baritone/command/defaults/SetCommand.java +++ b/src/main/java/baritone/command/defaults/SetCommand.java @@ -147,7 +147,8 @@ public class SetCommand extends Command { throw new CommandInvalidTypeException(args.consumed(), "a toggleable setting", "some other setting"); } //noinspection unchecked - ((Settings.Setting) setting).value ^= true; + Settings.Setting asBoolSetting = (Settings.Setting) setting; + asBoolSetting.value ^= true; logDirect(String.format( "Toggled setting %s to %s", setting.getName(), diff --git a/src/main/java/baritone/pathing/movement/CalculationContext.java b/src/main/java/baritone/pathing/movement/CalculationContext.java index 0b4bfc87..177435ef 100644 --- a/src/main/java/baritone/pathing/movement/CalculationContext.java +++ b/src/main/java/baritone/pathing/movement/CalculationContext.java @@ -21,6 +21,7 @@ import baritone.Baritone; import baritone.api.IBaritone; import baritone.api.pathing.movement.ActionCosts; import baritone.cache.WorldData; +import baritone.pathing.precompute.PrecomputedData; import baritone.utils.BlockStateInterface; import baritone.utils.ToolSet; import baritone.utils.pathing.BetterWorldBorder; @@ -78,11 +79,14 @@ public class CalculationContext { public final double walkOnWaterOnePenalty; public final BetterWorldBorder worldBorder; + public final PrecomputedData precomputedData; + public CalculationContext(IBaritone baritone) { this(baritone, false); } public CalculationContext(IBaritone baritone, boolean forUseOnAnotherThread) { + this.precomputedData = new PrecomputedData(); this.safeForThreadedUse = forUseOnAnotherThread; this.baritone = baritone; EntityPlayerSP player = baritone.getPlayerContext().player(); diff --git a/src/main/java/baritone/pathing/movement/MovementHelper.java b/src/main/java/baritone/pathing/movement/MovementHelper.java index b8c4c034..18123201 100644 --- a/src/main/java/baritone/pathing/movement/MovementHelper.java +++ b/src/main/java/baritone/pathing/movement/MovementHelper.java @@ -25,6 +25,7 @@ import baritone.api.pathing.movement.MovementStatus; import baritone.api.utils.*; import baritone.api.utils.input.Input; import baritone.pathing.movement.MovementState.MovementTarget; +import baritone.pathing.precompute.Ternary; import baritone.utils.BlockStateInterface; import baritone.utils.ToolSet; import net.minecraft.block.*; @@ -41,6 +42,7 @@ import net.minecraft.world.IBlockAccess; import java.util.Optional; import static baritone.pathing.movement.Movement.HORIZONTALS_BUT_ALSO_DOWN_____SO_EVERY_DIRECTION_EXCEPT_UP; +import static baritone.pathing.precompute.Ternary.*; /** * Static helpers for cost calculation @@ -89,29 +91,81 @@ public interface MovementHelper extends ActionCosts, Helper { return canWalkThrough(bsi, x, y, z, bsi.get0(x, y, z)); } + static boolean canWalkThrough(CalculationContext context, int x, int y, int z, IBlockState state) { + return context.precomputedData.canWalkThrough(context.bsi, x, y, z, state); + } + + static boolean canWalkThrough(CalculationContext context, int x, int y, int z) { + return context.precomputedData.canWalkThrough(context.bsi, x, y, z, context.get(x, y, z)); + } + static boolean canWalkThrough(BlockStateInterface bsi, int x, int y, int z, IBlockState state) { - Block block = state.getBlock(); - if (block == Blocks.AIR) { // early return for most common case + Ternary canWalkThrough = canWalkThroughBlockState(state); + if (canWalkThrough == YES) { return true; } - if (block == Blocks.FIRE || block == Blocks.TRIPWIRE || block == Blocks.WEB || block == Blocks.END_PORTAL || block == Blocks.COCOA || block instanceof BlockSkull || block instanceof BlockTrapDoor || block == Blocks.END_ROD) { + if (canWalkThrough == NO) { return false; } + return canWalkThroughPosition(bsi, x, y, z, state); + } + + static Ternary canWalkThroughBlockState(IBlockState state) { + Block block = state.getBlock(); + if (block == Blocks.AIR) { + return YES; + } + if (block == Blocks.FIRE || block == Blocks.TRIPWIRE || block == Blocks.WEB || block == Blocks.END_PORTAL || block == Blocks.COCOA || block instanceof BlockSkull || block instanceof BlockTrapDoor || block == Blocks.END_ROD) { + return NO; + } if (Baritone.settings().blocksToAvoid.value.contains(block)) { - return false; + return NO; } if (block instanceof BlockDoor || block instanceof BlockFenceGate) { - // Because there's no nice method in vanilla to check if a door is openable or not, we just have to assume - // that anything that isn't an iron door isn't openable, ignoring that some doors introduced in mods can't - // be opened by just interacting. - return block != Blocks.IRON_DOOR; + // TODO this assumes that all doors in all mods are openable + if (block == Blocks.IRON_DOOR) { + return NO; + } + return YES; } + if (block == Blocks.CARPET) { + return MAYBE; + } + if (block instanceof BlockSnow) { + // snow layers cached as the top layer of a packed chunk have no metadata, we can't make a decision based on their depth here + // it would otherwise make long distance pathing through snowy biomes impossible + return MAYBE; + } + if (block instanceof BlockLiquid) { + if (state.getValue(BlockLiquid.LEVEL) != 0) { + return NO; + } else { + return MAYBE; + } + } + if (block instanceof BlockCauldron) { + return NO; + } + try { // A dodgy catch-all at the end, for most blocks with default behaviour this will work, however where blocks are special this will error out, and we can handle it when we have this information + if (block.isPassable(null, null)) { + return YES; + } else { + return NO; + } + } catch (Throwable exception) { + System.out.println("The block " + state.getBlock().getLocalizedName() + " requires a special case due to the exception " + exception.getMessage()); + return MAYBE; + } + } + + static boolean canWalkThroughPosition(BlockStateInterface bsi, int x, int y, int z, IBlockState state) { + Block block = state.getBlock(); + if (block == Blocks.CARPET) { return canWalkOn(bsi, x, y - 1, z); } + if (block instanceof BlockSnow) { - // we've already checked doors and fence gates - // so the only remaining dynamic isPassables are snow and trapdoor // if they're cached as a top block, we don't know their metadata // default to true (mostly because it would otherwise make long distance pathing through snowy biomes impossible) if (!bsi.worldContainsLoadedChunk(x, z)) { @@ -125,25 +179,27 @@ public interface MovementHelper extends ActionCosts, Helper { // ok, it's low enough we could walk through it, but is it supported? return canWalkOn(bsi, x, y - 1, z); } - if (isFlowing(x, y, z, state, bsi)) { - return false; // Don't walk through flowing liquids - } + if (block instanceof BlockLiquid) { + if (isFlowing(x, y, z, state, bsi)) { + return false; + } + // Everything after this point has to be a special case as it relies on the water not being flowing, which means a special case is needed. if (Baritone.settings().assumeWalkOnWater.value) { return false; } + IBlockState up = bsi.get0(x, y + 1, z); if (up.getBlock() instanceof BlockLiquid || up.getBlock() instanceof BlockLilyPad) { return false; } return block == Blocks.WATER || block == Blocks.FLOWING_WATER; } - if (block instanceof BlockCauldron) { - return false; - } + return block.isPassable(bsi.access, bsi.isPassableBlockPos.setPos(x, y, z)); } + /** * 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) @@ -285,6 +341,8 @@ public interface MovementHelper extends ActionCosts, Helper { * Can I walk on this block without anything weird happening like me falling * through? Includes water because we know that we automatically jump on * water + *

+ * If changing something in this function remember to also change it in precomputed data * * @param bsi Block state provider * @param x The block's x position @@ -294,32 +352,67 @@ public interface MovementHelper extends ActionCosts, Helper { * @return Whether or not the specified block can be walked on */ static boolean canWalkOn(BlockStateInterface bsi, int x, int y, int z, IBlockState state) { - Block block = state.getBlock(); - if (block == Blocks.AIR || block == Blocks.MAGMA) { - // early return for most common case (air) - // plus magma, which is a normal cube but it hurts you + Ternary canWalkOn = canWalkOnBlockState(state); + if (canWalkOn == YES) { + return true; + } + if (canWalkOn == NO) { return false; } - if (state.isBlockNormalCube()) { - return true; + return canWalkOnPosition(bsi, x, y, z, state); + } + + static Ternary canWalkOnBlockState(IBlockState state) { + Block block = state.getBlock(); + if (state.isBlockNormalCube() && block != Blocks.MAGMA) { + return YES; } if (block == Blocks.LADDER || (block == Blocks.VINE && Baritone.settings().allowVines.value)) { // TODO reconsider this - return true; + return YES; } if (block == Blocks.FARMLAND || block == Blocks.GRASS_PATH) { - return true; + return YES; } if (block == Blocks.ENDER_CHEST || block == Blocks.CHEST || block == Blocks.TRAPPED_CHEST) { - return true; + return YES; + } + if (block == Blocks.GLASS || block == Blocks.STAINED_GLASS) { + return YES; + } + if (block instanceof BlockStairs) { + return YES; } + if (isWater(block)) { + return MAYBE; + } + if (MovementHelper.isLava(block) && Baritone.settings().assumeWalkOnLava.value) { + return MAYBE; + } + if (block instanceof BlockSlab) { + if (!Baritone.settings().allowWalkOnBottomSlab.value) { + if (((BlockSlab) block).isDouble()) { + return YES; + } + if (state.getValue(BlockSlab.HALF) != BlockSlab.EnumBlockHalf.BOTTOM) { + return YES; + } + return NO; + } + return YES; + } + return NO; + } + + static boolean canWalkOnPosition(BlockStateInterface bsi, int x, int y, int z, IBlockState state) { + Block block = state.getBlock(); if (isWater(block)) { // since this is called literally millions of times per second, the benefit of not allocating millions of useless "pos.up()" - // BlockPos s that we'd just garbage collect immediately is actually noticeable. I don't even think its a decrease in readability + // BlockPos s that we'd just garbage collect immediately is actually noticeable. I don't even think it's a decrease in readability Block up = bsi.get0(x, y + 1, z).getBlock(); if (up == Blocks.WATERLILY || up == Blocks.CARPET) { return true; } - if (isFlowing(x, y, z, state, bsi) || block == Blocks.FLOWING_WATER) { + if (MovementHelper.isFlowing(x, y, z, state, bsi) || block == Blocks.FLOWING_WATER) { // the only scenario in which we can walk on flowing water is if it's under still water with jesus off return isWater(up) && !Baritone.settings().assumeWalkOnWater.value; } @@ -327,22 +420,20 @@ public interface MovementHelper extends ActionCosts, Helper { // if assumeWalkOnWater is off, we can only walk on water if there is water above it return isWater(up) ^ Baritone.settings().assumeWalkOnWater.value; } - if (Baritone.settings().assumeWalkOnLava.value && isLava(block) && !isFlowing(x, y, z, state, bsi)) { + + if (MovementHelper.isLava(block) && !MovementHelper.isFlowing(x, y, z, state, bsi) && Baritone.settings().assumeWalkOnLava.value) { // if we get here it means that assumeWalkOnLava must be true, so put it last return true; } - if (block == Blocks.GLASS || block == Blocks.STAINED_GLASS) { - return true; - } - if (block instanceof BlockSlab) { - if (!Baritone.settings().allowWalkOnBottomSlab.value) { - if (((BlockSlab) block).isDouble()) { - return true; - } - return state.getValue(BlockSlab.HALF) != BlockSlab.EnumBlockHalf.BOTTOM; - } - return true; - } - return block instanceof BlockStairs; + + return false; // If we don't recognise it then we want to just return false to be safe. + } + + static boolean canWalkOn(CalculationContext context, int x, int y, int z, IBlockState state) { + return context.precomputedData.canWalkOn(context.bsi, x, y, z, state); + } + + static boolean canWalkOn(CalculationContext context, int x, int y, int z) { + return canWalkOn(context, x, y, z, context.get(x, y, z)); } static boolean canWalkOn(IPlayerContext ctx, BetterBlockPos pos, IBlockState state) { @@ -422,7 +513,7 @@ public interface MovementHelper extends ActionCosts, Helper { static double getMiningDurationTicks(CalculationContext context, int x, int y, int z, IBlockState state, boolean includeFalling) { Block block = state.getBlock(); - if (!canWalkThrough(context.bsi, x, y, z, state)) { + if (!canWalkThrough(context, x, y, z, state)) { if (block instanceof BlockLiquid) { return COST_INF; } diff --git a/src/main/java/baritone/pathing/movement/movements/MovementAscend.java b/src/main/java/baritone/pathing/movement/movements/MovementAscend.java index 161ffdac..3d6a6cb3 100644 --- a/src/main/java/baritone/pathing/movement/movements/MovementAscend.java +++ b/src/main/java/baritone/pathing/movement/movements/MovementAscend.java @@ -68,7 +68,7 @@ public class MovementAscend extends Movement { public static double cost(CalculationContext context, int x, int y, int z, int destX, int destZ) { IBlockState toPlace = context.get(destX, y, destZ); double additionalPlacementCost = 0; - if (!MovementHelper.canWalkOn(context.bsi, destX, y, destZ, toPlace)) { + if (!MovementHelper.canWalkOn(context, destX, y, destZ, toPlace)) { additionalPlacementCost = context.costOfPlacingAt(destX, y, destZ, toPlace); if (additionalPlacementCost >= COST_INF) { return COST_INF; @@ -94,7 +94,7 @@ public class MovementAscend extends Movement { } } 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.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, 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 // 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 diff --git a/src/main/java/baritone/pathing/movement/movements/MovementDescend.java b/src/main/java/baritone/pathing/movement/movements/MovementDescend.java index 9ac00309..52f102b2 100644 --- a/src/main/java/baritone/pathing/movement/movements/MovementDescend.java +++ b/src/main/java/baritone/pathing/movement/movements/MovementDescend.java @@ -111,7 +111,7 @@ public class MovementDescend extends Movement { //C, D, etc determine the length of the fall IBlockState below = context.get(destX, y - 2, destZ); - if (!MovementHelper.canWalkOn(context.bsi, destX, y - 2, destZ, below)) { + if (!MovementHelper.canWalkOn(context, destX, y - 2, destZ, below)) { dynamicFallCost(context, x, y, z, destX, destZ, totalCost, below, res); return; } @@ -143,7 +143,7 @@ public class MovementDescend extends Movement { // and potentially replace the water we're going to fall into return false; } - if (!MovementHelper.canWalkThrough(context.bsi, destX, y - 2, destZ, below)) { + if (!MovementHelper.canWalkThrough(context, destX, y - 2, destZ, below)) { return false; } double costSoFar = 0; @@ -159,7 +159,7 @@ public class MovementDescend extends Movement { int unprotectedFallHeight = fallHeight - (y - effectiveStartHeight); // equal to fallHeight - y + effectiveFallHeight, which is equal to -newY + effectiveFallHeight, which is equal to effectiveFallHeight - newY double tentativeCost = WALK_OFF_BLOCK_COST + FALL_N_BLOCKS_COST[unprotectedFallHeight] + frontBreak + costSoFar; if (MovementHelper.isWater(ontoBlock.getBlock())) { - if (!MovementHelper.canWalkThrough(context.bsi, destX, newY, destZ, ontoBlock)) { + if (!MovementHelper.canWalkThrough(context, destX, newY, destZ, ontoBlock)) { return false; } if (context.assumeWalkOnWater) { @@ -168,7 +168,7 @@ public class MovementDescend extends Movement { if (MovementHelper.isFlowing(destX, newY, destZ, ontoBlock, context.bsi)) { return false; // TODO flowing check required here? } - if (!MovementHelper.canWalkOn(context.bsi, destX, newY - 1, destZ)) { + if (!MovementHelper.canWalkOn(context, destX, newY - 1, destZ)) { // we could punch right through the water into something else return false; } @@ -187,10 +187,10 @@ public class MovementDescend extends Movement { effectiveStartHeight = newY; continue; } - if (MovementHelper.canWalkThrough(context.bsi, destX, newY, destZ, ontoBlock)) { + if (MovementHelper.canWalkThrough(context, destX, newY, destZ, ontoBlock)) { continue; } - if (!MovementHelper.canWalkOn(context.bsi, destX, newY, destZ, ontoBlock)) { + if (!MovementHelper.canWalkOn(context, destX, newY, destZ, ontoBlock)) { return false; } if (MovementHelper.isBottomSlab(ontoBlock)) { diff --git a/src/main/java/baritone/pathing/movement/movements/MovementDiagonal.java b/src/main/java/baritone/pathing/movement/movements/MovementDiagonal.java index ba8893a3..0a11b255 100644 --- a/src/main/java/baritone/pathing/movement/movements/MovementDiagonal.java +++ b/src/main/java/baritone/pathing/movement/movements/MovementDiagonal.java @@ -61,7 +61,7 @@ public class MovementDiagonal extends Movement { @Override protected boolean safeToCancel(MovementState state) { //too simple. backfill does not work after cornering with this - //return MovementHelper.canWalkOn(ctx, ctx.playerFeet().down()); + //return context.precomputedData.canWalkOn(ctx, ctx.playerFeet().down()); EntityPlayerSP player = ctx.player(); double offset = 0.25; double x = player.posX; @@ -111,7 +111,7 @@ public class MovementDiagonal extends Movement { } public static void cost(CalculationContext context, int x, int y, int z, int destX, int destZ, MutableMoveResult res) { - if (!MovementHelper.canWalkThrough(context.bsi, destX, y + 1, destZ)) { + if (!MovementHelper.canWalkThrough(context, destX, y + 1, destZ)) { return; } IBlockState destInto = context.get(destX, y, destZ); @@ -119,9 +119,9 @@ public class MovementDiagonal extends Movement { IBlockState destWalkOn; boolean descend = false; boolean frostWalker = false; - if (!MovementHelper.canWalkThrough(context.bsi, destX, y, destZ, destInto)) { + if (!MovementHelper.canWalkThrough(context, destX, y, destZ, destInto)) { ascend = true; - if (!context.allowDiagonalAscend || !MovementHelper.canWalkThrough(context.bsi, x, y + 2, z) || !MovementHelper.canWalkOn(context.bsi, destX, y, destZ, destInto) || !MovementHelper.canWalkThrough(context.bsi, destX, y + 2, destZ)) { + if (!context.allowDiagonalAscend || !MovementHelper.canWalkThrough(context, x, y + 2, z) || !MovementHelper.canWalkOn(context, destX, y, destZ, destInto) || !MovementHelper.canWalkThrough(context, destX, y + 2, destZ)) { return; } destWalkOn = destInto; @@ -129,9 +129,9 @@ public class MovementDiagonal extends Movement { destWalkOn = context.get(destX, y - 1, destZ); boolean standingOnABlock = MovementHelper.mustBeSolidToWalkOn(context, x, y-1, z, context.get(x, y-1, z)); frostWalker = standingOnABlock && MovementHelper.canUseFrostWalker(context, destWalkOn); - if (!MovementHelper.canWalkOn(context.bsi, destX, y - 1, destZ, destWalkOn) && !frostWalker) { + if (!MovementHelper.canWalkOn(context, destX, y - 1, destZ, destWalkOn) && !frostWalker) { descend = true; - if (!context.allowDiagonalDescend || !MovementHelper.canWalkOn(context.bsi, destX, y - 2, destZ) || !MovementHelper.canWalkThrough(context.bsi, destX, y - 1, destZ, destWalkOn)) { + if (!context.allowDiagonalDescend || !MovementHelper.canWalkOn(context, destX, y - 2, destZ) || !MovementHelper.canWalkThrough(context, destX, y - 1, destZ, destWalkOn)) { return; } } @@ -176,17 +176,17 @@ public class MovementDiagonal extends Movement { IBlockState pb0 = context.get(x, y, destZ); IBlockState pb2 = context.get(destX, y, z); if (ascend) { - boolean ATop = MovementHelper.canWalkThrough(context.bsi, x, y + 2, destZ); - boolean AMid = MovementHelper.canWalkThrough(context.bsi, x, y + 1, destZ); - boolean ALow = MovementHelper.canWalkThrough(context.bsi, x, y, destZ, pb0); - boolean BTop = MovementHelper.canWalkThrough(context.bsi, destX, y + 2, z); - boolean BMid = MovementHelper.canWalkThrough(context.bsi, destX, y + 1, z); - boolean BLow = MovementHelper.canWalkThrough(context.bsi, destX, y, z, pb2); + boolean ATop = MovementHelper.canWalkThrough(context, x, y + 2, destZ); + boolean AMid = MovementHelper.canWalkThrough(context, x, y + 1, destZ); + boolean ALow = MovementHelper.canWalkThrough(context, x, y, destZ, pb0); + boolean BTop = MovementHelper.canWalkThrough(context, destX, y + 2, z); + boolean BMid = MovementHelper.canWalkThrough(context, destX, y + 1, z); + boolean BLow = MovementHelper.canWalkThrough(context, destX, y, z, pb2); if ((!(ATop && AMid && ALow) && !(BTop && BMid && BLow)) // no option || MovementHelper.avoidWalkingInto(pb0.getBlock()) // bad || MovementHelper.avoidWalkingInto(pb2.getBlock()) // bad - || (ATop && AMid && MovementHelper.canWalkOn(context.bsi, x, y, destZ, pb0)) // we could just ascend - || (BTop && BMid && MovementHelper.canWalkOn(context.bsi, destX, y, z, pb2)) // we could just ascend + || (ATop && AMid && MovementHelper.canWalkOn(context, x, y, destZ, pb0)) // we could just ascend + || (BTop && BMid && MovementHelper.canWalkOn(context, destX, y, z, pb2)) // we could just ascend || (!ATop && AMid && ALow) // head bonk A || (!BTop && BMid && BLow)) { // head bonk B return; diff --git a/src/main/java/baritone/pathing/movement/movements/MovementDownward.java b/src/main/java/baritone/pathing/movement/movements/MovementDownward.java index da5e3f89..5a6ec5ce 100644 --- a/src/main/java/baritone/pathing/movement/movements/MovementDownward.java +++ b/src/main/java/baritone/pathing/movement/movements/MovementDownward.java @@ -59,7 +59,7 @@ public class MovementDownward extends Movement { if (!context.allowDownward) { return COST_INF; } - if (!MovementHelper.canWalkOn(context.bsi, x, y - 2, z)) { + if (!MovementHelper.canWalkOn(context, x, y - 2, z)) { return COST_INF; } IBlockState down = context.get(x, y - 1, z); diff --git a/src/main/java/baritone/pathing/movement/movements/MovementParkour.java b/src/main/java/baritone/pathing/movement/movements/MovementParkour.java index 6a94e47f..76b17f0f 100644 --- a/src/main/java/baritone/pathing/movement/movements/MovementParkour.java +++ b/src/main/java/baritone/pathing/movement/movements/MovementParkour.java @@ -75,7 +75,7 @@ public class MovementParkour extends Movement { 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) + if (MovementHelper.canWalkOn(context, 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; } @@ -130,7 +130,7 @@ public class MovementParkour extends Movement { // check for ascend landing position IBlockState destInto = context.bsi.get0(destX, y, destZ); if (!MovementHelper.fullyPassable(context.bsi.access, context.bsi.isPassableBlockPos.setPos(destX, y, destZ), destInto)) { - if (i <= 3 && context.allowParkourAscend && context.canSprint && MovementHelper.canWalkOn(context.bsi, destX, y, destZ, destInto) && checkOvershootSafety(context.bsi, destX + xDiff, y + 1, destZ + zDiff)) { + if (i <= 3 && context.allowParkourAscend && context.canSprint && MovementHelper.canWalkOn(context, destX, y, destZ, destInto) && checkOvershootSafety(context.bsi, destX + xDiff, y + 1, destZ + zDiff)) { res.x = destX; res.y = y + 1; res.z = destZ; @@ -144,7 +144,7 @@ public class MovementParkour extends Movement { IBlockState landingOn = context.bsi.get0(destX, y - 1, destZ); // farmland needs to be canWalkOn otherwise farm can never work at all, but we want to specifically disallow ending a jump on farmland haha // frostwalker works here because we can't jump from possibly unfrozen water - if ((landingOn.getBlock() != Blocks.FARMLAND && MovementHelper.canWalkOn(context.bsi, destX, y - 1, destZ, landingOn)) + if ((landingOn.getBlock() != Blocks.FARMLAND && MovementHelper.canWalkOn(context, destX, y - 1, destZ, landingOn)) || (Math.min(16, context.frostWalker + 2) >= i && MovementHelper.canUseFrostWalker(context, landingOn)) ) { if (checkOvershootSafety(context.bsi, destX + xDiff, y, destZ + zDiff)) { diff --git a/src/main/java/baritone/pathing/movement/movements/MovementPillar.java b/src/main/java/baritone/pathing/movement/movements/MovementPillar.java index 1a54c104..5d8d32a8 100644 --- a/src/main/java/baritone/pathing/movement/movements/MovementPillar.java +++ b/src/main/java/baritone/pathing/movement/movements/MovementPillar.java @@ -124,7 +124,7 @@ public class MovementPillar extends Movement { } } // this is commented because it may have had a purpose, but it's very unclear what it was. it's from the minebot era. - //if (!MovementHelper.canWalkOn(chkPos, check) || MovementHelper.canWalkThrough(chkPos, check)) {//if the block above where we want to break is not a full block, don't do it + //if (!MovementHelper.canWalkOn(context, chkPos, check) || MovementHelper.canWalkThrough(context, chkPos, check)) {//if the block above where we want to break is not a full block, don't do it // TODO why does canWalkThrough mean this action is COST_INF? // BlockFalling makes sense, and !canWalkOn deals with weird cases like if it were lava // but I don't understand why canWalkThrough makes it impossible diff --git a/src/main/java/baritone/pathing/movement/movements/MovementTraverse.java b/src/main/java/baritone/pathing/movement/movements/MovementTraverse.java index 1d6dd871..43386c4b 100644 --- a/src/main/java/baritone/pathing/movement/movements/MovementTraverse.java +++ b/src/main/java/baritone/pathing/movement/movements/MovementTraverse.java @@ -75,7 +75,7 @@ public class MovementTraverse extends Movement { Block srcDown = context.getBlock(x, y - 1, z); boolean standingOnABlock = MovementHelper.mustBeSolidToWalkOn(context, x, y-1, z, context.get(x, y-1, z)); boolean frostWalker = standingOnABlock && !context.assumeWalkOnWater && MovementHelper.canUseFrostWalker(context, destOn); - if (MovementHelper.canWalkOn(context.bsi, destX, y - 1, destZ, destOn) || frostWalker) { //this is a walk, not a bridge + if (MovementHelper.canWalkOn(context, destX, y - 1, destZ, destOn) || frostWalker) { //this is a walk, not a bridge double WC = WALK_ONE_BLOCK_COST; boolean water = false; if (MovementHelper.isWater(pb0.getBlock()) || MovementHelper.isWater(pb1.getBlock())) { diff --git a/src/main/java/baritone/pathing/path/PathExecutor.java b/src/main/java/baritone/pathing/path/PathExecutor.java index 01b849c8..38754851 100644 --- a/src/main/java/baritone/pathing/path/PathExecutor.java +++ b/src/main/java/baritone/pathing/path/PathExecutor.java @@ -350,7 +350,7 @@ public class PathExecutor implements IPathExecutor, Helper { behavior.baritone.getInputOverrideHandler().setInputForceState(Input.SPRINT, false); // first and foremost, if allowSprint is off, or if we don't have enough hunger, don't try and sprint - if (!new CalculationContext(behavior.baritone).canSprint) { + if (!new CalculationContext(behavior.baritone, false).canSprint) { return false; } IMovement current = path.movements().get(pathPosition); diff --git a/src/main/java/baritone/pathing/precompute/PrecomputedData.java b/src/main/java/baritone/pathing/precompute/PrecomputedData.java new file mode 100644 index 00000000..72eb575b --- /dev/null +++ b/src/main/java/baritone/pathing/precompute/PrecomputedData.java @@ -0,0 +1,92 @@ +/* + * 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 . + */ + +package baritone.pathing.precompute; + +import baritone.pathing.movement.MovementHelper; +import baritone.utils.BlockStateInterface; +import net.minecraft.block.Block; +import net.minecraft.block.state.IBlockState; + +import static baritone.pathing.precompute.Ternary.MAYBE; +import static baritone.pathing.precompute.Ternary.YES; + +public class PrecomputedData { // TODO add isFullyPassable + + private final int[] data = new int[Block.BLOCK_STATE_IDS.size()]; + + private static final int COMPLETED_MASK = 1 << 0; + private static final int CAN_WALK_ON_MASK = 1 << 1; + private static final int CAN_WALK_ON_SPECIAL_MASK = 1 << 2; + private static final int CAN_WALK_THROUGH_MASK = 1 << 3; + private static final int CAN_WALK_THROUGH_SPECIAL_MASK = 1 << 4; + + private int fillData(int id, IBlockState state) { + int blockData = 0; + + Ternary canWalkOnState = MovementHelper.canWalkOnBlockState(state); + if (canWalkOnState == YES) { + blockData |= CAN_WALK_ON_MASK; + } + if (canWalkOnState == MAYBE) { + blockData |= CAN_WALK_ON_SPECIAL_MASK; + } + + Ternary canWalkThroughState = MovementHelper.canWalkThroughBlockState(state); + if (canWalkThroughState == YES) { + blockData |= CAN_WALK_THROUGH_MASK; + } + if (canWalkOnState == MAYBE) { + blockData |= CAN_WALK_THROUGH_SPECIAL_MASK; + } + + blockData |= COMPLETED_MASK; + + data[id] = blockData; // in theory, this is thread "safe" because every thread should compute the exact same int to write? + return blockData; + } + + public boolean canWalkOn(BlockStateInterface bsi, int x, int y, int z, IBlockState state) { + int id = Block.BLOCK_STATE_IDS.get(state); + int blockData = data[id]; + + if ((blockData & COMPLETED_MASK) == 0) { // we need to fill in the data + blockData = fillData(id, state); + } + + if ((blockData & CAN_WALK_ON_SPECIAL_MASK) != 0) { + return MovementHelper.canWalkOnPosition(bsi, x, y, z, state); + } else { + return (blockData & CAN_WALK_ON_MASK) != 0; + } + } + + public boolean canWalkThrough(BlockStateInterface bsi, int x, int y, int z, IBlockState state) { + int id = Block.BLOCK_STATE_IDS.get(state); + int blockData = data[id]; + + if ((blockData & COMPLETED_MASK) == 0) { // we need to fill in the data + blockData = fillData(id, state); + } + + if ((blockData & CAN_WALK_THROUGH_SPECIAL_MASK) != 0) { + return MovementHelper.canWalkThroughPosition(bsi, x, y, z, state); + } else { + return (blockData & CAN_WALK_THROUGH_MASK) != 0; + } + } +} diff --git a/src/main/java/baritone/pathing/precompute/Ternary.java b/src/main/java/baritone/pathing/precompute/Ternary.java new file mode 100644 index 00000000..d4d8424e --- /dev/null +++ b/src/main/java/baritone/pathing/precompute/Ternary.java @@ -0,0 +1,22 @@ +/* + * 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 . + */ + +package baritone.pathing.precompute; + +public enum Ternary { + YES, MAYBE, NO +} diff --git a/src/main/java/baritone/process/BackfillProcess.java b/src/main/java/baritone/process/BackfillProcess.java index 04b3ca78..f42dfbe5 100644 --- a/src/main/java/baritone/process/BackfillProcess.java +++ b/src/main/java/baritone/process/BackfillProcess.java @@ -56,12 +56,12 @@ public final class BackfillProcess extends BaritoneProcessHelper { Baritone.settings().backfill.value = false; return false; } - amIBreakingABlockHMMMMMMM(); for (BlockPos pos : new ArrayList<>(blocksToReplace.keySet())) { - if (ctx.world().getChunk(pos) instanceof EmptyChunk) { + if (ctx.world().getChunk(pos) instanceof EmptyChunk || ctx.world().getBlockState(pos).getBlock() != Blocks.AIR) { blocksToReplace.remove(pos); } } + amIBreakingABlockHMMMMMMM(); baritone.getInputOverrideHandler().clearAllKeys(); return !toFillIn().isEmpty(); @@ -93,7 +93,7 @@ public final class BackfillProcess extends BaritoneProcessHelper { } private void amIBreakingABlockHMMMMMMM() { - if (!ctx.getSelectedBlock().isPresent()) { + if (!ctx.getSelectedBlock().isPresent() || !baritone.getPathingBehavior().isPathing()) { return; } blocksToReplace.put(ctx.getSelectedBlock().get(), ctx.world().getBlockState(ctx.getSelectedBlock().get())); diff --git a/src/main/java/baritone/process/BuilderProcess.java b/src/main/java/baritone/process/BuilderProcess.java index 1766af62..ccadd551 100644 --- a/src/main/java/baritone/process/BuilderProcess.java +++ b/src/main/java/baritone/process/BuilderProcess.java @@ -26,9 +26,9 @@ import baritone.api.process.IBuilderProcess; import baritone.api.process.PathingCommand; import baritone.api.process.PathingCommandType; import baritone.api.schematic.FillSchematic; -import baritone.api.schematic.SubstituteSchematic; import baritone.api.schematic.ISchematic; import baritone.api.schematic.IStaticSchematic; +import baritone.api.schematic.SubstituteSchematic; import baritone.api.schematic.format.ISchematicFormat; import baritone.api.utils.BetterBlockPos; import baritone.api.utils.RayTraceUtils; @@ -42,8 +42,10 @@ import baritone.utils.BaritoneProcessHelper; import baritone.utils.BlockStateInterface; import baritone.utils.PathingCommandContext; import baritone.utils.schematic.MapArtSchematic; -import baritone.utils.schematic.SelectionSchematic; import baritone.utils.schematic.SchematicSystem; +import baritone.utils.schematic.SelectionSchematic; +import baritone.utils.schematic.format.defaults.LitematicaSchematic; +import baritone.utils.schematic.litematica.LitematicaHelper; import baritone.utils.schematic.schematica.SchematicaHelper; import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableSet; @@ -54,12 +56,15 @@ import net.minecraft.block.state.IBlockState; import net.minecraft.init.Blocks; import net.minecraft.item.ItemBlock; import net.minecraft.item.ItemStack; +import net.minecraft.nbt.CompressedStreamTools; import net.minecraft.util.EnumFacing; import net.minecraft.util.Tuple; import net.minecraft.util.math.*; import java.io.File; import java.io.FileInputStream; +import java.io.IOException; +import java.nio.file.Files; import java.util.*; import java.util.stream.Collectors; @@ -176,6 +181,33 @@ public final class BuilderProcess extends BaritoneProcessHelper implements IBuil } } + /** + * Builds the with index 'i' given schematic placement. + * + * @param i index reference to the schematic placement list. + */ + @Override + public void buildOpenLitematic(int i) { + if (LitematicaHelper.isLitematicaPresent()) { + //if java.lang.NoSuchMethodError is thrown see comment in SchematicPlacementManager + if (LitematicaHelper.hasLoadedSchematic()) { + String name = LitematicaHelper.getName(i); + try { + LitematicaSchematic schematic1 = new LitematicaSchematic(CompressedStreamTools.readCompressed(Files.newInputStream(LitematicaHelper.getSchematicFile(i).toPath())), false); + Vec3i correctedOrigin = LitematicaHelper.getCorrectedOrigin(schematic1, i); + LitematicaSchematic schematic2 = LitematicaHelper.blackMagicFuckery(schematic1, i); + build(name, schematic2, correctedOrigin); + } catch (IOException e) { + logDirect("Schematic File could not be loaded."); + } + } else { + logDirect("No schematic currently loaded"); + } + } else { + logDirect("Litematica is not present"); + } + } + public void clearArea(BlockPos corner1, BlockPos corner2) { BlockPos origin = new BlockPos(Math.min(corner1.getX(), corner2.getX()), Math.min(corner1.getY(), corner2.getY()), Math.min(corner1.getZ(), corner2.getZ())); int widthX = Math.abs(corner1.getX() - corner2.getX()) + 1; @@ -606,7 +638,7 @@ public final class BuilderProcess extends BaritoneProcessHelper implements IBuil } // this is not in render distance if (!observedCompleted.contains(BetterBlockPos.longHash(blockX, blockY, blockZ)) - && !Baritone.settings().buildSkipBlocks.value.contains(schematic.desiredState(x, y, z, current, this.approxPlaceable).getBlock())) { + && !Baritone.settings().buildSkipBlocks.value.contains(schematic.desiredState(x, y, z, current, this.approxPlaceable).getBlock())) { // and we've never seen this position be correct // therefore mark as incorrect incorrectPositions.add(new BetterBlockPos(blockX, blockY, blockZ)); @@ -842,14 +874,21 @@ public final class BuilderProcess extends BaritoneProcessHelper implements IBuil BlockTrapDoor.OPEN, BlockTrapDoor.HALF ); - private boolean sameWithoutOrientation(IBlockState first, IBlockState second) { + private boolean sameBlockstate(IBlockState first, IBlockState second) { if (first.getBlock() != second.getBlock()) { return false; } + boolean ignoreDirection = Baritone.settings().buildIgnoreDirection.value; + List ignoredProps = Baritone.settings().buildIgnoreProperties.value; + if (!ignoreDirection && ignoredProps.isEmpty()) { + return first.equals(second); // early return if no properties are being ignored + } ImmutableMap, Comparable> map1 = first.getProperties(); ImmutableMap, Comparable> map2 = second.getProperties(); for (IProperty prop : map1.keySet()) { - if (map1.get(prop) != map2.get(prop) && !orientationProps.contains(prop)) { + if (map1.get(prop) != map2.get(prop) + && !(ignoreDirection && orientationProps.contains(prop)) + && !ignoredProps.contains(prop.getName())) { return false; } } @@ -881,7 +920,7 @@ public final class BuilderProcess extends BaritoneProcessHelper implements IBuil if (current.equals(desired)) { return true; } - return Baritone.settings().buildIgnoreDirection.value && sameWithoutOrientation(current, desired); + return sameBlockstate(current, desired); } public class BuilderCalculationContext extends CalculationContext { diff --git a/src/main/java/baritone/process/MineProcess.java b/src/main/java/baritone/process/MineProcess.java index 1ec47cd9..e79cb815 100644 --- a/src/main/java/baritone/process/MineProcess.java +++ b/src/main/java/baritone/process/MineProcess.java @@ -28,6 +28,7 @@ import baritone.cache.CachedChunk; import baritone.cache.WorldScanner; import baritone.pathing.movement.CalculationContext; import baritone.pathing.movement.MovementHelper; +import baritone.pathing.precompute.PrecomputedData; import baritone.utils.BaritoneProcessHelper; import baritone.utils.BlockStateInterface; import net.minecraft.block.Block; @@ -102,11 +103,7 @@ public final class MineProcess extends BaritoneProcessHelper implements IMinePro return null; } } - if (!Baritone.settings().allowBreak.value) { - logDirect("Unable to mine when allowBreak is false!"); - cancel(); - return null; - } + updateLoucaSystem(); int mineGoalUpdateInterval = Baritone.settings().mineGoalUpdateInterval.value; List curr = new ArrayList<>(knownOreLocations); @@ -115,7 +112,10 @@ public final class MineProcess extends BaritoneProcessHelper implements IMinePro Baritone.getExecutor().execute(() -> rescan(curr, context)); } if (Baritone.settings().legitMine.value) { - addNearby(); + if (!addNearby()) { + cancel(); + return null; + } } Optional shaft = curr.stream() .filter(pos -> pos.getX() == ctx.playerFeet().getX() && pos.getZ() == ctx.playerFeet().getZ()) @@ -176,6 +176,11 @@ public final class MineProcess extends BaritoneProcessHelper implements IMinePro } private PathingCommand updateGoal() { + BlockOptionalMetaLookup filter = filterFilter(); + if (filter == null) { + return null; + } + boolean legit = Baritone.settings().legitMine.value; List locs = knownOreLocations; if (!locs.isEmpty()) { @@ -220,6 +225,7 @@ public final class MineProcess extends BaritoneProcessHelper implements IMinePro } private void rescan(List already, CalculationContext context) { + BlockOptionalMetaLookup filter = filterFilter(); if (filter == null) { return; } @@ -369,11 +375,18 @@ public final class MineProcess extends BaritoneProcessHelper implements IMinePro return prune(ctx, locs, filter, max, blacklist, dropped); } - private void addNearby() { + private boolean addNearby() { List dropped = droppedItemsScan(); knownOreLocations.addAll(dropped); BlockPos playerFeet = ctx.playerFeet(); BlockStateInterface bsi = new BlockStateInterface(ctx); + + + BlockOptionalMetaLookup filter = filterFilter(); + if (filter == null) { + return false; + } + int searchDist = 10; double fakedBlockReachDistance = 20; // at least 10 * sqrt(3) with some extra space to account for positioning within the block for (int x = playerFeet.getX() - searchDist; x <= playerFeet.getX() + searchDist; x++) { @@ -391,6 +404,7 @@ public final class MineProcess extends BaritoneProcessHelper implements IMinePro } } knownOreLocations = prune(new CalculationContext(baritone), knownOreLocations, filter, ORE_LOCATIONS_COUNT, blacklist, dropped); + return true; } private static List prune(CalculationContext ctx, List locs2, BlockOptionalMetaLookup filter, int max, List blacklist, List dropped) { @@ -466,10 +480,8 @@ public final class MineProcess extends BaritoneProcessHelper implements IMinePro @Override public void mine(int quantity, BlockOptionalMetaLookup filter) { this.filter = filter; - if (filter != null && !Baritone.settings().allowBreak.value) { - logDirect("Unable to mine when allowBreak is false!"); - this.mine(quantity, (BlockOptionalMetaLookup) null); - return; + if (this.filterFilter() == null) { + this.filter = null; } this.desiredQuantity = quantity; this.knownOreLocations = new ArrayList<>(); @@ -481,4 +493,22 @@ public final class MineProcess extends BaritoneProcessHelper implements IMinePro rescan(new ArrayList<>(), new CalculationContext(baritone)); } } + + private BlockOptionalMetaLookup filterFilter() { + if (this.filter == null) { + return null; + } + if (!Baritone.settings().allowBreak.value) { + BlockOptionalMetaLookup f = new BlockOptionalMetaLookup(this.filter.blocks() + .stream() + .filter(e -> Baritone.settings().allowBreakAnyway.value.contains(e.getBlock())) + .toArray(BlockOptionalMeta[]::new)); + if (f.blocks().isEmpty()) { + logDirect("Unable to mine when allowBreak is false and target block is not in allowBreakAnyway!"); + return null; + } + return f; + } + return filter; + } } diff --git a/src/main/java/baritone/utils/accessor/INBTTagLongArray.java b/src/main/java/baritone/utils/accessor/INBTTagLongArray.java new file mode 100644 index 00000000..fe4f0bd8 --- /dev/null +++ b/src/main/java/baritone/utils/accessor/INBTTagLongArray.java @@ -0,0 +1,27 @@ +/* + * 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 . + */ + +package baritone.utils.accessor; + +/** + * @author rycbar + * @since 26.09.2022 + */ +public interface INBTTagLongArray { + + long[] getLongArray(); +} \ No newline at end of file diff --git a/src/main/java/baritone/utils/schematic/format/DefaultSchematicFormats.java b/src/main/java/baritone/utils/schematic/format/DefaultSchematicFormats.java index fb20164b..cd38433a 100644 --- a/src/main/java/baritone/utils/schematic/format/DefaultSchematicFormats.java +++ b/src/main/java/baritone/utils/schematic/format/DefaultSchematicFormats.java @@ -19,6 +19,7 @@ package baritone.utils.schematic.format; import baritone.api.schematic.IStaticSchematic; import baritone.api.schematic.format.ISchematicFormat; +import baritone.utils.schematic.format.defaults.LitematicaSchematic; import baritone.utils.schematic.format.defaults.MCEditSchematic; import baritone.utils.schematic.format.defaults.SpongeSchematic; import net.minecraft.nbt.CompressedStreamTools; @@ -65,6 +66,26 @@ public enum DefaultSchematicFormats implements ISchematicFormat { throw new UnsupportedOperationException("Unsupported Version of a Sponge Schematic"); } } + }, + + /** + * The Litematica schematic specification. Commonly denoted by the ".litematic" file extension. + */ + LITEMATICA("litematic") { + @Override + public IStaticSchematic parse(InputStream input) throws IOException { + NBTTagCompound nbt = CompressedStreamTools.readCompressed(input); + int version = nbt.getInteger("Version"); + switch (version) { + case 4: //1.12 + return new LitematicaSchematic(nbt, false); + case 5: //1.13-1.17 + case 6: //1.18+ + throw new UnsupportedOperationException("This litematic Verion is too new."); + default: + throw new UnsupportedOperationException("Unsuported Version of a Litematica Schematic"); + } + } }; private final String extension; diff --git a/src/main/java/baritone/utils/schematic/format/defaults/LitematicaSchematic.java b/src/main/java/baritone/utils/schematic/format/defaults/LitematicaSchematic.java new file mode 100644 index 00000000..c2857bc2 --- /dev/null +++ b/src/main/java/baritone/utils/schematic/format/defaults/LitematicaSchematic.java @@ -0,0 +1,347 @@ +/* + * 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 . + */ + +package baritone.utils.schematic.format.defaults; + +import baritone.utils.accessor.INBTTagLongArray; +import baritone.utils.schematic.StaticSchematic; +import net.minecraft.block.Block; +import net.minecraft.block.properties.IProperty; +import net.minecraft.block.state.IBlockState; +import net.minecraft.nbt.NBTTagCompound; +import net.minecraft.nbt.NBTTagList; +import net.minecraft.util.ResourceLocation; +import net.minecraft.util.math.Vec3i; +import org.apache.commons.lang3.Validate; + +import javax.annotation.Nullable; +import java.util.Optional; + +/** + * Based on EmersonDove's work + * ... + * + * @author rycbar + * @since 22.09.2022 + */ +public final class LitematicaSchematic extends StaticSchematic { + private final Vec3i offsetMinCorner; + private final NBTTagCompound nbt; + + /** + * @param nbtTagCompound a decompressed file stream aka nbt data. + * @param rotated if the schematic is rotated by 90°. + */ + public LitematicaSchematic(NBTTagCompound nbtTagCompound, boolean rotated) { + this.nbt = nbtTagCompound; + this.offsetMinCorner = new Vec3i(getMinOfSchematic("x"), getMinOfSchematic("y"), getMinOfSchematic("z")); + this.y = Math.abs(nbt.getCompoundTag("Metadata").getCompoundTag("EnclosingSize").getInteger("y")); + + if (rotated) { + this.x = Math.abs(nbt.getCompoundTag("Metadata").getCompoundTag("EnclosingSize").getInteger("z")); + this.z = Math.abs(nbt.getCompoundTag("Metadata").getCompoundTag("EnclosingSize").getInteger("x")); + } else { + this.x = Math.abs(nbt.getCompoundTag("Metadata").getCompoundTag("EnclosingSize").getInteger("x")); + this.z = Math.abs(nbt.getCompoundTag("Metadata").getCompoundTag("EnclosingSize").getInteger("z")); + } + this.states = new IBlockState[this.x][this.z][this.y]; + fillInSchematic(); + } + + /** + * @return Array of subregion names. + */ + private static String[] getRegions(NBTTagCompound nbt) { + return nbt.getCompoundTag("Regions").getKeySet().toArray(new String[0]); + } + + /** + * Gets both ends from a region box for a given axis and returns the lower one. + * + * @param s axis that should be read. + * @return the lower coord of the requested axis. + */ + private static int getMinOfSubregion(NBTTagCompound nbt, String subReg, String s) { + int a = nbt.getCompoundTag("Regions").getCompoundTag(subReg).getCompoundTag("Position").getInteger(s); + int b = nbt.getCompoundTag("Regions").getCompoundTag(subReg).getCompoundTag("Size").getInteger(s); + if (b < 0) { + b++; + } + return Math.min(a, a + b); + + } + + /** + * @param blockStatePalette List of all different block types used in the schematic. + * @return Array of BlockStates. + */ + private static IBlockState[] getBlockList(NBTTagList blockStatePalette) { + IBlockState[] blockList = new IBlockState[blockStatePalette.tagCount()]; + + for (int i = 0; i < blockStatePalette.tagCount(); i++) { + Block block = Block.REGISTRY.getObject(new ResourceLocation((((NBTTagCompound) blockStatePalette.get(i)).getString("Name")))); + NBTTagCompound properties = ((NBTTagCompound) blockStatePalette.get(i)).getCompoundTag("Properties"); + + blockList[i] = getBlockState(block, properties); + } + return blockList; + } + + /** + * @param block block. + * @param properties List of Properties the block has. + * @return A blockState. + */ + private static IBlockState getBlockState(Block block, NBTTagCompound properties) { + IBlockState blockState = block.getDefaultState(); + + for (Object key : properties.getKeySet().toArray()) { + IProperty property = block.getBlockState().getProperty((String) key); + String propertyValue = properties.getString((String) key); + if (property != null) { + blockState = setPropertyValue(blockState, property, propertyValue); + } + } + return blockState; + } + + /** + * @author Emerson + */ + private static > IBlockState setPropertyValue(IBlockState state, IProperty property, String value) { + Optional parsed = property.parseValue(value).toJavaUtil(); + if (parsed.isPresent()) { + return state.withProperty(property, parsed.get()); + } else { + throw new IllegalArgumentException("Invalid value for property " + property); + } + } + + /** + * @param amountOfBlockTypes amount of block types in the schematic. + * @return amount of bits used to encode a block. + */ + private static int getBitsPerBlock(int amountOfBlockTypes) { + return (int) Math.max(2,Math.ceil(Math.log(amountOfBlockTypes) / Math.log(2))); + } + + /** + * Calculates the volume of the subregion. As size can be a negative value we take the absolute value of the + * multiplication as the volume still holds a positive amount of blocks. + * + * @return the volume of the subregion. + */ + private static long getVolume(NBTTagCompound nbt, String subReg) { + return Math.abs( + nbt.getCompoundTag("Regions").getCompoundTag(subReg).getCompoundTag("Size").getInteger("x") * + nbt.getCompoundTag("Regions").getCompoundTag(subReg).getCompoundTag("Size").getInteger("y") * + nbt.getCompoundTag("Regions").getCompoundTag(subReg).getCompoundTag("Size").getInteger("z")); + } + + /** + * @return array of Long values. + */ + private static long[] getBlockStates(NBTTagCompound nbt, String subReg) { + return ((INBTTagLongArray) nbt.getCompoundTag("Regions").getCompoundTag(subReg).getTag("BlockStates")).getLongArray(); + } + + /** + * Subregion don't have to be the same size as the enclosing size of the schematic. If they are smaller we check here if the current block is part of the subregion. + * + * @param x coord of the block relative to the minimum corner. + * @param y coord of the block relative to the minimum corner. + * @param z coord of the block relative to the minimum corner. + * @return if the current block is part of the subregion. + */ + private static boolean inSubregion(NBTTagCompound nbt, String subReg, int x, int y, int z) { + return x >= 0 && y >= 0 && z >= 0 && + x < Math.abs(nbt.getCompoundTag("Regions").getCompoundTag(subReg).getCompoundTag("Size").getInteger("x")) && + y < Math.abs(nbt.getCompoundTag("Regions").getCompoundTag(subReg).getCompoundTag("Size").getInteger("y")) && + z < Math.abs(nbt.getCompoundTag("Regions").getCompoundTag(subReg).getCompoundTag("Size").getInteger("z")); + } + + /** + * @param s axis. + * @return the lowest coordinate of that axis of the schematic. + */ + private int getMinOfSchematic(String s) { + int n = Integer.MAX_VALUE; + for (String subReg : getRegions(nbt)) { + n = Math.min(n, getMinOfSubregion(nbt, subReg, s)); + } + return n; + } + + /** + * reads the file data. + */ + private void fillInSchematic() { + for (String subReg : getRegions(nbt)) { + NBTTagList usedBlockTypes = nbt.getCompoundTag("Regions").getCompoundTag(subReg).getTagList("BlockStatePalette", 10); + IBlockState[] blockList = getBlockList(usedBlockTypes); + + int bitsPerBlock = getBitsPerBlock(usedBlockTypes.tagCount()); + long regionVolume = getVolume(nbt, subReg); + long[] blockStateArray = getBlockStates(nbt, subReg); + + LitematicaBitArray bitArray = new LitematicaBitArray(bitsPerBlock, regionVolume, blockStateArray); + + writeSubregionIntoSchematic(nbt, subReg, blockList, bitArray); + } + } + + /** + * Writes the file data in to the IBlockstate array. + * + * @param blockList list with the different block types used in the schematic. + * @param bitArray bit array that holds the placement pattern. + */ + private void writeSubregionIntoSchematic(NBTTagCompound nbt, String subReg, IBlockState[] blockList, LitematicaBitArray bitArray) { + Vec3i offsetSubregion = new Vec3i(getMinOfSubregion(nbt, subReg, "x"), getMinOfSubregion(nbt, subReg, "y"), getMinOfSubregion(nbt, subReg, "z")); + int index = 0; + for (int y = 0; y < this.y; y++) { + for (int z = 0; z < this.z; z++) { + for (int x = 0; x < this.x; x++) { + if (inSubregion(nbt, subReg, x, y, z)) { + this.states[x - (offsetMinCorner.getX() - offsetSubregion.getX())][z - (offsetMinCorner.getZ() - offsetSubregion.getZ())][y - (offsetMinCorner.getY() - offsetSubregion.getY())] = blockList[bitArray.getAt(index)]; + index++; + } + } + } + } + } + + /** + * @return offset from the schematic origin to the minimum Corner as a Vec3i. + */ + public Vec3i getOffsetMinCorner() { + return offsetMinCorner; + } + + /** + * @return x size of the schematic. + */ + public int getX() { + return this.x; + } + + /** + * @return y size of the schematic. + */ + public int getY() { + return this.y; + } + + /** + * @return z size of the schematic. + */ + public int getZ() { + return this.z; + } + + /** + * @param x position relative to the minimum corner of the schematic. + * @param y position relative to the minimum corner of the schematic. + * @param z position relative to the minimum corner of the schematic. + * @param blockState new blockstate of the block at this position. + */ + public void setDirect(int x, int y, int z, IBlockState blockState) { + this.states[x][z][y] = blockState; + } + + /** + * @param rotated if the schematic is rotated by 90°. + * @return a copy of the schematic. + */ + public LitematicaSchematic getCopy(boolean rotated) { + return new LitematicaSchematic(nbt, rotated); + } + + /** + * @author maruohon + * Class from the Litematica mod by maruohon + * Usage under LGPLv3 with the permission of the author. + * ... + */ + private static class LitematicaBitArray { + /** + * The long array that is used to store the data for this BitArray. + */ + private final long[] longArray; + /** + * Number of bits a single entry takes up + */ + private final int bitsPerEntry; + /** + * The maximum value for a single entry. This also works as a bitmask for a single entry. + * For instance, if bitsPerEntry were 5, this value would be 31 (ie, {@code 0b00011111}). + */ + private final long maxEntryValue; + /** + * Number of entries in this array (not the length of the long array that internally backs this array) + */ + private final long arraySize; + + public LitematicaBitArray(int bitsPerEntryIn, long arraySizeIn, @Nullable long[] longArrayIn) { + Validate.inclusiveBetween(1L, 32L, bitsPerEntryIn); + this.arraySize = arraySizeIn; + this.bitsPerEntry = bitsPerEntryIn; + this.maxEntryValue = (1L << bitsPerEntryIn) - 1L; + + if (longArrayIn != null) { + this.longArray = longArrayIn; + } else { + this.longArray = new long[(int) (roundUp(arraySizeIn * (long) bitsPerEntryIn, 64L) / 64L)]; + } + } + + public static long roundUp(long number, long interval) { + int sign = 1; + if (interval == 0) { + return 0; + } else if (number == 0) { + return interval; + } else { + if (number < 0) { + sign = -1; + } + + long i = number % (interval * sign); + return i == 0 ? number : number + (interval * sign) - i; + } + } + + public int getAt(long index) { + Validate.inclusiveBetween(0L, this.arraySize - 1L, index); + long startOffset = index * (long) this.bitsPerEntry; + int startArrIndex = (int) (startOffset >> 6); // startOffset / 64 + int endArrIndex = (int) (((index + 1L) * (long) this.bitsPerEntry - 1L) >> 6); + int startBitOffset = (int) (startOffset & 0x3F); // startOffset % 64 + + if (startArrIndex == endArrIndex) { + return (int) (this.longArray[startArrIndex] >>> startBitOffset & this.maxEntryValue); + } else { + int endOffset = 64 - startBitOffset; + return (int) ((this.longArray[startArrIndex] >>> startBitOffset | this.longArray[endArrIndex] << endOffset) & this.maxEntryValue); + } + } + + public long size() { + return this.arraySize; + } + } +} \ No newline at end of file diff --git a/src/main/java/baritone/utils/schematic/litematica/LitematicaHelper.java b/src/main/java/baritone/utils/schematic/litematica/LitematicaHelper.java new file mode 100644 index 00000000..ec9fcc73 --- /dev/null +++ b/src/main/java/baritone/utils/schematic/litematica/LitematicaHelper.java @@ -0,0 +1,215 @@ +/* + * 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 . + */ + +package baritone.utils.schematic.litematica; + +import baritone.utils.schematic.format.defaults.LitematicaSchematic; +import fi.dy.masa.litematica.Litematica; +import fi.dy.masa.litematica.data.DataManager; +import net.minecraft.block.state.IBlockState; +import net.minecraft.util.Mirror; +import net.minecraft.util.Rotation; +import net.minecraft.util.math.Vec3i; + +import java.io.File; + +/** + * Helper class that provides access or processes data related to Litmatica schematics. + * + * @author rycbar + * @since 28.09.2022 + */ +public final class LitematicaHelper { + + /** + * @return if Litmatica is installed. + */ + public static boolean isLitematicaPresent() { + try { + Class.forName(Litematica.class.getName()); + return true; + } catch (ClassNotFoundException | NoClassDefFoundError ex) { + return false; + } + } + + /** + * @return if there are loaded schematics. + */ + public static boolean hasLoadedSchematic() { + return DataManager.getSchematicPlacementManager().getAllSchematicsPlacements().size() > 0; + } + + /** + * @param i index of the Schematic in the schematic placement list. + * @return the name of the requested schematic. + */ + public static String getName(int i) { + return DataManager.getSchematicPlacementManager().getAllSchematicsPlacements().get(i).getName(); + } + + /** + * @param i index of the Schematic in the schematic placement list. + * @return the world coordinates of the schematic origin. This can but does not have to be the minimum corner. + */ + public static Vec3i getOrigin(int i) { + return DataManager.getSchematicPlacementManager().getAllSchematicsPlacements().get(i).getOrigin(); + } + + /** + * @param i index of the Schematic in the schematic placement list. + * @return Filepath of the schematic file. + */ + public static File getSchematicFile(int i) { + return DataManager.getSchematicPlacementManager().getAllSchematicsPlacements().get(i).getSchematicFile(); + } + + /** + * @param i index of the Schematic in the schematic placement list. + * @return rotation of the schematic placement. + */ + public static Rotation getRotation(int i) { + return DataManager.getSchematicPlacementManager().getAllSchematicsPlacements().get(i).getRotation(); + } + + /** + * @param i index of the Schematic in the schematic placement list. + * @return the mirroring of the schematic placement. + */ + public static Mirror getMirror(int i) { + return DataManager.getSchematicPlacementManager().getAllSchematicsPlacements().get(i).getMirror(); + } + + /** + * @param schematic original schematic. + * @param i index of the Schematic in the schematic placement list. + * @return the minimum corner coordinates of the schematic, after the original schematic got rotated and mirrored. + */ + public static Vec3i getCorrectedOrigin(LitematicaSchematic schematic, int i) { + int x = LitematicaHelper.getOrigin(i).getX(); + int y = LitematicaHelper.getOrigin(i).getY(); + int z = LitematicaHelper.getOrigin(i).getZ(); + int mx = schematic.getOffsetMinCorner().getX(); + int my = schematic.getOffsetMinCorner().getY(); + int mz = schematic.getOffsetMinCorner().getZ(); + int sx = (schematic.getX() - 1) * -1; + int sz = (schematic.getZ() - 1) * -1; + + Vec3i correctedOrigin; + Mirror mirror = LitematicaHelper.getMirror(i); + Rotation rotation = LitematicaHelper.getRotation(i); + + //todo there has to be a better way to do this but i cant finde it atm + switch (mirror) { + case FRONT_BACK: + case LEFT_RIGHT: + switch ((mirror.ordinal() * 2 + rotation.ordinal()) % 4) { + case 1: + correctedOrigin = new Vec3i(x + (sz - mz), y + my, z + (sx - mx)); + break; + case 2: + correctedOrigin = new Vec3i(x + mx, y + my, z + (sz - mz)); + break; + case 3: + correctedOrigin = new Vec3i(x + mz, y + my, z + mx); + break; + default: + correctedOrigin = new Vec3i(x + (sx - mx), y + my, z + mz); + break; + } + break; + default: + switch (rotation) { + case CLOCKWISE_90: + correctedOrigin = new Vec3i(x + (sz - mz), y + my, z + mx); + break; + case CLOCKWISE_180: + correctedOrigin = new Vec3i(x + (sx - mx), y + my, z + (sz - mz)); + break; + case COUNTERCLOCKWISE_90: + correctedOrigin = new Vec3i(x + mz, y + my, z + (sx - mx)); + break; + default: + correctedOrigin = new Vec3i(x + mx, y + my, z + mz); + break; + } + } + return correctedOrigin; + } + + /** + * @param in the xyz offsets of the block relative to the schematic minimum corner. + * @param sizeX size of the schematic in the x-axis direction. + * @param sizeZ size of the schematic in the z-axis direction. + * @param mirror the mirroring of the schematic placement. + * @return the corresponding xyz coordinates after mirroring them according to the given mirroring. + */ + public static Vec3i doMirroring(Vec3i in, int sizeX, int sizeZ, Mirror mirror) { + int xOut = in.getX(); + int zOut = in.getZ(); + if (mirror == Mirror.LEFT_RIGHT) { + zOut = sizeZ - in.getZ(); + } else if (mirror == Mirror.FRONT_BACK) { + xOut = sizeX - in.getX(); + } + return new Vec3i(xOut, in.getY(), zOut); + } + + /** + * @param in the xyz offsets of the block relative to the schematic minimum corner. + * @param sizeX size of the schematic in the x-axis direction. + * @param sizeZ size of the schematic in the z-axis direction. + * @return the corresponding xyz coordinates after rotation them 90° clockwise. + */ + public static Vec3i rotate(Vec3i in, int sizeX, int sizeZ) { + return new Vec3i(sizeX - (sizeX - sizeZ) - in.getZ(), in.getY(), in.getX()); + } + + /** + * IDFK this just grew and it somehow works. If you understand how, pls tell me. + * + * @param schemIn give in the original schematic. + * @param i index of the Schematic in the schematic placement list. + * @return get it out rotated and mirrored. + */ + public static LitematicaSchematic blackMagicFuckery(LitematicaSchematic schemIn, int i) { + LitematicaSchematic tempSchem = schemIn.getCopy(LitematicaHelper.getRotation(i).ordinal() % 2 == 1); + for (int yCounter = 0; yCounter < schemIn.getY(); yCounter++) { + for (int zCounter = 0; zCounter < schemIn.getZ(); zCounter++) { + for (int xCounter = 0; xCounter < schemIn.getX(); xCounter++) { + Vec3i xyzHolder = new Vec3i(xCounter, yCounter, zCounter); + xyzHolder = LitematicaHelper.doMirroring(xyzHolder, schemIn.getX() - 1, schemIn.getZ() - 1, LitematicaHelper.getMirror(i)); + for (int turns = 0; turns < LitematicaHelper.getRotation(i).ordinal(); turns++) { + if ((turns % 2) == 0) { + xyzHolder = LitematicaHelper.rotate(xyzHolder, schemIn.getX() - 1, schemIn.getZ() - 1); + } else { + xyzHolder = LitematicaHelper.rotate(xyzHolder, schemIn.getZ() - 1, schemIn.getX() - 1); + } + } + IBlockState state = schemIn.getDirect(xCounter, yCounter, zCounter); + try { + state = state.withMirror(LitematicaHelper.getMirror(i)).withRotation(LitematicaHelper.getRotation(i)); + } catch (NullPointerException e) { + //nothing to worry about it's just a hole in the schematic. + } + tempSchem.setDirect(xyzHolder.getX(), xyzHolder.getY(), xyzHolder.getZ(), state); + } + } + } + return tempSchem; + } +} \ No newline at end of file diff --git a/src/schematica_api/java/fi/dy/masa/litematica/Litematica.java b/src/schematica_api/java/fi/dy/masa/litematica/Litematica.java new file mode 100644 index 00000000..46f4e02a --- /dev/null +++ b/src/schematica_api/java/fi/dy/masa/litematica/Litematica.java @@ -0,0 +1,21 @@ +/* + * 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 . + */ + +package fi.dy.masa.litematica; + +public class Litematica { +} diff --git a/src/schematica_api/java/fi/dy/masa/litematica/data/DataManager.java b/src/schematica_api/java/fi/dy/masa/litematica/data/DataManager.java new file mode 100644 index 00000000..39fea40c --- /dev/null +++ b/src/schematica_api/java/fi/dy/masa/litematica/data/DataManager.java @@ -0,0 +1,33 @@ +/* + * 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 . + */ + +package fi.dy.masa.litematica.data; + +import fi.dy.masa.litematica.schematic.placement.SchematicPlacementManager; + +public class DataManager { + public static final DataManager INSTANCE = new DataManager(); + private final SchematicPlacementManager schematicPlacementManager = new SchematicPlacementManager(); + + private static DataManager getInstance() { + return INSTANCE; + } + + public static SchematicPlacementManager getSchematicPlacementManager() { + return getInstance().schematicPlacementManager; + } +} diff --git a/src/schematica_api/java/fi/dy/masa/litematica/schematic/placement/SchematicPlacement.java b/src/schematica_api/java/fi/dy/masa/litematica/schematic/placement/SchematicPlacement.java new file mode 100644 index 00000000..773aa392 --- /dev/null +++ b/src/schematica_api/java/fi/dy/masa/litematica/schematic/placement/SchematicPlacement.java @@ -0,0 +1,35 @@ +/* + * 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 . + */ + +package fi.dy.masa.litematica.schematic.placement; + +import net.minecraft.util.Mirror; +import net.minecraft.util.Rotation; + +public class SchematicPlacement extends SchematicPlacementUnloaded { + private Rotation rotation; + private Mirror mirror; + + public Rotation getRotation() { + return this.rotation; + } + + public Mirror getMirror() { + return this.mirror; + } + +} diff --git a/src/schematica_api/java/fi/dy/masa/litematica/schematic/placement/SchematicPlacementManager.java b/src/schematica_api/java/fi/dy/masa/litematica/schematic/placement/SchematicPlacementManager.java new file mode 100644 index 00000000..805fca8b --- /dev/null +++ b/src/schematica_api/java/fi/dy/masa/litematica/schematic/placement/SchematicPlacementManager.java @@ -0,0 +1,31 @@ +/* + * 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 . + */ + +package fi.dy.masa.litematica.schematic.placement; + +import java.util.ArrayList; +import java.util.List; + +public class SchematicPlacementManager { + private final List schematicPlacements = new ArrayList<>(); + + //in case of a java.lang.NoSuchMethodError try change the name of this method to getAllSchematicPlacements() + //there are inconsistencies in the litematica mod about the naming of this method + public List getAllSchematicsPlacements() { + return schematicPlacements; + } +} diff --git a/src/schematica_api/java/fi/dy/masa/litematica/schematic/placement/SchematicPlacementUnloaded.java b/src/schematica_api/java/fi/dy/masa/litematica/schematic/placement/SchematicPlacementUnloaded.java new file mode 100644 index 00000000..bc857a9c --- /dev/null +++ b/src/schematica_api/java/fi/dy/masa/litematica/schematic/placement/SchematicPlacementUnloaded.java @@ -0,0 +1,43 @@ +/* + * 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 . + */ + +package fi.dy.masa.litematica.schematic.placement; + +import net.minecraft.util.math.BlockPos; + +import javax.annotation.Nullable; +import java.io.File; + +public class SchematicPlacementUnloaded { + protected String name = "?"; + @Nullable + protected File schematicFile; + protected BlockPos origin = BlockPos.ORIGIN; + + public String getName() { + return this.name; + } + + @Nullable + public File getSchematicFile() { + return this.schematicFile; + } + + public BlockPos getOrigin() { + return this.origin; + } +}