diff --git a/src/api/java/baritone/api/Settings.java b/src/api/java/baritone/api/Settings.java index 33b1c134..95682d32 100644 --- a/src/api/java/baritone/api/Settings.java +++ b/src/api/java/baritone/api/Settings.java @@ -206,6 +206,29 @@ public final class Settings { ))); + /** + * A list of blocks to be treated as correct. + *

+ * If a schematic asks for any block on this list at a certain position, it will be treated as correct, regardless of what it currently is. + */ + public final Setting> buildSkipBlocks = new Setting<>(new ArrayList<>(Arrays.asList( + + ))); + + /** + * A mapping of blocks to blocks treated as correct in their position + *

+ * If a schematic asks for a block on this mapping, all blocks on the mapped list will be accepted at that location as well + */ + public final Setting>> buildValidSubstitutes = new Setting<>(new HashMap<>()); + + /** + * A mapping of blocks to blocks to be built instead + *

+ * If a schematic asks for a block on this mapping, Baritone will place the first placeable block in the mapped list + */ + public final Setting>> buildSubstitutes = new Setting<>(new HashMap<>()); + /** * A list of blocks to become air *

diff --git a/src/api/java/baritone/api/schematic/FillSchematic.java b/src/api/java/baritone/api/schematic/FillSchematic.java index de9ccf97..f2fb203c 100644 --- a/src/api/java/baritone/api/schematic/FillSchematic.java +++ b/src/api/java/baritone/api/schematic/FillSchematic.java @@ -19,7 +19,6 @@ package baritone.api.schematic; import baritone.api.utils.BlockOptionalMeta; import net.minecraft.block.state.IBlockState; -import net.minecraft.init.Blocks; import java.util.List; @@ -44,8 +43,6 @@ public class FillSchematic extends AbstractSchematic { public IBlockState desiredState(int x, int y, int z, IBlockState current, List approxPlaceable) { if (bom.matches(current)) { return current; - } else if (current.getBlock() != Blocks.AIR) { - return Blocks.AIR.getDefaultState(); } for (IBlockState placeable : approxPlaceable) { if (bom.matches(placeable)) { diff --git a/src/api/java/baritone/api/schematic/SubstituteSchematic.java b/src/api/java/baritone/api/schematic/SubstituteSchematic.java new file mode 100644 index 00000000..72787c24 --- /dev/null +++ b/src/api/java/baritone/api/schematic/SubstituteSchematic.java @@ -0,0 +1,89 @@ +/* + * 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.api.schematic; + +import net.minecraft.block.Block; +import net.minecraft.block.BlockAir; +import net.minecraft.block.properties.IProperty; +import net.minecraft.block.state.IBlockState; +import net.minecraft.init.Blocks; +import java.util.Collection; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +public class SubstituteSchematic extends AbstractSchematic { + + private final ISchematic schematic; + private final Map> substitutions; + private final Map> blockStateCache = new HashMap<>(); + + public SubstituteSchematic(ISchematic schematic, Map> substitutions) { + super(schematic.widthX(), schematic.heightY(), schematic.lengthZ()); + this.schematic = schematic; + this.substitutions = substitutions; + } + + @Override + public boolean inSchematic(int x, int y, int z, IBlockState currentState) { + return schematic.inSchematic(x, y, z, currentState); + } + + @Override + public IBlockState desiredState(int x, int y, int z, IBlockState current, List approxPlaceable) { + IBlockState desired = schematic.desiredState(x, y, z, current, approxPlaceable); + Block desiredBlock = desired.getBlock(); + if (!substitutions.containsKey(desiredBlock)) { + return desired; + } + List substitutes = substitutions.get(desiredBlock); + if (substitutes.contains(current.getBlock()) && !(current.getBlock() instanceof BlockAir)) {// don't preserve air, it's almost always there and almost never wanted + return withBlock(desired, current.getBlock()); + } + for (Block substitute : substitutes) { + if (substitute instanceof BlockAir) { + return current.getBlock() instanceof BlockAir ? current : Blocks.AIR.getDefaultState(); // can always "place" air + } + for (IBlockState placeable : approxPlaceable) { + if (substitute.equals(placeable.getBlock())) { + return withBlock(desired, placeable.getBlock()); + } + } + } + return substitutes.get(0).getDefaultState(); + } + + private IBlockState withBlock(IBlockState state, Block block) { + if (blockStateCache.containsKey(state) && blockStateCache.get(state).containsKey(block)) { + return blockStateCache.get(state).get(block); + } + Collection> properties = state.getPropertyKeys(); + IBlockState newState = block.getDefaultState(); + for (IProperty property : properties) { + try { + newState = copySingleProp(state, newState, property); + } catch (IllegalArgumentException e) { //property does not exist for target block + } + } + blockStateCache.computeIfAbsent(state, s -> new HashMap()).put(block, newState); + return newState; + } + private > IBlockState copySingleProp(IBlockState fromState, IBlockState toState, IProperty prop) { + return toState.withProperty(prop, fromState.getValue(prop)); + } +} diff --git a/src/api/java/baritone/api/utils/SettingsUtil.java b/src/api/java/baritone/api/utils/SettingsUtil.java index f9cb7136..48c8ee4d 100644 --- a/src/api/java/baritone/api/utils/SettingsUtil.java +++ b/src/api/java/baritone/api/utils/SettingsUtil.java @@ -35,6 +35,7 @@ import java.nio.file.NoSuchFileException; import java.nio.file.Path; import java.util.ArrayList; import java.util.List; +import java.util.Map; import java.util.Objects; import java.util.function.Consumer; import java.util.function.Function; @@ -261,6 +262,36 @@ public class SettingsUtil { public boolean accepts(Type type) { return List.class.isAssignableFrom(TypeUtils.resolveBaseClass(type)); } + }, + MAPPING() { + @Override + public Object parse(ParserContext context, String raw) { + Type keyType = ((ParameterizedType) context.getSetting().getType()).getActualTypeArguments()[0]; + Type valueType = ((ParameterizedType) context.getSetting().getType()).getActualTypeArguments()[1]; + Parser keyParser = Parser.getParser(keyType); + Parser valueParser = Parser.getParser(valueType); + + return Stream.of(raw.split(",(?=[^,]*->)")) + .map(s -> s.split("->")) + .collect(Collectors.toMap(s -> keyParser.parse(context, s[0]), s -> valueParser.parse(context, s[1]))); + } + + @Override + public String toString(ParserContext context, Object value) { + Type keyType = ((ParameterizedType) context.getSetting().getType()).getActualTypeArguments()[0]; + Type valueType = ((ParameterizedType) context.getSetting().getType()).getActualTypeArguments()[1]; + Parser keyParser = Parser.getParser(keyType); + Parser valueParser = Parser.getParser(valueType); + + return ((Map) value).entrySet().stream() + .map(o -> keyParser.toString(context, o.getKey()) + "->" + valueParser.toString(context, o.getValue())) + .collect(Collectors.joining(",")); + } + + @Override + public boolean accepts(Type type) { + return Map.class.isAssignableFrom(TypeUtils.resolveBaseClass(type)); + } }; private final Class cla$$; diff --git a/src/main/java/baritone/process/BuilderProcess.java b/src/main/java/baritone/process/BuilderProcess.java index bae136e0..ca8ca534 100644 --- a/src/main/java/baritone/process/BuilderProcess.java +++ b/src/main/java/baritone/process/BuilderProcess.java @@ -26,6 +26,7 @@ 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.format.ISchematicFormat; @@ -85,6 +86,9 @@ public final class BuilderProcess extends BaritoneProcessHelper implements IBuil this.name = name; this.schematic = schematic; this.realSchematic = null; + if (!Baritone.settings().buildSubstitutes.value.isEmpty()) { + this.schematic = new SubstituteSchematic(this.schematic, Baritone.settings().buildSubstitutes.value); + } int x = origin.getX(); int y = origin.getY(); int z = origin.getZ(); @@ -588,7 +592,8 @@ public final class BuilderProcess extends BaritoneProcessHelper implements IBuil continue; } // this is not in render distance - if (!observedCompleted.contains(BetterBlockPos.longHash(blockX, blockY, blockZ))) { + if (!observedCompleted.contains(BetterBlockPos.longHash(blockX, blockY, blockZ)) + && !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)); @@ -833,6 +838,12 @@ public final class BuilderProcess extends BaritoneProcessHelper implements IBuil if (!(current.getBlock() instanceof BlockAir) && Baritone.settings().buildIgnoreExisting.value && !itemVerify) { return true; } + if (Baritone.settings().buildSkipBlocks.value.contains(desired.getBlock()) && !itemVerify) { + return true; + } + if (Baritone.settings().buildValidSubstitutes.value.getOrDefault(desired.getBlock(), Collections.emptyList()).contains(current.getBlock()) && !itemVerify) { + return true; + } return current.equals(desired); } @@ -870,7 +881,7 @@ public final class BuilderProcess extends BaritoneProcessHelper implements IBuil return COST_INF; } IBlockState sch = getSchematic(x, y, z, current); - if (sch != null) { + if (sch != null && !Baritone.settings().buildSkipBlocks.value.contains(sch.getBlock())) { // TODO this can return true even when allowPlace is off.... is that an issue? if (sch.getBlock() == Blocks.AIR) { // we want this to be air, but they're asking if they can place here @@ -904,7 +915,7 @@ public final class BuilderProcess extends BaritoneProcessHelper implements IBuil return COST_INF; } IBlockState sch = getSchematic(x, y, z, current); - if (sch != null) { + if (sch != null && !Baritone.settings().buildSkipBlocks.value.contains(sch.getBlock())) { if (sch.getBlock() == Blocks.AIR) { // it should be air // regardless of current contents, we can break it