Schematic Format API support

This commit is contained in:
Brady 2019-12-24 17:20:00 -06:00
parent 77bdf6e444
commit eee705b371
No known key found for this signature in database
GPG Key ID: 73A788379A197567
13 changed files with 282 additions and 77 deletions

View File

@ -20,6 +20,7 @@ package baritone.api;
import baritone.api.cache.IWorldScanner; import baritone.api.cache.IWorldScanner;
import baritone.api.command.ICommand; import baritone.api.command.ICommand;
import baritone.api.command.ICommandSystem; import baritone.api.command.ICommandSystem;
import baritone.api.schematic.ISchematicSystem;
import net.minecraft.client.entity.EntityPlayerSP; import net.minecraft.client.entity.EntityPlayerSP;
import java.util.List; import java.util.List;
@ -82,4 +83,9 @@ public interface IBaritoneProvider {
* @return The {@link ICommandSystem} instance. * @return The {@link ICommandSystem} instance.
*/ */
ICommandSystem getCommandSystem(); ICommandSystem getCommandSystem();
/**
* @return The {@link ISchematicSystem} instance.
*/
ISchematicSystem getSchematicSystem();
} }

View File

@ -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 <https://www.gnu.org/licenses/>.
*/
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<ISchematicFormat> 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<ISchematicFormat> getByFile(File file);
}

View File

@ -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 <https://www.gnu.org/licenses/>.
*/
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;
}
}

View File

@ -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 <https://www.gnu.org/licenses/>.
*/
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);
}

View File

@ -15,9 +15,9 @@
* along with Baritone. If not, see <https://www.gnu.org/licenses/>. * along with Baritone. If not, see <https://www.gnu.org/licenses/>.
*/ */
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.IOException;
import java.io.InputStream; import java.io.InputStream;
@ -28,5 +28,5 @@ import java.io.InputStream;
*/ */
public interface ISchematicParser { public interface ISchematicParser {
StaticSchematic parse(InputStream input) throws IOException; IStaticSchematic parse(InputStream input) throws IOException;
} }

View File

@ -21,9 +21,11 @@ import baritone.api.IBaritone;
import baritone.api.IBaritoneProvider; import baritone.api.IBaritoneProvider;
import baritone.api.cache.IWorldScanner; import baritone.api.cache.IWorldScanner;
import baritone.api.command.ICommandSystem; import baritone.api.command.ICommandSystem;
import baritone.api.schematic.ISchematicSystem;
import baritone.command.BaritoneChatControl; import baritone.command.BaritoneChatControl;
import baritone.cache.WorldScanner; import baritone.cache.WorldScanner;
import baritone.command.CommandSystem; import baritone.command.CommandSystem;
import baritone.utils.schematic.SchematicSystem;
import java.util.Collections; import java.util.Collections;
import java.util.List; import java.util.List;
@ -64,4 +66,9 @@ public final class BaritoneProvider implements IBaritoneProvider {
public ICommandSystem getCommandSystem() { public ICommandSystem getCommandSystem() {
return CommandSystem.INSTANCE; return CommandSystem.INSTANCE;
} }
@Override
public ISchematicSystem getSchematicSystem() {
return SchematicSystem.INSTANCE;
}
} }

View File

