diff --git a/src/main/java/baritone/bot/chunk/CachedChunk.java b/src/main/java/baritone/bot/chunk/CachedChunk.java
new file mode 100644
index 00000000..b139eb69
--- /dev/null
+++ b/src/main/java/baritone/bot/chunk/CachedChunk.java
@@ -0,0 +1,103 @@
+package baritone.bot.chunk;
+
+import baritone.bot.pathing.util.IBlockTypeAccess;
+import baritone.bot.pathing.util.PathingBlockType;
+
+import java.util.BitSet;
+
+/**
+ * @author Brady
+ * @since 8/3/2018 1:04 AM
+ */
+public final class CachedChunk implements IBlockTypeAccess {
+
+ /**
+ * The size of the chunk data in bits. Equal to 16 KiB.
+ *
+ * Chunks are 16x16x256, each block requires 2 bits.
+ */
+ public static final int SIZE = 2 * 16 * 16 * 256;
+
+ /**
+ * The size of the chunk data in bytes. Equal to 16 KiB.
+ */
+ public static final int SIZE_IN_BYTES = SIZE / 8;
+
+ /**
+ * An array of just 0s with the length of {@link CachedChunk#SIZE_IN_BYTES}
+ */
+ public static final byte[] EMPTY_CHUNK = new byte[SIZE_IN_BYTES];
+
+ /**
+ * The chunk x coordinate
+ */
+ private final int x;
+
+ /**
+ * The chunk z coordinate
+ */
+ private final int z;
+
+ /**
+ * The actual raw data of this packed chunk.
+ *
+ * Each block is expressed as 2 bits giving a total of 16 KiB
+ */
+ private final BitSet data;
+
+ CachedChunk(int x, int z, BitSet data) {
+ if (data.size() != SIZE)
+ throw new IllegalArgumentException("BitSet of invalid length provided");
+
+ this.x = x;
+ this.z = z;
+ this.data = data;
+ }
+
+ @Override
+ public final PathingBlockType getBlockType(int x, int y, int z) {
+ int index = getPositionIndex(x, y, z);
+ return PathingBlockType.fromBits(data.get(index), data.get(index + 1));
+ }
+
+ void updateContents(BitSet data) {
+ if (data.size() > SIZE)
+ throw new IllegalArgumentException("BitSet of invalid length provided");
+
+ for (int i = 0; i < data.length(); i++)
+ this.data.set(i, data.get(i));
+ }
+
+ /**
+ * @return Thee chunk x coordinat
+ */
+ public final int getX() {
+ return this.x;
+ }
+
+ /**
+ * @return The chunk z coordinate
+ */
+ public final int getZ() {
+ return this.z;
+ }
+
+ /**
+ * @return Returns the raw packed chunk data as a byte array
+ */
+ public final byte[] toByteArray() {
+ return this.data.toByteArray();
+ }
+
+ /**
+ * Returns the raw bit index of the specified position
+ *
+ * @param x The x position
+ * @param y The y position
+ * @param z The z position
+ * @return The bit index
+ */
+ public static int getPositionIndex(int x, int y, int z) {
+ return (x + (z << 4) + (y << 8)) * 2;
+ }
+}
diff --git a/src/main/java/baritone/bot/chunk/CachedRegion.java b/src/main/java/baritone/bot/chunk/CachedRegion.java
new file mode 100644
index 00000000..9be288f0
--- /dev/null
+++ b/src/main/java/baritone/bot/chunk/CachedRegion.java
@@ -0,0 +1,141 @@
+package baritone.bot.chunk;
+
+import baritone.bot.pathing.util.PathingBlockType;
+import baritone.bot.utils.GZIPUtils;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.Arrays;
+import java.util.BitSet;
+
+/**
+ * @author Brady
+ * @since 8/3/2018 9:35 PM
+ */
+public final class CachedRegion implements ICachedChunkAccess {
+
+ /**
+ * All of the chunks in this region. A 16x16 array of them.
+ *
+ * I would make these 32x32 regions to be in line with the Anvil format, but 16 is a nice number.
+ */
+ private final CachedChunk[][] chunks = new CachedChunk[32][32];
+
+ /**
+ * The region x coordinate
+ */
+ private final int x;
+
+ /**
+ * The region z coordinate
+ */
+ private final int z;
+
+ CachedRegion(int x, int z) {
+ this.x = x;
+ this.z = z;
+ }
+
+ @Override
+ public final PathingBlockType getBlockType(int x, int y, int z) {
+ CachedChunk chunk = this.getChunk(x >> 4, z >> 4);
+ if (chunk != null) {
+ return chunk.getBlockType(x, y, z);
+ }
+ return null;
+ }
+
+ @Override
+ public final void updateCachedChunk(int chunkX, int chunkZ, BitSet data) {
+ CachedChunk chunk = this.getChunk(chunkX, chunkZ);
+ if (chunk == null)
+ this.chunks[chunkX][chunkZ] = new CachedChunk(chunkX, chunkZ, data);
+ else
+ chunk.updateContents(data);
+ }
+
+ private CachedChunk getChunk(int chunkX, int chunkZ) {
+ return this.chunks[chunkX][chunkZ];
+ }
+
+ public final void save(String directory) {
+ try {
+ Path path = Paths.get(directory);
+ if (!Files.exists(path))
+ Files.createDirectories(path);
+
+ ByteArrayOutputStream bos = new ByteArrayOutputStream(32 * 32 * CachedChunk.SIZE_IN_BYTES);
+ for (int z = 0; z < 32; z++) {
+ for (int x = 0; x < 32; x++) {
+ CachedChunk chunk = this.chunks[x][z];
+ if (chunk == null) {
+ bos.write(CachedChunk.EMPTY_CHUNK);
+ } else {
+ byte[] chunkBytes = chunk.toByteArray();
+ bos.write(chunkBytes);
+ // Messy, but fills the empty 0s that should be trailing to fill up the space.
+ bos.write(new byte[CachedChunk.SIZE_IN_BYTES - chunkBytes.length]);
+ }
+ }
+ }
+
+ Path regionFile = getRegionFile(path, this.x, this.z);
+ if (!Files.exists(regionFile))
+ Files.createFile(regionFile);
+
+ byte[] compressed = GZIPUtils.compress(bos.toByteArray());
+ if (compressed != null)
+ Files.write(regionFile, compressed);
+ } catch (IOException ignored) {}
+ }
+
+ public void load(String directory) {
+ try {
+ Path path = Paths.get(directory);
+ if (!Files.exists(path))
+ Files.createDirectories(path);
+
+ Path regionFile = getRegionFile(path, this.x, this.z);
+ if (!Files.exists(regionFile))
+ return;
+
+ byte[] fileBytes = Files.readAllBytes(regionFile);
+ byte[] decompressed = GZIPUtils.decompress(fileBytes);
+ if (decompressed == null)
+ return;
+
+ for (int z = 0; z < 32; z++) {
+ for (int x = 0; x < 32; x++) {
+ CachedChunk chunk = this.chunks[x][z];
+ if (chunk != null) {
+ int index = (x + (z << 5)) * CachedChunk.SIZE_IN_BYTES;
+ byte[] bytes = Arrays.copyOfRange(decompressed, index, index + CachedChunk.SIZE_IN_BYTES);
+ BitSet bits = BitSet.valueOf(bytes);
+ chunk.updateContents(bits);
+ }
+ }
+ }
+ } catch (IOException ignored) {}
+ }
+
+ /**
+ * @return The region x coordinate
+ */
+ public final int getX() {
+ return this.x;
+ }
+
+ /**
+ * @return The region z coordinate
+ */
+ public final int getZ() {
+ return this.z;
+ }
+
+ private static Path getRegionFile(Path cacheDir, int regionX, int regionZ) {
+ return Paths.get(cacheDir.toString() + "\\r." + regionX + "." + regionZ + ".bcr");
+ }
+}
diff --git a/src/main/java/baritone/bot/chunk/CachedWorld.java b/src/main/java/baritone/bot/chunk/CachedWorld.java
new file mode 100644
index 00000000..35aad603
--- /dev/null
+++ b/src/main/java/baritone/bot/chunk/CachedWorld.java
@@ -0,0 +1,119 @@
+package baritone.bot.chunk;
+
+import baritone.bot.pathing.util.PathingBlockType;
+import it.unimi.dsi.fastutil.longs.Long2ObjectMap;
+import it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap;
+
+import java.util.BitSet;
+
+/**
+ * @author Brady
+ * @since 8/4/2018 12:02 AM
+ */
+public final class CachedWorld implements ICachedChunkAccess {
+
+ /**
+ * The maximum number of regions in any direction from (0,0)
+ */
+ private static final int REGION_MAX = 117188;
+
+ /**
+ * A map of all of the cached regions.
+ */
+ private Long2ObjectMap cachedRegions = new Long2ObjectOpenHashMap<>();
+
+ /**
+ * The directory that the cached region files are saved to
+ */
+ private final String directory;
+
+ public CachedWorld(String directory) {
+ this.directory = directory;
+ // Insert an invalid region element
+ cachedRegions.put(0, null);
+ }
+
+ @Override
+ public final PathingBlockType getBlockType(int x, int y, int z) {
+ CachedRegion region = getRegion(x >> 9, z >> 9);
+ if (region != null) {
+ return region.getBlockType(x, y, z);
+ }
+ return null;
+ }
+
+ @Override
+ public final void updateCachedChunk(int chunkX, int chunkZ, BitSet data) {
+ CachedRegion region = getOrCreateRegion(chunkX >> 5, chunkZ >> 5);
+ if (region != null) {
+ region.updateCachedChunk(chunkX & 31, chunkZ & 31, data);
+ }
+ }
+
+ public final void save() {
+ this.cachedRegions.values().forEach(region -> {
+ if (region != null)
+ region.save(this.directory);
+ });
+ }
+
+ public final void load() {
+ this.cachedRegions.values().forEach(region -> {
+ if (region != null)
+ region.load(this.directory);
+ });
+ }
+
+ /**
+ * Returns the region at the specified region coordinates
+ *
+ * @param regionX The region X coordinate
+ * @param regionZ The region Z coordinate
+ * @return The region located at the specified coordinates
+ */
+ public final CachedRegion getRegion(int regionX, int regionZ) {
+ return cachedRegions.get(getRegionID(regionX, regionZ));
+ }
+
+ /**
+ * Returns the region at the specified region coordinates. If a
+ * region is not found, then a new one is created.
+ *
+ * @param regionX The region X coordinate
+ * @param regionZ The region Z coordinate
+ * @return The region located at the specified coordinates
+ */
+ private CachedRegion getOrCreateRegion(int regionX, int regionZ) {
+ return cachedRegions.computeIfAbsent(getRegionID(regionX, regionZ), id -> {
+ CachedRegion newRegion = new CachedRegion(regionX, regionZ);
+ newRegion.load(this.directory);
+ return newRegion;
+ });
+ }
+
+ /**
+ * Returns the region ID based on the region coordinates. 0 will be
+ * returned if the specified region coordinates are out of bounds.
+ *
+ * @param regionX The region X coordinate
+ * @param regionZ The region Z coordinate
+ * @return The region ID
+ */
+ private long getRegionID(int regionX, int regionZ) {
+ if (!isRegionInWorld(regionX, regionZ))
+ return 0;
+
+ return (long) regionX & 0xFFFFFFFFL | ((long) regionZ & 0xFFFFFFFFL) << 32;
+ }
+
+ /**
+ * Returns whether or not the specified region coordinates is within the world bounds.
+ *
+ * @param regionX The region X coordinate
+ * @param regionZ The region Z coordinate
+ * @return Whether or not the region is in world bounds
+ */
+ private boolean isRegionInWorld(int regionX, int regionZ) {
+ return regionX <= REGION_MAX && regionX >= -REGION_MAX && regionZ <= REGION_MAX && regionZ >= -REGION_MAX;
+ }
+}
diff --git a/src/main/java/baritone/bot/chunk/CachedWorldProvider.java b/src/main/java/baritone/bot/chunk/CachedWorldProvider.java
new file mode 100644
index 00000000..fb2e69fd
--- /dev/null
+++ b/src/main/java/baritone/bot/chunk/CachedWorldProvider.java
@@ -0,0 +1,63 @@
+package baritone.bot.chunk;
+
+import baritone.bot.utils.Helper;
+import baritone.launch.mixins.accessor.IAnvilChunkLoader;
+import baritone.launch.mixins.accessor.IChunkProviderServer;
+import net.minecraft.client.multiplayer.WorldClient;
+import net.minecraft.server.integrated.IntegratedServer;
+import net.minecraft.world.WorldServer;
+
+import java.io.File;
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.function.Consumer;
+
+/**
+ * @author Brady
+ * @since 8/4/2018 11:06 AM
+ */
+public enum CachedWorldProvider implements Helper {
+
+ INSTANCE;
+
+ private final Map singlePlayerWorldCache = new HashMap<>();
+
+ private CachedWorld currentWorld;
+
+ public final CachedWorld getCurrentWorld() {
+ return this.currentWorld;
+ }
+
+ public final void initWorld(WorldClient world) {
+ IntegratedServer integratedServer;
+ if ((integratedServer = mc.getIntegratedServer()) != null) {
+
+ WorldServer localServerWorld = integratedServer.getWorld(world.provider.getDimensionType().getId());
+ IChunkProviderServer provider = (IChunkProviderServer) localServerWorld.getChunkProvider();
+ IAnvilChunkLoader loader = (IAnvilChunkLoader) provider.getChunkLoader();
+
+ Path dir = new File(new File(loader.getChunkSaveLocation(), "region"), "cache").toPath();
+ if (!Files.exists(dir)) {
+ try {
+ Files.createDirectories(dir);
+ } catch (IOException ignored) {}
+ }
+
+ this.currentWorld = this.singlePlayerWorldCache.computeIfAbsent(dir.toString(), CachedWorld::new);
+ this.currentWorld.load();
+ }
+ // TODO: Store server worlds
+ }
+
+ public final void closeWorld() {
+ this.currentWorld = null;
+ }
+
+ public final void ifWorldLoaded(Consumer currentWorldConsumer) {
+ if (this.currentWorld != null)
+ currentWorldConsumer.accept(this.currentWorld);
+ }
+}
diff --git a/src/main/java/baritone/bot/chunk/ChunkPacker.java b/src/main/java/baritone/bot/chunk/ChunkPacker.java
new file mode 100644
index 00000000..883a7969
--- /dev/null
+++ b/src/main/java/baritone/bot/chunk/ChunkPacker.java
@@ -0,0 +1,60 @@
+package baritone.bot.chunk;
+
+import baritone.bot.pathing.movement.MovementHelper;
+import baritone.bot.pathing.util.PathingBlockType;
+import baritone.bot.utils.Helper;
+import net.minecraft.block.Block;
+import net.minecraft.block.BlockAir;
+import net.minecraft.block.state.IBlockState;
+import net.minecraft.util.math.BlockPos;
+import net.minecraft.world.chunk.Chunk;
+
+import java.util.BitSet;
+
+import static net.minecraft.block.Block.NULL_AABB;
+
+/**
+ * @author Brady
+ * @since 8/3/2018 1:09 AM
+ */
+public final class ChunkPacker implements Helper {
+
+ private ChunkPacker() {}
+
+ public static BitSet createPackedChunk(Chunk chunk) {
+ BitSet bitSet = new BitSet(CachedChunk.SIZE);
+ try {
+ for (int y = 0; y < 256; y++) {
+ for (int z = 0; z < 16; z++) {
+ for (int x = 0; x < 16; x++) {
+ int index = CachedChunk.getPositionIndex(x, y, z);
+ boolean[] bits = getPathingBlockType(new BlockPos(x, y, z), chunk.getBlockState(x, y, z)).getBits();
+ bitSet.set(index, bits[0]);
+ bitSet.set(index + 1, bits[1]);
+ }
+ }
+ }
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ return bitSet;
+ }
+
+ private static PathingBlockType getPathingBlockType(BlockPos pos, IBlockState state) {
+ Block block = state.getBlock();
+
+ if (MovementHelper.isWater(block)) {
+ return PathingBlockType.WATER;
+ }
+
+ if (MovementHelper.avoidWalkingInto(block)) {
+ return PathingBlockType.AVOID;
+ }
+
+ if (block instanceof BlockAir || state.getCollisionBoundingBox(mc.world, pos) == NULL_AABB) {
+ return PathingBlockType.AIR;
+ }
+
+ return PathingBlockType.SOLID;
+ }
+}
diff --git a/src/main/java/baritone/bot/chunk/ICachedChunkAccess.java b/src/main/java/baritone/bot/chunk/ICachedChunkAccess.java
new file mode 100644
index 00000000..93aac861
--- /dev/null
+++ b/src/main/java/baritone/bot/chunk/ICachedChunkAccess.java
@@ -0,0 +1,14 @@
+package baritone.bot.chunk;
+
+import baritone.bot.pathing.util.IBlockTypeAccess;
+
+import java.util.BitSet;
+
+/**
+ * @author Brady
+ * @since 8/4/2018 1:10 AM
+ */
+public interface ICachedChunkAccess extends IBlockTypeAccess {
+
+ void updateCachedChunk(int chunkX, int chunkZ, BitSet data);
+}
diff --git a/src/main/java/baritone/bot/event/events/WorldEvent.java b/src/main/java/baritone/bot/event/events/WorldEvent.java
new file mode 100644
index 00000000..8b219131
--- /dev/null
+++ b/src/main/java/baritone/bot/event/events/WorldEvent.java
@@ -0,0 +1,40 @@
+package baritone.bot.event.events;
+
+import baritone.bot.event.events.type.EventState;
+import net.minecraft.client.multiplayer.WorldClient;
+
+/**
+ * @author Brady
+ * @since 8/4/2018 3:13 AM
+ */
+public final class WorldEvent {
+
+ /**
+ * The new world that is being loaded. {@code null} if being unloaded.
+ */
+ private final WorldClient world;
+
+ /**
+ * The state of the event
+ */
+ private final EventState state;
+
+ public WorldEvent(WorldClient world, EventState state) {
+ this.world = world;
+ this.state = state;
+ }
+
+ /**
+ * @return The new world that is being loaded. {@code null} if being unloaded.
+ */
+ public final WorldClient getWorld() {
+ return this.world;
+ }
+
+ /**
+ * @return The state of the event
+ */
+ public final EventState getState() {
+ return this.state;
+ }
+}
diff --git a/src/main/java/baritone/bot/pathing/util/IBlockTypeAccess.java b/src/main/java/baritone/bot/pathing/util/IBlockTypeAccess.java
new file mode 100644
index 00000000..07db4ce4
--- /dev/null
+++ b/src/main/java/baritone/bot/pathing/util/IBlockTypeAccess.java
@@ -0,0 +1,17 @@
+package baritone.bot.pathing.util;
+
+import baritone.bot.utils.Helper;
+import net.minecraft.util.math.BlockPos;
+
+/**
+ * @author Brady
+ * @since 8/4/2018 2:01 AM
+ */
+public interface IBlockTypeAccess extends Helper {
+
+ PathingBlockType getBlockType(int x, int y, int z);
+
+ default PathingBlockType getBlockType(BlockPos pos) {
+ return getBlockType(pos.getX(), pos.getY(), pos.getZ());
+ }
+}
diff --git a/src/main/java/baritone/bot/pathing/util/PathingBlockType.java b/src/main/java/baritone/bot/pathing/util/PathingBlockType.java
new file mode 100644
index 00000000..433bb580
--- /dev/null
+++ b/src/main/java/baritone/bot/pathing/util/PathingBlockType.java
@@ -0,0 +1,35 @@
+package baritone.bot.pathing.util;
+
+/**
+ * @author Brady
+ * @since 8/4/2018 1:11 AM
+ */
+public enum PathingBlockType {
+
+ AIR (0b00),
+ WATER(0b01),
+ AVOID(0b10),
+ SOLID(0b11);
+
+ private final boolean[] bits;
+
+ PathingBlockType(int bits) {
+ this.bits = new boolean[] {
+ (bits & 0b10) != 0,
+ (bits & 0b01) != 0
+ };
+ }
+
+ public final boolean[] getBits() {
+ return this.bits;
+ }
+
+ public static PathingBlockType fromBits(boolean b1, boolean b2) {
+ for (PathingBlockType type : values())
+ if (type.bits[0] == b1 && type.bits[1] == b2)
+ return type;
+
+ // This will never happen, but if it does, assume it's just AIR
+ return PathingBlockType.AIR;
+ }
+}
diff --git a/src/main/java/baritone/launch/mixins/MixinChunkProviderServer.java b/src/main/java/baritone/launch/mixins/MixinChunkProviderServer.java
new file mode 100644
index 00000000..ef1aa344
--- /dev/null
+++ b/src/main/java/baritone/launch/mixins/MixinChunkProviderServer.java
@@ -0,0 +1,16 @@
+package baritone.launch.mixins;
+
+import net.minecraft.world.chunk.storage.IChunkLoader;
+import net.minecraft.world.gen.ChunkProviderServer;
+import org.spongepowered.asm.mixin.Mixin;
+import org.spongepowered.asm.mixin.gen.Accessor;
+
+/**
+ * @author Brady
+ * @since 8/4/2018 11:33 AM
+ */
+@Mixin(ChunkProviderServer.class)
+public interface MixinChunkProviderServer {
+
+ @Accessor IChunkLoader getChunkLoader();
+}
diff --git a/src/main/java/baritone/launch/mixins/MixinMinecraft.java b/src/main/java/baritone/launch/mixins/MixinMinecraft.java
index 526fd31e..4dbc40bf 100755
--- a/src/main/java/baritone/launch/mixins/MixinMinecraft.java
+++ b/src/main/java/baritone/launch/mixins/MixinMinecraft.java
@@ -1,7 +1,10 @@
package baritone.launch.mixins;
import baritone.bot.Baritone;
+import baritone.bot.event.events.WorldEvent;
+import baritone.bot.event.events.type.EventState;
import net.minecraft.client.Minecraft;
+import net.minecraft.client.multiplayer.WorldClient;
import net.minecraft.item.ItemStack;
import net.minecraft.util.EnumActionResult;
import net.minecraft.util.math.BlockPos;
@@ -21,8 +24,8 @@ import org.spongepowered.asm.mixin.injection.callback.LocalCapture;
@Mixin(Minecraft.class)
public class MixinMinecraft {
- @Shadow
- private int leftClickCounter;
+ @Shadow private int leftClickCounter;
+ @Shadow public WorldClient world;
@Inject(
method = "init",
@@ -99,4 +102,38 @@ public class MixinMinecraft {
bot.getMemory().scanBlock(pos.offset(mc.objectMouseOver.sideHit));
bot.getActionHandler().onPlacedBlock(stack, pos);
}
+
+ @Inject(
+ method = "loadWorld(Lnet/minecraft/client/multiplayer/WorldClient;Ljava/lang/String;)V",
+ at = @At("HEAD")
+ )
+ private void preLoadWorld(WorldClient world, String loadingMessage, CallbackInfo ci) {
+ // If we're unloading the world but one doesn't exist, ignore it
+ if (this.world == null && world == null)
+ return;
+
+ Baritone.INSTANCE.getGameEventHandler().onWorldEvent(
+ new WorldEvent(
+ world,
+ EventState.PRE
+ )
+ );
+ }
+
+ @Inject(
+ method = "loadWorld(Lnet/minecraft/client/multiplayer/WorldClient;Ljava/lang/String;)V",
+ at = @At("RETURN")
+ )
+ private void postLoadWorld(WorldClient world, String loadingMessage, CallbackInfo ci) {
+ // If we're unloading the world but one doesn't exist, ignore it
+ if (this.world == null && world == null)
+ return;
+
+ Baritone.INSTANCE.getGameEventHandler().onWorldEvent(
+ new WorldEvent(
+ world,
+ EventState.POST
+ )
+ );
+ }
}
diff --git a/src/main/java/baritone/launch/mixins/accessor/IAnvilChunkLoader.java b/src/main/java/baritone/launch/mixins/accessor/IAnvilChunkLoader.java
new file mode 100644
index 00000000..ca19337b
--- /dev/null
+++ b/src/main/java/baritone/launch/mixins/accessor/IAnvilChunkLoader.java
@@ -0,0 +1,17 @@
+package baritone.launch.mixins.accessor;
+
+import net.minecraft.world.chunk.storage.AnvilChunkLoader;
+import org.spongepowered.asm.mixin.Mixin;
+import org.spongepowered.asm.mixin.gen.Accessor;
+
+import java.io.File;
+
+/**
+ * @author Brady
+ * @since 8/4/2018 11:36 AM
+ */
+@Mixin(AnvilChunkLoader.class)
+public interface IAnvilChunkLoader {
+
+ @Accessor File getChunkSaveLocation();
+}
diff --git a/src/main/java/baritone/launch/mixins/accessor/IChunkProviderServer.java b/src/main/java/baritone/launch/mixins/accessor/IChunkProviderServer.java
new file mode 100644
index 00000000..455ce22d
--- /dev/null
+++ b/src/main/java/baritone/launch/mixins/accessor/IChunkProviderServer.java
@@ -0,0 +1,16 @@
+package baritone.launch.mixins.accessor;
+
+import net.minecraft.world.chunk.storage.IChunkLoader;
+import net.minecraft.world.gen.ChunkProviderServer;
+import org.spongepowered.asm.mixin.Mixin;
+import org.spongepowered.asm.mixin.gen.Accessor;
+
+/**
+ * @author Brady
+ * @since 8/4/2018 11:33 AM
+ */
+@Mixin(ChunkProviderServer.class)
+public interface IChunkProviderServer {
+
+ @Accessor IChunkLoader getChunkLoader();
+}
diff --git a/src/main/resources/mixins.baritone.json b/src/main/resources/mixins.baritone.json
index fa8c9b9a..24bb4c64 100755
--- a/src/main/resources/mixins.baritone.json
+++ b/src/main/resources/mixins.baritone.json
@@ -14,6 +14,9 @@
"MixinMain",
"MixinMinecraft",
"MixinNetHandlerPlayClient",
- "MixinWorldClient"
+ "MixinWorldClient",
+
+ "accessor.IAnvilChunkLoader",
+ "accessor.IChunkProviderServer"
]
}
\ No newline at end of file