From c1d269b81277050649e636a8972712af790923ea Mon Sep 17 00:00:00 2001 From: ChomeNS <95471003+ChomeNS@users.noreply.github.com> Date: Tue, 18 Feb 2025 20:41:51 +0700 Subject: [PATCH] feat: FINALLY chunk parsing with actual working core complete check --- build-number.txt | 2 +- .../chomens_bot/chunk/ChunkColumn.java | 48 ++++++++++++ .../chomens_bot/data/chunk/ChunkPos.java | 4 + .../chomens_bot/plugins/CorePlugin.java | 45 ++++------- .../chomens_bot/plugins/WorldPlugin.java | 74 +++++++++++++++++-- 5 files changed, 134 insertions(+), 39 deletions(-) create mode 100644 src/main/java/me/chayapak1/chomens_bot/chunk/ChunkColumn.java create mode 100644 src/main/java/me/chayapak1/chomens_bot/data/chunk/ChunkPos.java diff --git a/build-number.txt b/build-number.txt index 929e30ed..05952d91 100644 --- a/build-number.txt +++ b/build-number.txt @@ -1 +1 @@ -1519 \ No newline at end of file +1528 \ No newline at end of file diff --git a/src/main/java/me/chayapak1/chomens_bot/chunk/ChunkColumn.java b/src/main/java/me/chayapak1/chomens_bot/chunk/ChunkColumn.java new file mode 100644 index 00000000..f98605a5 --- /dev/null +++ b/src/main/java/me/chayapak1/chomens_bot/chunk/ChunkColumn.java @@ -0,0 +1,48 @@ +package me.chayapak1.chomens_bot.chunk; + +import io.netty.buffer.ByteBuf; +import io.netty.buffer.Unpooled; +import me.chayapak1.chomens_bot.data.chunk.ChunkPos; +import org.geysermc.mcprotocollib.protocol.codec.MinecraftTypes; +import org.geysermc.mcprotocollib.protocol.data.game.chunk.ChunkSection; + +// Author: hhhzzzsss +public class ChunkColumn { + public final ChunkPos pos; + public final ChunkSection[] chunks; + + private final int minY; + + public ChunkColumn (ChunkPos chunkPos, byte[] data, int worldHeight, int minY) { + this.pos = chunkPos; + this.minY = minY; + + final ByteBuf in = Unpooled.wrappedBuffer(data); + final int numSections = -Math.floorDiv(-worldHeight, 16); + + chunks = new ChunkSection[numSections]; + + for (int i = 0; i < numSections; i++) { + chunks[i] = MinecraftTypes.readChunkSection(in); + } + } + + public int getBlock (int x, int y, int z) { + int yIndex = (y - minY) >> 4; + + if (chunks[yIndex] == null) return 0; + + return chunks[yIndex].getBlock(x, y & 15, z); + } + + public void setBlock (int x, int y, int z, int id) { + int yIndex = (y - minY) >> 4; + + if (chunks[yIndex] == null) { + chunks[yIndex] = new ChunkSection(); + chunks[yIndex].setBlock(0, 0, 0, 0); + } + + chunks[yIndex].setBlock(x, y & 15, z, id); + } +} diff --git a/src/main/java/me/chayapak1/chomens_bot/data/chunk/ChunkPos.java b/src/main/java/me/chayapak1/chomens_bot/data/chunk/ChunkPos.java new file mode 100644 index 00000000..aac4de94 --- /dev/null +++ b/src/main/java/me/chayapak1/chomens_bot/data/chunk/ChunkPos.java @@ -0,0 +1,4 @@ +package me.chayapak1.chomens_bot.data.chunk; + +public record ChunkPos (int x, int z) { +} diff --git a/src/main/java/me/chayapak1/chomens_bot/plugins/CorePlugin.java b/src/main/java/me/chayapak1/chomens_bot/plugins/CorePlugin.java index c8f2e492..d0200a21 100644 --- a/src/main/java/me/chayapak1/chomens_bot/plugins/CorePlugin.java +++ b/src/main/java/me/chayapak1/chomens_bot/plugins/CorePlugin.java @@ -11,7 +11,6 @@ import org.cloudburstmc.nbt.NbtMap; import org.cloudburstmc.nbt.NbtMapBuilder; import org.geysermc.mcprotocollib.network.Session; import org.geysermc.mcprotocollib.network.event.session.DisconnectedEvent; -import org.geysermc.mcprotocollib.network.packet.Packet; import org.geysermc.mcprotocollib.protocol.data.game.entity.object.Direction; import org.geysermc.mcprotocollib.protocol.data.game.entity.player.Hand; import org.geysermc.mcprotocollib.protocol.data.game.entity.player.PlayerAction; @@ -20,11 +19,7 @@ import org.geysermc.mcprotocollib.protocol.data.game.item.component.DataComponen import org.geysermc.mcprotocollib.protocol.data.game.item.component.DataComponentType; import org.geysermc.mcprotocollib.protocol.data.game.item.component.DataComponentTypes; import org.geysermc.mcprotocollib.protocol.data.game.item.component.DataComponents; -import org.geysermc.mcprotocollib.protocol.data.game.level.block.BlockChangeEntry; import org.geysermc.mcprotocollib.protocol.data.game.level.block.CommandBlockMode; -import org.geysermc.mcprotocollib.protocol.packet.ingame.clientbound.level.ClientboundBlockUpdatePacket; -import org.geysermc.mcprotocollib.protocol.packet.ingame.clientbound.level.ClientboundLevelChunkWithLightPacket; -import org.geysermc.mcprotocollib.protocol.packet.ingame.clientbound.level.ClientboundSectionBlocksUpdatePacket; import org.geysermc.mcprotocollib.protocol.packet.ingame.serverbound.inventory.ServerboundSetCommandBlockPacket; import org.geysermc.mcprotocollib.protocol.packet.ingame.serverbound.inventory.ServerboundSetCreativeModeSlotPacket; import org.geysermc.mcprotocollib.protocol.packet.ingame.serverbound.player.ServerboundPlayerActionPacket; @@ -87,6 +82,8 @@ public class CorePlugin extends PositionPlugin.Listener { } bot.executor.scheduleAtFixedRate(() -> { + checkCoreTick(); + if (!shouldRefill) return; refill(); @@ -102,13 +99,6 @@ public class CorePlugin extends PositionPlugin.Listener { reset(); } - - @Override - public void packetReceived(Session session, Packet packet) { - if (packet instanceof ClientboundBlockUpdatePacket) CorePlugin.this.packetReceived((ClientboundBlockUpdatePacket) packet); - else if (packet instanceof ClientboundSectionBlocksUpdatePacket) CorePlugin.this.packetReceived((ClientboundSectionBlocksUpdatePacket) packet); - else if (packet instanceof ClientboundLevelChunkWithLightPacket) CorePlugin.this.packetReceived((ClientboundLevelChunkWithLightPacket) packet); - } }); bot.tick.addListener(new TickPlugin.Listener() { @@ -217,7 +207,7 @@ public class CorePlugin extends PositionPlugin.Listener { final CompletableFuture future = bot.query.block(coreBlock, "LastOutput"); future.thenApplyAsync(output -> { - if (output == null) return output; + if (output == null) return null; trackedFuture.complete( Component.join( @@ -284,29 +274,24 @@ public class CorePlugin extends PositionPlugin.Listener { session.send(new ServerboundUseItemOnPacket(temporaryBlockPosition, Direction.UP, Hand.MAIN_HAND, 0.5f, 0.5f, 0.5f, false, false, 1)); } - public void packetReceived (ClientboundBlockUpdatePacket packet) { - final BlockChangeEntry entry = packet.getEntry(); + public boolean isCoreComplete () { + if (!ready) return false; - final Vector3i position = entry.getPosition(); + for (int x = from.getX(); x <= to.getX(); x++) { + for (int y = from.getY(); y <= to.getY(); y++) { + for (int z = from.getZ(); z <= to.getZ(); z++) { + final int block = bot.world.getBlock(x, y, z); - if (isCore(position) && !isCommandBlockState(entry.getBlock())) shouldRefill = true; - } - - public void packetReceived (ClientboundSectionBlocksUpdatePacket packet) { - final BlockChangeEntry[] entries = packet.getEntries(); - - for (BlockChangeEntry entry : entries) { - final Vector3i position = entry.getPosition(); - - if (isCore(position) && !isCommandBlockState(entry.getBlock())) { - shouldRefill = true; - break; + if (!isCommandBlockState(block)) return false; + } } } + + return true; } - public void packetReceived (ClientboundLevelChunkWithLightPacket packet) { - shouldRefill = true; // worst fix + private void checkCoreTick () { + if (!isCoreComplete()) shouldRefill = true; } private boolean isCommandBlockState (int blockState) { diff --git a/src/main/java/me/chayapak1/chomens_bot/plugins/WorldPlugin.java b/src/main/java/me/chayapak1/chomens_bot/plugins/WorldPlugin.java index c513697b..d2e25dd9 100644 --- a/src/main/java/me/chayapak1/chomens_bot/plugins/WorldPlugin.java +++ b/src/main/java/me/chayapak1/chomens_bot/plugins/WorldPlugin.java @@ -1,17 +1,21 @@ package me.chayapak1.chomens_bot.plugins; +import me.chayapak1.chomens_bot.Bot; +import me.chayapak1.chomens_bot.chunk.ChunkColumn; +import me.chayapak1.chomens_bot.data.chunk.ChunkPos; +import org.cloudburstmc.math.vector.Vector3i; import org.cloudburstmc.nbt.NbtMap; import org.geysermc.mcprotocollib.network.Session; +import org.geysermc.mcprotocollib.network.event.session.DisconnectedEvent; import org.geysermc.mcprotocollib.network.packet.Packet; import org.geysermc.mcprotocollib.protocol.data.game.RegistryEntry; +import org.geysermc.mcprotocollib.protocol.data.game.level.block.BlockChangeEntry; import org.geysermc.mcprotocollib.protocol.packet.configuration.clientbound.ClientboundRegistryDataPacket; import org.geysermc.mcprotocollib.protocol.packet.ingame.clientbound.ClientboundLoginPacket; import org.geysermc.mcprotocollib.protocol.packet.ingame.clientbound.ClientboundRespawnPacket; -import me.chayapak1.chomens_bot.Bot; -import org.geysermc.mcprotocollib.protocol.packet.ingame.clientbound.level.ClientboundSetSimulationDistancePacket; +import org.geysermc.mcprotocollib.protocol.packet.ingame.clientbound.level.*; -import java.util.ArrayList; -import java.util.List; +import java.util.*; public class WorldPlugin extends Bot.Listener { public int minY = 0; @@ -19,6 +23,8 @@ public class WorldPlugin extends Bot.Listener { public int simulationDistance = 8; + private final Map chunks = new HashMap<>(); + public List registry = null; private final List listeners = new ArrayList<>(); @@ -33,6 +39,15 @@ public class WorldPlugin extends Bot.Listener { else if (packet instanceof ClientboundRespawnPacket t_packet) packetReceived(t_packet); else if (packet instanceof ClientboundRegistryDataPacket t_packet) packetReceived(t_packet); else if (packet instanceof ClientboundSetSimulationDistancePacket t_packet) packetReceived(t_packet); + else if (packet instanceof ClientboundLevelChunkWithLightPacket t_packet) packetReceived(t_packet); + else if (packet instanceof ClientboundForgetLevelChunkPacket t_packet) packetReceived(t_packet); + else if (packet instanceof ClientboundBlockUpdatePacket t_packet) packetReceived(t_packet); + else if (packet instanceof ClientboundSectionBlocksUpdatePacket t_packet) packetReceived(t_packet); + } + + @Override + public void disconnected (DisconnectedEvent event) { + chunks.clear(); } private void worldChanged (String dimension) { @@ -53,26 +68,69 @@ public class WorldPlugin extends Bot.Listener { for (Listener listener : listeners) listener.worldChanged(dimension); } - public void packetReceived (ClientboundRegistryDataPacket packet) { + private void packetReceived (ClientboundRegistryDataPacket packet) { if (!packet.getRegistry().value().equals("dimension_type")) return; registry = packet.getEntries(); } - public void packetReceived (ClientboundLoginPacket packet) { + private void packetReceived (ClientboundLoginPacket packet) { simulationDistance = packet.getSimulationDistance(); worldChanged(packet.getCommonPlayerSpawnInfo().getWorldName().asString()); } - public void packetReceived (ClientboundRespawnPacket packet) { + private void packetReceived (ClientboundRespawnPacket packet) { worldChanged(packet.getCommonPlayerSpawnInfo().getWorldName().asString()); } - public void packetReceived (ClientboundSetSimulationDistancePacket packet) { + private void packetReceived (ClientboundSetSimulationDistancePacket packet) { simulationDistance = packet.getSimulationDistance(); } + private void packetReceived (ClientboundLevelChunkWithLightPacket packet) { + final ChunkPos pos = new ChunkPos(packet.getX(), packet.getZ()); + final ChunkColumn column = new ChunkColumn(pos, packet.getChunkData(), maxY, minY); + chunks.put(pos, column); + } + + private void packetReceived (ClientboundForgetLevelChunkPacket packet) { + chunks.remove(new ChunkPos(packet.getX(), packet.getZ())); + } + + private void packetReceived (ClientboundBlockUpdatePacket packet) { + final Vector3i position = packet.getEntry().getPosition(); + final int id = packet.getEntry().getBlock(); + + setBlock(position.getX(), position.getY(), position.getZ(), id); + } + + private void packetReceived (ClientboundSectionBlocksUpdatePacket packet) { + for (BlockChangeEntry entry : packet.getEntries()) { + final Vector3i position = entry.getPosition(); + final int id = entry.getBlock(); + + setBlock(position.getX(), position.getY(), position.getZ(), id); + } + } + + public ChunkColumn getChunk (int x, int z) { return chunks.get(new ChunkPos(x, z)); } + public ChunkColumn getChunk (ChunkPos pos) { return chunks.get(pos); } + public Collection getChunks () { return chunks.values(); } + + public int getBlock (int x, int y, int z) { + final ChunkPos chunkPos = new ChunkPos(Math.floorDiv(x, 16), Math.floorDiv(z, 16)); + final ChunkColumn chunk = chunks.get(chunkPos); + return chunk == null ? 0 : chunks.get(chunkPos).getBlock(x & 15, y, z & 15); + } + + // should this be public? + public void setBlock (int x, int y, int z, int id) { + final ChunkPos chunkPos = new ChunkPos(Math.floorDiv(x, 16), Math.floorDiv(z, 16)); + if (!chunks.containsKey(chunkPos)) return; + chunks.get(chunkPos).setBlock(x & 15, y, z & 15, id); + } + public static class Listener { public void worldChanged (String dimension) {} }