diff --git a/src/main/java/baritone/utils/schematic/MapArtSchematic.java b/src/main/java/baritone/utils/schematic/MapArtSchematic.java index 33ec3a15..ec477b57 100644 --- a/src/main/java/baritone/utils/schematic/MapArtSchematic.java +++ b/src/main/java/baritone/utils/schematic/MapArtSchematic.java @@ -65,7 +65,7 @@ public class MapArtSchematic extends AbstractSchematic { @Override public boolean inSchematic(int x, int y, int z, IBlockState currentState) { // in map art, we only care about coordinates in or above the art - return super.inSchematic(x, y, z, currentState) && y >= heightMap[x][z]; + return this.child.inSchematic(x, y, z, currentState) && y >= heightMap[x][z]; } @Override diff --git a/src/main/java/baritone/utils/schematic/parse/SpongeParser.java b/src/main/java/baritone/utils/schematic/parse/SpongeParser.java index 1b6cb5e0..6219f148 100644 --- a/src/main/java/baritone/utils/schematic/parse/SpongeParser.java +++ b/src/main/java/baritone/utils/schematic/parse/SpongeParser.java @@ -19,16 +19,14 @@ package baritone.utils.schematic.parse; import baritone.api.schematic.AbstractSchematic; import baritone.api.schematic.ISchematic; -import baritone.api.utils.BlockOptionalMeta; import baritone.utils.schematic.format.SchematicFormat; -import io.netty.buffer.Unpooled; +import baritone.utils.type.VarInt; import it.unimi.dsi.fastutil.ints.Int2ObjectArrayMap; import net.minecraft.block.Block; import net.minecraft.block.properties.IProperty; import net.minecraft.block.state.IBlockState; import net.minecraft.nbt.CompressedStreamTools; import net.minecraft.nbt.NBTTagCompound; -import net.minecraft.network.PacketBuffer; import net.minecraft.util.ResourceLocation; import java.io.IOException; @@ -97,17 +95,18 @@ public enum SpongeParser implements ISchematicParser { palette.put(index, state); } - // BlockData is stored as an NBT byte[], however, the actual data that is represented is a varint[]. - // This is kind of a hacky approach but it works /shrug + // BlockData is stored as an NBT byte[], however, the actual data that is represented is a varint[] byte[] rawBlockData = nbt.getByteArray("BlockData"); int[] blockData = new int[this.x * this.y * this.z]; - PacketBuffer buffer = new PacketBuffer(Unpooled.wrappedBuffer(rawBlockData)); + int offset = 0; for (int i = 0; i < blockData.length; i++) { - if (buffer.readableBytes() > 0) { - blockData[i] = buffer.readVarInt(); - } else { - throw new IllegalArgumentException("Buffer has no remaining bytes"); + if (offset >= blockData.length) { + throw new IllegalArgumentException("No remaining bytes in BlockData for complete schematic"); } + + VarInt varInt = VarInt.read(rawBlockData, offset); + blockData[i] = varInt.getValue(); + offset += varInt.getSize(); } for (int y = 0; y < this.y; y++) { @@ -144,35 +143,18 @@ public enum SpongeParser implements ISchematicParser { this.properties = properties; } - ResourceLocation getResourceLocation() { - return this.resourceLocation; - } - - Map getProperties() { - return this.properties; - } - IBlockState deserialize() { if (this.blockState == null) { - // Get the base state for the block specified - this.blockState = Block.REGISTRY.getObject(this.resourceLocation).getDefaultState(); + Block block = Block.REGISTRY.getObject(this.resourceLocation); + this.blockState = block.getDefaultState(); - // AFAIK it is best to order the property keys so that Minecraft caches the Block States ideally this.properties.keySet().stream().sorted(String::compareTo).forEachOrdered(key -> { - // getProperty(String) when lol - IProperty property = this.blockState.getPropertyKeys().stream() - .filter(p -> p.getName().equals(key)) - .findFirst().orElseThrow(IllegalArgumentException::new); - - Optional value = property.parseValue(this.properties.get(key)).toJavaUtil(); - if (value.isPresent()) { - this.blockState = this.blockState.withProperty( - BlockOptionalMeta.castToIProperty(property), - BlockOptionalMeta.castToIPropertyValue(property, value) - ); - } else { - throw new IllegalArgumentException(); + IProperty property = block.getBlockState().getProperty(key); + if (property == null) { + throw new IllegalArgumentException("Invalid property"); } + + this.blockState = setPropertyValue(this.blockState, property, this.properties.get(key)); }); } return this.blockState; @@ -203,5 +185,14 @@ public enum SpongeParser implements ISchematicParser { return null; } } + + 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); + } + } } } diff --git a/src/main/java/baritone/utils/type/VarInt.java b/src/main/java/baritone/utils/type/VarInt.java new file mode 100644 index 00000000..8eb31e7f --- /dev/null +++ b/src/main/java/baritone/utils/type/VarInt.java @@ -0,0 +1,94 @@ +/* + * 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.type; + +import it.unimi.dsi.fastutil.bytes.ByteArrayList; +import it.unimi.dsi.fastutil.bytes.ByteList; + +/** + * @author Brady + * @since 12/19/2019 + */ +public final class VarInt { + + private final int value; + private final byte[] serialized; + private final int size; + + public VarInt(int value) { + this.value = value; + this.serialized = serialize0(this.value); + this.size = this.serialized.length; + } + + /** + * @return The integer value that is represented by this {@link VarInt}. + */ + public final int getValue() { + return this.value; + } + + /** + * @return The size of this {@link VarInt}, in bytes, once serialized. + */ + public final int getSize() { + return this.size; + } + + public final byte[] serialize() { + return this.serialized; + } + + private static byte[] serialize0(int value) { + ByteList bytes = new ByteArrayList(); + + while ((value & 0xFF) != 0) { + bytes.add((byte) (value & 0x7F | 0x80)); + value >>>= 7; + } + bytes.add((byte) (value & 0xFF)); + + return bytes.toByteArray(); + } + + public static VarInt read(byte[] bytes) { + return read(bytes, 0); + } + + public static VarInt read(byte[] bytes, int start) { + int value = 0; + int size = 0; + int index = start; + + while (true) { + byte b = bytes[index++]; + value |= (b & 0x7F) << size++ * 7; + + if (size > 5) { + throw new IllegalArgumentException("VarInt size cannot exceed 5 bytes"); + } + + // Most significant bit denotes another byte is to be read. + if ((b & 0x80) != 0x80) { + break; + } + } + + return new VarInt(value); + } +}