@ -27,6 +27,8 @@ import baritone.api.process.PathingCommand;
import baritone.api.process.PathingCommandType; import baritone.api.process.PathingCommandType;
import baritone.api.schematic.FillSchematic; import baritone.api.schematic.FillSchematic;
import baritone.api.schematic.ISchematic; 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.BetterBlockPos;
import baritone.api.utils.RayTraceUtils; import baritone.api.utils.RayTraceUtils;
import baritone.api.utils.Rotation; import baritone.api.utils.Rotation;
@ -39,8 +41,7 @@ import baritone.utils.BaritoneProcessHelper;
import baritone.utils.BlockStateInterface; import baritone.utils.BlockStateInterface;
import baritone.utils.PathingCommandContext; import baritone.utils.PathingCommandContext;
import baritone.utils.schematic.MapArtSchematic; import baritone.utils.schematic.MapArtSchematic;
import baritone.utils.schematic.StaticSchematic; import baritone.utils.schematic.SchematicSystem;
import baritone.utils.schematic.format.SchematicFormat;
import baritone.utils.schematic.schematica.SchematicaHelper; import baritone.utils.schematic.schematica.SchematicaHelper;
import it.unimi.dsi.fastutil.longs.LongOpenHashSet; import it.unimi.dsi.fastutil.longs.LongOpenHashSet;
import net.minecraft.block.BlockAir; import net.minecraft.block.BlockAir;
@ -116,7 +117,7 @@ public final class BuilderProcess extends BaritoneProcessHelper implements IBuil
@Override @Override
public boolean build(String name, File schematic, Vec3i origin) { public boolean build(String name, File schematic, Vec3i origin) {
Optional<SchematicFormat> format = SchematicFormat.getByFile(schematic); Optional<ISchematicFormat> format = SchematicSystem.INSTANCE.getByFile(schematic);
if (!format.isPresent()) { if (!format.isPresent()) {
return false; return false;
} }
@ -130,7 +131,7 @@ public final class BuilderProcess extends BaritoneProcessHelper implements IBuil
} }
if (Baritone.settings().mapArtMode.value) { if (Baritone.settings().mapArtMode.value) {
parsed = new MapArtSchematic((StaticSchematic) parsed); parsed = new MapArtSchematic((IStaticSchematic) parsed);
} }
build(name, parsed, origin); build(name, parsed, origin);

View File

@ -17,20 +17,54 @@
package baritone.utils.schematic; package baritone.utils.schematic;
import baritone.api.schematic.IStaticSchematic;
import baritone.api.schematic.MaskSchematic; import baritone.api.schematic.MaskSchematic;
import net.minecraft.block.BlockAir;
import net.minecraft.block.state.IBlockState; import net.minecraft.block.state.IBlockState;
import java.util.OptionalInt;
import java.util.function.Predicate;
public class MapArtSchematic extends MaskSchematic { public class MapArtSchematic extends MaskSchematic {
private final int[][] heightMap; private final int[][] heightMap;
public MapArtSchematic(StaticSchematic schematic) { public MapArtSchematic(IStaticSchematic schematic) {
super(schematic); super(schematic);
this.heightMap = schematic.getHeightMap(); this.heightMap = generateHeightMap(schematic);
} }
@Override @Override
protected boolean partOfMask(int x, int y, int z, IBlockState currentState) { 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 <T> OptionalInt lastIndexMatching(T[] arr, Predicate<? super T> predicate) {
for (int y = arr.length - 1; y >= 0; y--) {
if (predicate.test(arr[y])) {
return OptionalInt.of(y);
}
}
return OptionalInt.empty();
} }
} }

View File

@ -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 <https://www.gnu.org/licenses/>.
*/
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<ISchematicFormat> registry = new Registry<>();
SchematicSystem() {
Arrays.stream(DefaultSchematicFormats.values()).forEach(this.registry::register);
}
@Override
public Registry<ISchematicFormat> getRegistry() {
return this.registry;
}
@Override
public Optional<ISchematicFormat> getByFile(File file) {
return this.registry.stream().filter(format -> format.isFileType(file)).findFirst();
}
}

View File

