From eee705b371d27bf7f797ffa3d578c797cf046395 Mon Sep 17 00:00:00 2001 From: Brady Date: Tue, 24 Dec 2019 17:20:00 -0600 Subject: [PATCH] Schematic Format API support --- .../java/baritone/api/IBaritoneProvider.java | 6 ++ .../api/schematic/ISchematicSystem.java | 44 ++++++++++++++ .../api/schematic/IStaticSchematic.java | 60 +++++++++++++++++++ .../schematic/format/ISchematicFormat.java | 43 +++++++++++++ .../schematic/parse/ISchematicParser.java | 6 +- src/main/java/baritone/BaritoneProvider.java | 7 +++ .../java/baritone/process/BuilderProcess.java | 9 +-- .../utils/schematic/MapArtSchematic.java | 40 ++++++++++++- .../utils/schematic/SchematicSystem.java | 51 ++++++++++++++++ .../utils/schematic/StaticSchematic.java | 58 ++++-------------- ...rmat.java => DefaultSchematicFormats.java} | 25 ++++---- .../utils/schematic/parse/MCEditParser.java | 5 +- .../utils/schematic/parse/SpongeParser.java | 5 +- 13 files changed, 282 insertions(+), 77 deletions(-) create mode 100644 src/api/java/baritone/api/schematic/ISchematicSystem.java create mode 100644 src/api/java/baritone/api/schematic/IStaticSchematic.java create mode 100644 src/api/java/baritone/api/schematic/format/ISchematicFormat.java rename src/{main/java/baritone/utils => api/java/baritone/api}/schematic/parse/ISchematicParser.java (84%) create mode 100644 src/main/java/baritone/utils/schematic/SchematicSystem.java rename src/main/java/baritone/utils/schematic/format/{SchematicFormat.java => DefaultSchematicFormats.java} (68%) diff --git a/src/api/java/baritone/api/IBaritoneProvider.java b/src/api/java/baritone/api/IBaritoneProvider.java index b7228e33..84a8abbb 100644 --- a/src/api/java/baritone/api/IBaritoneProvider.java +++ b/src/api/java/baritone/api/IBaritoneProvider.java @@ -20,6 +20,7 @@ package baritone.api; import baritone.api.cache.IWorldScanner; import baritone.api.command.ICommand; import baritone.api.command.ICommandSystem; +import baritone.api.schematic.ISchematicSystem; import net.minecraft.client.entity.EntityPlayerSP; import java.util.List; @@ -82,4 +83,9 @@ public interface IBaritoneProvider { * @return The {@link ICommandSystem} instance. */ ICommandSystem getCommandSystem(); + + /** + * @return The {@link ISchematicSystem} instance. + */ + ISchematicSystem getSchematicSystem(); } diff --git a/src/api/java/baritone/api/schematic/ISchematicSystem.java b/src/api/java/baritone/api/schematic/ISchematicSystem.java new file mode 100644 index 00000000..c8f03907 --- /dev/null +++ b/src/api/java/baritone/api/schematic/ISchematicSystem.java @@ -0,0 +1,44 @@ +/* + * 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 baritone.api.command.registry.Registry; +import baritone.api.schematic.format.ISchematicFormat; + +import java.io.File; +import java.util.Optional; + +/** + * @author Brady + * @since 12/23/2019 + */ +public interface ISchematicSystem { + + /** + * @return The registry of supported schematic formats + */ + Registry getRegistry(); + + /** + * Attempts to find an {@link ISchematicFormat} that supports the specified schematic file. + * + * @param file A schematic file + * @return The corresponding format for the file, {@link Optional#empty()} if no candidates were found. + */ + Optional getByFile(File file); +} diff --git a/src/api/java/baritone/api/schematic/IStaticSchematic.java b/src/api/java/baritone/api/schematic/IStaticSchematic.java new file mode 100644 index 00000000..268b1b1f --- /dev/null +++ b/src/api/java/baritone/api/schematic/IStaticSchematic.java @@ -0,0 +1,60 @@ +/* + * 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.state.IBlockState; + +/** + * A static schematic is capable of providing the desired state at a given position without + * additional context. Schematics of this type are expected to have non-varying contents. + * + * @see #getDirect(int, int, int) + * + * @author Brady + * @since 12/24/2019 + */ +public interface IStaticSchematic extends ISchematic { + + /** + * Gets the {@link IBlockState} for a given position in this schematic. It should be guaranteed + * that the return value of this method will not change given that the parameters are the same. + * + * @param x The X block position + * @param y The Y block position + * @param z The Z block position + * @return The desired state at the specified position. + */ + IBlockState getDirect(int x, int y, int z); + + /** + * Returns an {@link IBlockState} array of size {@link #heightY()} which contains all + * desired block states in the specified vertical column. The index of {@link IBlockState}s + * in the array are equivalent to their Y position in the schematic. + * + * @param x The X column position + * @param z The Z column position + * @return An {@link IBlockState} array + */ + default IBlockState[] getColumn(int x, int z) { + IBlockState[] column = new IBlockState[this.heightY()]; + for (int i = 0; i < this.heightY(); i++) { + column[i] = getDirect(x, i, z); + } + return column; + } +} diff --git a/src/api/java/baritone/api/schematic/format/ISchematicFormat.java b/src/api/java/baritone/api/schematic/format/ISchematicFormat.java new file mode 100644 index 00000000..260ab453 --- /dev/null +++ b/src/api/java/baritone/api/schematic/format/ISchematicFormat.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 baritone.api.schematic.format; + +import baritone.api.schematic.ISchematic; +import baritone.api.schematic.parse.ISchematicParser; + +import java.io.File; + +/** + * The base of a {@link ISchematic} file format + * + * @author Brady + * @since 12/23/2019 + */ +public interface ISchematicFormat { + + /** + * @return The parser for creating schematics of this format + */ + ISchematicParser getParser(); + + /** + * @param file The file to check against + * @return Whether or not the specified file matches this schematic format + */ + boolean isFileType(File file); +} diff --git a/src/main/java/baritone/utils/schematic/parse/ISchematicParser.java b/src/api/java/baritone/api/schematic/parse/ISchematicParser.java similarity index 84% rename from src/main/java/baritone/utils/schematic/parse/ISchematicParser.java rename to src/api/java/baritone/api/schematic/parse/ISchematicParser.java index 8bc716f1..ab0f12f4 100644 --- a/src/main/java/baritone/utils/schematic/parse/ISchematicParser.java +++ b/src/api/java/baritone/api/schematic/parse/ISchematicParser.java @@ -15,9 +15,9 @@ * along with Baritone. If not, see . */ -package baritone.utils.schematic.parse; +package baritone.api.schematic.parse; -import baritone.utils.schematic.StaticSchematic; +import baritone.api.schematic.IStaticSchematic; import java.io.IOException; import java.io.InputStream; @@ -28,5 +28,5 @@ import java.io.InputStream; */ public interface ISchematicParser { - StaticSchematic parse(InputStream input) throws IOException; + IStaticSchematic parse(InputStream input) throws IOException; } diff --git a/src/main/java/baritone/BaritoneProvider.java b/src/main/java/baritone/BaritoneProvider.java index cb24dfe2..84034ef3 100644 --- a/src/main/java/baritone/BaritoneProvider.java +++ b/src/main/java/baritone/BaritoneProvider.java @@ -21,9 +21,11 @@ import baritone.api.IBaritone; import baritone.api.IBaritoneProvider; import baritone.api.cache.IWorldScanner; import baritone.api.command.ICommandSystem; +import baritone.api.schematic.ISchematicSystem; import baritone.command.BaritoneChatControl; import baritone.cache.WorldScanner; import baritone.command.CommandSystem; +import baritone.utils.schematic.SchematicSystem; import java.util.Collections; import java.util.List; @@ -64,4 +66,9 @@ public final class BaritoneProvider implements IBaritoneProvider { public ICommandSystem getCommandSystem() { return CommandSystem.INSTANCE; } + + @Override + public ISchematicSystem getSchematicSystem() { + return SchematicSystem.INSTANCE; + } } diff --git a/src/main/java/baritone/process/BuilderProcess.java b/src/main/java/baritone/process/BuilderProcess.java index a7a17160..55361602 100644 --- a/src/main/java/baritone/process/BuilderProcess.java +++ b/src/main/java/baritone/process/BuilderProcess.java @@ -27,6 +27,8 @@ import baritone.api.process.PathingCommand; import baritone.api.process.PathingCommandType; import baritone.api.schematic.FillSchematic; import baritone.api.schematic.ISchematic; +import baritone.api.schematic.IStaticSchematic; +import baritone.api.schematic.format.ISchematicFormat; import baritone.api.utils.BetterBlockPos; import baritone.api.utils.RayTraceUtils; import baritone.api.utils.Rotation; @@ -39,8 +41,7 @@ import baritone.utils.BaritoneProcessHelper; import baritone.utils.BlockStateInterface; import baritone.utils.PathingCommandContext; import baritone.utils.schematic.MapArtSchematic; -import baritone.utils.schematic.StaticSchematic; -import baritone.utils.schematic.format.SchematicFormat; +import baritone.utils.schematic.SchematicSystem; import baritone.utils.schematic.schematica.SchematicaHelper; import it.unimi.dsi.fastutil.longs.LongOpenHashSet; import net.minecraft.block.BlockAir; @@ -116,7 +117,7 @@ public final class BuilderProcess extends BaritoneProcessHelper implements IBuil @Override public boolean build(String name, File schematic, Vec3i origin) { - Optional format = SchematicFormat.getByFile(schematic); + Optional format = SchematicSystem.INSTANCE.getByFile(schematic); if (!format.isPresent()) { return false; } @@ -130,7 +131,7 @@ public final class BuilderProcess extends BaritoneProcessHelper implements IBuil } if (Baritone.settings().mapArtMode.value) { - parsed = new MapArtSchematic((StaticSchematic) parsed); + parsed = new MapArtSchematic((IStaticSchematic) parsed); } build(name, parsed, origin); diff --git a/src/main/java/baritone/utils/schematic/MapArtSchematic.java b/src/main/java/baritone/utils/schematic/MapArtSchematic.java index dc2edf2c..31442261 100644 --- a/src/main/java/baritone/utils/schematic/MapArtSchematic.java +++ b/src/main/java/baritone/utils/schematic/MapArtSchematic.java @@ -17,20 +17,54 @@ package baritone.utils.schematic; +import baritone.api.schematic.IStaticSchematic; import baritone.api.schematic.MaskSchematic; +import net.minecraft.block.BlockAir; import net.minecraft.block.state.IBlockState; +import java.util.OptionalInt; +import java.util.function.Predicate; + public class MapArtSchematic extends MaskSchematic { private final int[][] heightMap; - public MapArtSchematic(StaticSchematic schematic) { + public MapArtSchematic(IStaticSchematic schematic) { super(schematic); - this.heightMap = schematic.getHeightMap(); + this.heightMap = generateHeightMap(schematic); } @Override protected boolean partOfMask(int x, int y, int z, IBlockState currentState) { - return y >= heightMap[x][z]; + return y >= this.heightMap[x][z]; + } + + private static int[][] generateHeightMap(IStaticSchematic schematic) { + int[][] heightMap = new int[schematic.widthX()][schematic.lengthZ()]; + + for (int x = 0; x < schematic.widthX(); x++) { + for (int z = 0; z < schematic.lengthZ(); z++) { + IBlockState[] column = schematic.getColumn(x, z); + + OptionalInt lowestBlockY = lastIndexMatching(column, state -> !(state.getBlock() instanceof BlockAir)); + if (lowestBlockY.isPresent()) { + heightMap[x][z] = lowestBlockY.getAsInt(); + } else { + System.out.println("Column " + x + "," + z + " has no blocks, but it's apparently map art? wtf"); + System.out.println("Letting it be whatever"); + heightMap[x][z] = 256; + } + } + } + return heightMap; + } + + private static OptionalInt lastIndexMatching(T[] arr, Predicate predicate) { + for (int y = arr.length - 1; y >= 0; y--) { + if (predicate.test(arr[y])) { + return OptionalInt.of(y); + } + } + return OptionalInt.empty(); } } diff --git a/src/main/java/baritone/utils/schematic/SchematicSystem.java b/src/main/java/baritone/utils/schematic/SchematicSystem.java new file mode 100644 index 00000000..8afafa8c --- /dev/null +++ b/src/main/java/baritone/utils/schematic/SchematicSystem.java @@ -0,0 +1,51 @@ +/* + * 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; + +import baritone.api.command.registry.Registry; +import baritone.api.schematic.ISchematicSystem; +import baritone.api.schematic.format.ISchematicFormat; +import baritone.utils.schematic.format.DefaultSchematicFormats; + +import java.io.File; +import java.util.Arrays; +import java.util.Optional; + +/** + * @author Brady + * @since 12/24/2019 + */ +public enum SchematicSystem implements ISchematicSystem { + INSTANCE; + + private final Registry registry = new Registry<>(); + + SchematicSystem() { + Arrays.stream(DefaultSchematicFormats.values()).forEach(this.registry::register); + } + + @Override + public Registry getRegistry() { + return this.registry; + } + + @Override + public Optional getByFile(File file) { + return this.registry.stream().filter(format -> format.isFileType(file)).findFirst(); + } +} diff --git a/src/main/java/baritone/utils/schematic/StaticSchematic.java b/src/main/java/baritone/utils/schematic/StaticSchematic.java index 97fc91cd..2251450a 100644 --- a/src/main/java/baritone/utils/schematic/StaticSchematic.java +++ b/src/main/java/baritone/utils/schematic/StaticSchematic.java @@ -18,71 +18,33 @@ package baritone.utils.schematic; import baritone.api.schematic.AbstractSchematic; -import net.minecraft.block.BlockAir; +import baritone.api.schematic.IStaticSchematic; import net.minecraft.block.state.IBlockState; import java.util.List; -import java.util.OptionalInt; -import java.util.function.Predicate; /** + * Default implementation of {@link IStaticSchematic} + * * @author Brady * @since 12/23/2019 */ -public abstract class StaticSchematic extends AbstractSchematic { +public class StaticSchematic extends AbstractSchematic implements IStaticSchematic { - /** - * Block states for this schematic stored in [x, z, y] indexing order - */ protected IBlockState[][][] states; - /** - * The maximum height of a given block in this schematic, indexed as [x, z]. - * This is lazily initialized by {@link #getHeightMap()}. - */ - protected int[][] heightMap; - - public StaticSchematic() { - super(); - } - - public StaticSchematic(int x, int y, int z) { - super(x, y, z); - } - @Override public IBlockState desiredState(int x, int y, int z, IBlockState current, List approxPlaceable) { return this.states[x][z][y]; } - public final int[][] getHeightMap() { - if (this.heightMap == null) { - this.heightMap = new int[this.x][this.z]; - - for (int x = 0; x < this.x; x++) { - for (int z = 0; z < this.z; z++) { - IBlockState[] column = states[x][z]; - - OptionalInt lowestBlockY = lastIndexMatching(column, state -> !(state.getBlock() instanceof BlockAir)); - if (lowestBlockY.isPresent()) { - this.heightMap[x][z] = lowestBlockY.getAsInt(); - } else { - System.out.println("Column " + x + "," + z + " has no blocks, but it's apparently map art? wtf"); - System.out.println("Letting it be whatever"); - this.heightMap[x][z] = 256; - } - } - } - } - return this.heightMap; + @Override + public IBlockState getDirect(int x, int y, int z) { + return this.states[x][z][y]; } - private static OptionalInt lastIndexMatching(T[] arr, Predicate predicate) { - for (int y = arr.length - 1; y >= 0; y--) { - if (predicate.test(arr[y])) { - return OptionalInt.of(y); - } - } - return OptionalInt.empty(); + @Override + public IBlockState[] getColumn(int x, int z) { + return this.states[x][z]; } } diff --git a/src/main/java/baritone/utils/schematic/format/SchematicFormat.java b/src/main/java/baritone/utils/schematic/format/DefaultSchematicFormats.java similarity index 68% rename from src/main/java/baritone/utils/schematic/format/SchematicFormat.java rename to src/main/java/baritone/utils/schematic/format/DefaultSchematicFormats.java index 6c84273e..494d6ec8 100644 --- a/src/main/java/baritone/utils/schematic/format/SchematicFormat.java +++ b/src/main/java/baritone/utils/schematic/format/DefaultSchematicFormats.java @@ -17,20 +17,21 @@ package baritone.utils.schematic.format; -import baritone.utils.schematic.parse.ISchematicParser; +import baritone.api.schematic.format.ISchematicFormat; +import baritone.api.schematic.parse.ISchematicParser; import baritone.utils.schematic.parse.MCEditParser; import baritone.utils.schematic.parse.SpongeParser; import org.apache.commons.io.FilenameUtils; import java.io.File; -import java.util.Optional; -import java.util.stream.Stream; /** + * Default implementations of {@link ISchematicFormat} + * * @author Brady * @since 12/13/2019 */ -public enum SchematicFormat { +public enum DefaultSchematicFormats implements ISchematicFormat { /** * The MCEdit schematic specification. Commonly denoted by the ".schematic" file extension. @@ -47,24 +48,18 @@ public enum SchematicFormat { private final String extension; private final ISchematicParser parser; - SchematicFormat(String extension, ISchematicParser parser) { + DefaultSchematicFormats(String extension, ISchematicParser parser) { this.extension = extension; this.parser = parser; } + @Override public final ISchematicParser getParser() { return this.parser; } - public static Optional getByFile(File schematic) { - // TODO: Better identification - // Maybe peek file contents and make a safe determination? - return getByExtension(FilenameUtils.getExtension(schematic.getAbsolutePath())); - } - - public static Optional getByExtension(String extension) { - return extension == null || extension.isEmpty() - ? Optional.empty() - : Stream.of(values()).filter(format -> format.extension.equalsIgnoreCase(extension)).findFirst(); + @Override + public boolean isFileType(File file) { + return this.extension.equalsIgnoreCase(FilenameUtils.getExtension(file.getAbsolutePath())); } } diff --git a/src/main/java/baritone/utils/schematic/parse/MCEditParser.java b/src/main/java/baritone/utils/schematic/parse/MCEditParser.java index 54d78201..3599b824 100644 --- a/src/main/java/baritone/utils/schematic/parse/MCEditParser.java +++ b/src/main/java/baritone/utils/schematic/parse/MCEditParser.java @@ -17,8 +17,9 @@ package baritone.utils.schematic.parse; +import baritone.api.schematic.parse.ISchematicParser; import baritone.utils.schematic.StaticSchematic; -import baritone.utils.schematic.format.SchematicFormat; +import baritone.utils.schematic.format.DefaultSchematicFormats; import net.minecraft.block.Block; import net.minecraft.block.state.IBlockState; import net.minecraft.nbt.CompressedStreamTools; @@ -28,7 +29,7 @@ import java.io.IOException; import java.io.InputStream; /** - * An implementation of {@link ISchematicParser} for {@link SchematicFormat#MCEDIT} + * An implementation of {@link ISchematicParser} for {@link DefaultSchematicFormats#MCEDIT} * * @author Brady * @since 12/16/2019 diff --git a/src/main/java/baritone/utils/schematic/parse/SpongeParser.java b/src/main/java/baritone/utils/schematic/parse/SpongeParser.java index 48cc60aa..255916d1 100644 --- a/src/main/java/baritone/utils/schematic/parse/SpongeParser.java +++ b/src/main/java/baritone/utils/schematic/parse/SpongeParser.java @@ -17,8 +17,9 @@ package baritone.utils.schematic.parse; +import baritone.api.schematic.parse.ISchematicParser; import baritone.utils.schematic.StaticSchematic; -import baritone.utils.schematic.format.SchematicFormat; +import baritone.utils.schematic.format.DefaultSchematicFormats; import baritone.utils.type.VarInt; import it.unimi.dsi.fastutil.ints.Int2ObjectArrayMap; import net.minecraft.block.Block; @@ -37,7 +38,7 @@ import java.util.regex.Matcher; import java.util.regex.Pattern; /** - * An implementation of {@link ISchematicParser} for {@link SchematicFormat#SPONGE} + * An implementation of {@link ISchematicParser} for {@link DefaultSchematicFormats#SPONGE} * * @author Brady * @since 12/16/2019