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/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/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/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/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/process/BuilderProcess.java b/src/main/java/baritone/process/BuilderProcess.java
index 1766af62..c1c0cb1d 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;
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..84b9b86e
--- /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.floor((Math.log(amountOfBlockTypes)) / Math.log(2)) + 1;
+ }
+
+ /**
+ * 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;
+ }
+}