@ -18,71 +18,33 @@
package baritone.utils.schematic; package baritone.utils.schematic;
import baritone.api.schematic.AbstractSchematic; import baritone.api.schematic.AbstractSchematic;
import net.minecraft.block.BlockAir; import baritone.api.schematic.IStaticSchematic;
import net.minecraft.block.state.IBlockState; import net.minecraft.block.state.IBlockState;
import java.util.List; import java.util.List;
import java.util.OptionalInt;
import java.util.function.Predicate;
/** /**
* Default implementation of {@link IStaticSchematic}
*
* @author Brady * @author Brady
* @since 12/23/2019 * @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; 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 @Override
public IBlockState desiredState(int x, int y, int z, IBlockState current, List<IBlockState> approxPlaceable) { public IBlockState desiredState(int x, int y, int z, IBlockState current, List<IBlockState> approxPlaceable) {
return this.states[x][z][y]; return this.states[x][z][y];
} }
public final int[][] getHeightMap() { @Override
if (this.heightMap == null) { public IBlockState getDirect(int x, int y, int z) {
this.heightMap = new int[this.x][this.z]; return this.states[x][z][y];
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;
} }
private static <T> OptionalInt lastIndexMatching(T[] arr, Predicate<? super T> predicate) { @Override
for (int y = arr.length - 1; y >= 0; y--) { public IBlockState[] getColumn(int x, int z) {
if (predicate.test(arr[y])) { return this.states[x][z];
return OptionalInt.of(y);
}
}
return OptionalInt.empty();
} }
} }

View File

@ -17,20 +17,21 @@
package baritone.utils.schematic.format; 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.MCEditParser;
import baritone.utils.schematic.parse.SpongeParser; import baritone.utils.schematic.parse.SpongeParser;
import org.apache.commons.io.FilenameUtils; import org.apache.commons.io.FilenameUtils;
import java.io.File; import java.io.File;
import java.util.Optional;
import java.util.stream.Stream;
/** /**
* Default implementations of {@link ISchematicFormat}
*
* @author Brady * @author Brady
* @since 12/13/2019 * @since 12/13/2019
*/ */
public enum SchematicFormat { public enum DefaultSchematicFormats implements ISchematicFormat {
/** /**
* The MCEdit schematic specification. Commonly denoted by the ".schematic" file extension. * The MCEdit schematic specification. Commonly denoted by the ".schematic" file extension.
@ -47,24 +48,18 @@ public enum SchematicFormat {
private final String extension; private final String extension;
private final ISchematicParser parser; private final ISchematicParser parser;
SchematicFormat(String extension, ISchematicParser parser) { DefaultSchematicFormats(String extension, ISchematicParser parser) {
this.extension = extension; this.extension = extension;
this.parser = parser; this.parser = parser;
} }
@Override
public final ISchematicParser getParser() { public final ISchematicParser getParser() {
return this.parser; return this.parser;
} }
public static Optional<SchematicFormat> getByFile(File schematic) { @Override
// TODO: Better identification public boolean isFileType(File file) {
// Maybe peek file contents and make a safe determination? return this.extension.equalsIgnoreCase(FilenameUtils.getExtension(file.getAbsolutePath()));
return getByExtension(FilenameUtils.getExtension(schematic.getAbsolutePath()));
}
public static Optional<SchematicFormat> getByExtension(String extension) {
return extension == null || extension.isEmpty()
? Optional.empty()
: Stream.of(values()).filter(format -> format.extension.equalsIgnoreCase(extension)).findFirst();
} }
} }

View File

@ -17,8 +17,9 @@
package baritone.utils.schematic.parse; package baritone.utils.schematic.parse;
import baritone.api.schematic.parse.ISchematicParser;
import baritone.utils.schematic.StaticSchematic; 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.Block;
import net.minecraft.block.state.IBlockState; import net.minecraft.block.state.IBlockState;
import net.minecraft.nbt.CompressedStreamTools; import net.minecraft.nbt.CompressedStreamTools;
@ -28,7 +29,7 @@ import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
/** /**
* An implementation of {@link ISchematicParser} for {@link SchematicFormat#MCEDIT} * An implementation of {@link ISchematicParser} for {@link DefaultSchematicFormats#MCEDIT}
* *
* @author Brady * @author Brady
* @since 12/16/2019 * @since 12/16/2019

View File

@ -17,8 +17,9 @@
package baritone.utils.schematic.parse; package baritone.utils.schematic.parse;
import baritone.api.schematic.parse.ISchematicParser;
import baritone.utils.schematic.StaticSchematic; import baritone.utils.schematic.StaticSchematic;
import baritone.utils.schematic.format.SchematicFormat; import baritone.utils.schematic.format.DefaultSchematicFormats;
import baritone.utils.type.VarInt; import baritone.utils.type.VarInt;
import it.unimi.dsi.fastutil.ints.Int2ObjectArrayMap; import it.unimi.dsi.fastutil.ints.Int2ObjectArrayMap;
import net.minecraft.block.Block; import net.minecraft.block.Block;
@ -37,7 +38,7 @@ import java.util.regex.Matcher;
import java.util.regex.Pattern; 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 * @author Brady
* @since 12/16/2019 * @since 12/16/2019