diff --git a/src/api/java/baritone/api/Settings.java b/src/api/java/baritone/api/Settings.java index 9130165f7..09fe85cf2 100644 --- a/src/api/java/baritone/api/Settings.java +++ b/src/api/java/baritone/api/Settings.java @@ -190,6 +190,15 @@ public final class Settings { ))); + /** + * A list of blocks to become air + *

+ * If a schematic asks for a block on this list, only air will be accepted at that location (and nothing on buildIgnoreBlocks) + */ + public final Setting> okIfAir = new Setting<>(new ArrayList<>(Arrays.asList( + + ))); + /** * If this is true, the builder will treat all non-air blocks as correct. It will only place new blocks. */ @@ -432,6 +441,11 @@ public final class Settings { */ public final Setting simplifyUnloadedYCoord = new Setting<>(true); + /** + * Whenever a block changes, repack the whole chunk that it's in + */ + public final Setting repackOnAnyBlockChange = new Setting<>(true); + /** * If a movement takes this many ticks more than its initial cost estimate, cancel it */ diff --git a/src/launch/java/baritone/launch/mixins/MixinClientPlayNetHandler.java b/src/launch/java/baritone/launch/mixins/MixinClientPlayNetHandler.java index e31856c4d..15766b98e 100644 --- a/src/launch/java/baritone/launch/mixins/MixinClientPlayNetHandler.java +++ b/src/launch/java/baritone/launch/mixins/MixinClientPlayNetHandler.java @@ -17,15 +17,19 @@ package baritone.launch.mixins; +import baritone.Baritone; import baritone.api.BaritoneAPI; import baritone.api.IBaritone; import baritone.api.event.events.ChunkEvent; import baritone.api.event.events.type.EventState; +import baritone.cache.CachedChunk; import net.minecraft.client.entity.player.ClientPlayerEntity; import net.minecraft.client.network.play.ClientPlayNetHandler; +import net.minecraft.network.play.server.SChangeBlockPacket; import net.minecraft.network.play.server.SChunkDataPacket; import net.minecraft.network.play.server.SCombatPacket; -import net.minecraft.network.play.server.SUnloadChunkPacket; +import net.minecraft.network.play.server.SMultiBlockChangePacket; +import net.minecraft.util.math.ChunkPos; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.Inject; @@ -83,30 +87,62 @@ public class MixinClientPlayNetHandler { } @Inject( - method = "processChunkUnload", - at = @At("HEAD") + method = "handleBlockChange", + at = @At("RETURN") ) - private void preChunkUnload(SUnloadChunkPacket packet, CallbackInfo ci) { + private void postHandleBlockChange(SChangeBlockPacket packetIn, CallbackInfo ci) { + if (!Baritone.settings().repackOnAnyBlockChange.value) { + return; + } + if (!CachedChunk.BLOCKS_TO_KEEP_TRACK_OF.contains(packetIn.getState().getBlock())) { + return; + } for (IBaritone ibaritone : BaritoneAPI.getProvider().getAllBaritones()) { ClientPlayerEntity player = ibaritone.getPlayerContext().player(); if (player != null && player.connection == (ClientPlayNetHandler) (Object) this) { ibaritone.getGameEventHandler().onChunkEvent( - new ChunkEvent(EventState.PRE, ChunkEvent.Type.UNLOAD, packet.getX(), packet.getZ()) + new ChunkEvent( + EventState.POST, + ChunkEvent.Type.POPULATE_FULL, + packetIn.getPos().getX() >> 4, + packetIn.getPos().getZ() >> 4 + ) ); } } } @Inject( - method = "processChunkUnload", + method = "handleMultiBlockChange", at = @At("RETURN") ) - private void postChunkUnload(SUnloadChunkPacket packet, CallbackInfo ci) { + private void postHandleMultiBlockChange(SMultiBlockChangePacket packetIn, CallbackInfo ci) { + if (!Baritone.settings().repackOnAnyBlockChange.value) { + return; + } + if (packetIn.getChangedBlocks().length == 0) { + return; + } + https://docs.oracle.com/javase/specs/jls/se7/html/jls-14.html#jls-14.15 + { + for (SMultiBlockChangePacket.BlockUpdateData update : packetIn.getChangedBlocks()) { + if (CachedChunk.BLOCKS_TO_KEEP_TRACK_OF.contains(update.getBlockState().getBlock())) { + break https; + } + } + return; + } + ChunkPos pos = new ChunkPos(packetIn.getChangedBlocks()[0].getPos()); for (IBaritone ibaritone : BaritoneAPI.getProvider().getAllBaritones()) { ClientPlayerEntity player = ibaritone.getPlayerContext().player(); if (player != null && player.connection == (ClientPlayNetHandler) (Object) this) { ibaritone.getGameEventHandler().onChunkEvent( - new ChunkEvent(EventState.POST, ChunkEvent.Type.UNLOAD, packet.getX(), packet.getZ()) + new ChunkEvent( + EventState.POST, + ChunkEvent.Type.POPULATE_FULL, + pos.x, + pos.z + ) ); } } diff --git a/src/main/java/baritone/cache/CachedWorld.java b/src/main/java/baritone/cache/CachedWorld.java index 3cfb8623f..634a79040 100644 --- a/src/main/java/baritone/cache/CachedWorld.java +++ b/src/main/java/baritone/cache/CachedWorld.java @@ -27,7 +27,7 @@ import it.unimi.dsi.fastutil.longs.Long2ObjectMap; import it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap; import net.minecraft.util.RegistryKey; import net.minecraft.util.math.BlockPos; -import net.minecraft.world.World; +import net.minecraft.util.math.ChunkPos; import net.minecraft.world.chunk.Chunk; import java.io.IOException; @@ -35,6 +35,8 @@ import java.nio.file.Files; import java.nio.file.Path; import java.util.ArrayList; import java.util.List; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.LinkedBlockingQueue; /** @@ -58,7 +60,17 @@ public final class CachedWorld implements ICachedWorld, Helper { */ private final String directory; - private final LinkedBlockingQueue toPack = new LinkedBlockingQueue<>(); + /** + * Queue of positions to pack. Refers to the toPackMap, in that every element of this queue will be a + * key in that map. + */ + private final LinkedBlockingQueue toPackQueue = new LinkedBlockingQueue<>(); + + /** + * All chunk positions pending packing. This map will be updated in-place if a new update to the chunk occurs + * while waiting in the queue for the packer thread to get to it. + */ + private final Map toPackMap = new ConcurrentHashMap<>(); private final RegistryKey dimension; @@ -91,10 +103,8 @@ public final class CachedWorld implements ICachedWorld, Helper { @Override public final void queueForPacking(Chunk chunk) { - try { - toPack.put(chunk); - } catch (InterruptedException e) { - e.printStackTrace(); + if (toPackMap.put(chunk.getPos(), chunk) == null) { + toPackQueue.add(chunk.getPos()); } } @@ -295,13 +305,9 @@ public final class CachedWorld implements ICachedWorld, Helper { public void run() { while (true) { - // TODO: Add CachedWorld unloading to remove the redundancy of having this - LinkedBlockingQueue queue = toPack; - if (queue == null) { - break; - } try { - Chunk chunk = queue.take(); + ChunkPos pos = toPackQueue.take(); + Chunk chunk = toPackMap.remove(pos); CachedChunk cached = ChunkPacker.pack(chunk); CachedWorld.this.updateCachedChunk(cached); //System.out.println("Processed chunk at " + chunk.x + "," + chunk.z); diff --git a/src/main/java/baritone/process/BuilderProcess.java b/src/main/java/baritone/process/BuilderProcess.java index 6f9b85bea..62cee7bf9 100644 --- a/src/main/java/baritone/process/BuilderProcess.java +++ b/src/main/java/baritone/process/BuilderProcess.java @@ -791,10 +791,13 @@ public final class BuilderProcess extends BaritoneProcessHelper implements IBuil if (desired == null) { return true; } - if (current.getBlock() instanceof AirBlock && desired.getBlock() instanceof AirBlock) { + if (current.getBlock() instanceof BlockLiquid && Baritone.settings().okIfWater.value) { return true; } - if ((current.getBlock() == Blocks.WATER || current.getBlock() == Blocks.LAVA) && Baritone.settings().okIfWater.value) { + if (current.getBlock() instanceof BlockAir && Baritone.settings().okIfAir.value.contains(desired.getBlock())) { + return true; + } + if (desired.getBlock() instanceof BlockAir && Baritone.settings().buildIgnoreBlocks.value.contains(current.getBlock())) { return true; } // TODO more complicated comparison logic I guess