From ebde6f26e9e06975727c4c3bb12d260fad5dd097 Mon Sep 17 00:00:00 2001 From: Brady Date: Wed, 8 Aug 2018 03:35:29 -0500 Subject: [PATCH] Created MemoryBehavior Includes basic memory of inventories that have been interacted with. --- build.gradle | 2 +- src/main/java/baritone/bot/Baritone.java | 2 + .../bot/behavior/impl/MemoryBehavior.java | 185 ++++++++++++++++++ .../bot/event/events/PacketEvent.java | 15 +- .../launch/mixins/MixinNetworkManager.java | 36 +++- 5 files changed, 233 insertions(+), 7 deletions(-) create mode 100644 src/main/java/baritone/bot/behavior/impl/MemoryBehavior.java diff --git a/build.gradle b/build.gradle index ce6d058c..9159f4d9 100755 --- a/build.gradle +++ b/build.gradle @@ -65,7 +65,7 @@ repositories { } dependencies { - implementation ('org.spongepowered:mixin:0.7.10-SNAPSHOT') { + implementation ('org.spongepowered:mixin:0.7.11-SNAPSHOT') { // Mixin includes a lot of dependencies that are too up-to-date exclude module: 'launchwrapper' exclude module: 'guava' diff --git a/src/main/java/baritone/bot/Baritone.java b/src/main/java/baritone/bot/Baritone.java index 19887260..26aac506 100755 --- a/src/main/java/baritone/bot/Baritone.java +++ b/src/main/java/baritone/bot/Baritone.java @@ -19,6 +19,7 @@ package baritone.bot; import baritone.bot.behavior.Behavior; import baritone.bot.behavior.impl.LookBehavior; +import baritone.bot.behavior.impl.MemoryBehavior; import baritone.bot.behavior.impl.PathingBehavior; import java.util.ArrayList; @@ -61,6 +62,7 @@ public enum Baritone { this.behaviors = new ArrayList<>(); behaviors.add(PathingBehavior.INSTANCE); behaviors.add(LookBehavior.INSTANCE); + behaviors.add(MemoryBehavior.INSTANCE); this.active = true; this.initialized = true; diff --git a/src/main/java/baritone/bot/behavior/impl/MemoryBehavior.java b/src/main/java/baritone/bot/behavior/impl/MemoryBehavior.java new file mode 100644 index 00000000..5803fb78 --- /dev/null +++ b/src/main/java/baritone/bot/behavior/impl/MemoryBehavior.java @@ -0,0 +1,185 @@ +package baritone.bot.behavior.impl; + +import baritone.bot.behavior.Behavior; +import baritone.bot.event.events.PacketEvent; +import net.minecraft.item.ItemStack; +import net.minecraft.network.Packet; +import net.minecraft.network.play.client.CPacketCloseWindow; +import net.minecraft.network.play.client.CPacketPlayerTryUseItemOnBlock; +import net.minecraft.network.play.server.SPacketCloseWindow; +import net.minecraft.network.play.server.SPacketOpenWindow; +import net.minecraft.tileentity.TileEntity; +import net.minecraft.tileentity.TileEntityLockable; +import net.minecraft.util.math.BlockPos; + +import java.util.*; + +/** + * @author Brady + * @since 8/6/2018 9:47 PM + */ +public class MemoryBehavior extends Behavior { + + public static MemoryBehavior INSTANCE = new MemoryBehavior(); + + private MemoryBehavior() {} + + /** + * Possible future inventories that we will be able to remember + */ + private final List futureInventories = new ArrayList<>(); + + /** + * The current remembered inventories + */ + private final Map rememberedInventories = new HashMap<>(); + + @Override + public void onPlayerUpdate() { + updateInventory(); + } + + @Override + public void onSendPacket(PacketEvent event) { + Packet p = event.getPacket(); + + switch (event.getState()) { + case PRE: { + if (p instanceof CPacketPlayerTryUseItemOnBlock) { + CPacketPlayerTryUseItemOnBlock packet = event.cast(); + + TileEntity tileEntity = world().getTileEntity(packet.getPos()); + + // Ensure the TileEntity is a container of some sort + if (tileEntity instanceof TileEntityLockable) { + + TileEntityLockable lockable = (TileEntityLockable) tileEntity; + int size = lockable.getSizeInventory(); + + this.futureInventories.add(new FutureInventory(System.currentTimeMillis(), size, lockable.getGuiID(), tileEntity.getPos())); + } + } + + if (p instanceof CPacketCloseWindow) { + updateInventory(); + } + break; + } + } + } + + @Override + public void onReceivePacket(PacketEvent event) { + Packet p = event.getPacket(); + + switch (event.getState()) { + case PRE: { + if (p instanceof SPacketOpenWindow) { + SPacketOpenWindow packet = event.cast(); + + // Remove any entries that were created over a second ago, this should make up for INSANE latency + this.futureInventories.removeIf(i -> System.currentTimeMillis() - i.time > 1000); + + this.futureInventories.stream() + .filter(i -> i.type.equals(packet.getGuiId()) && i.slots == packet.getSlotCount()) + .findFirst().ifPresent(matched -> { + // Remove the future inventory + this.futureInventories.remove(matched); + + // Setup the remembered inventory + RememberedInventory inventory = this.rememberedInventories.computeIfAbsent(matched.pos, pos -> new RememberedInventory()); + inventory.windowId = packet.getWindowId(); + inventory.size = packet.getSlotCount(); + }); + } + + if (p instanceof SPacketCloseWindow) { + updateInventory(); + } + break; + } + } + } + + private Optional getInventoryFromWindow(int windowId) { + return this.rememberedInventories.values().stream().filter(i -> i.windowId == windowId).findFirst(); + } + + private void updateInventory() { + getInventoryFromWindow(player().openContainer.windowId).ifPresent(inventory -> { + inventory.items.clear(); + inventory.items.addAll(player().openContainer.getInventory().subList(0, inventory.size)); + }); + } + + public final RememberedInventory getInventoryByPos(BlockPos pos) { + return this.rememberedInventories.get(pos); + } + + /** + * An inventory that we are not yet fully aware of, but are expecting to exist at some point in the future. + */ + private static final class FutureInventory { + + /** + * The time that we initially expected the inventory to be provided, in milliseconds + */ + private final long time; + + /** + * The amount of slots in the inventory + */ + private final int slots; + + /** + * The type of inventory + */ + private final String type; + + /** + * The position of the inventory container + */ + private final BlockPos pos; + + private FutureInventory(long time, int slots, String type, BlockPos pos) { + this.time = time; + this.slots = slots; + this.type = type; + this.pos = pos; + } + } + + /** + * An inventory that we are aware of. + *

+ * Associated with a {@link BlockPos} in {@link MemoryBehavior#rememberedInventories}. + */ + public static class RememberedInventory { + + /** + * The list of items in the inventory + */ + private final List items; + + /** + * The last known window ID of the inventory + */ + private int windowId; + + /** + * The size of the inventory + */ + private int size; + + private RememberedInventory() { + this.items = new ArrayList<>(); + } + + /** + * @return The list of items in the inventory + */ + public final List getItems() { + return this.items; + } + } +} diff --git a/src/main/java/baritone/bot/event/events/PacketEvent.java b/src/main/java/baritone/bot/event/events/PacketEvent.java index 239e1551..c2bf15c0 100644 --- a/src/main/java/baritone/bot/event/events/PacketEvent.java +++ b/src/main/java/baritone/bot/event/events/PacketEvent.java @@ -17,6 +17,7 @@ package baritone.bot.event.events; +import baritone.bot.event.events.type.EventState; import net.minecraft.network.Packet; /** @@ -25,13 +26,25 @@ import net.minecraft.network.Packet; */ public final class PacketEvent { + private final EventState state; + private final Packet packet; - public PacketEvent(Packet packet) { + public PacketEvent(EventState state, Packet packet) { + this.state = state; this.packet = packet; } + public final EventState getState() { + return this.state; + } + public final Packet getPacket() { return this.packet; } + + @SuppressWarnings("unchecked") + public final > T cast() { + return (T) this.packet; + } } diff --git a/src/main/java/baritone/launch/mixins/MixinNetworkManager.java b/src/main/java/baritone/launch/mixins/MixinNetworkManager.java index f1535d7f..793748e2 100644 --- a/src/main/java/baritone/launch/mixins/MixinNetworkManager.java +++ b/src/main/java/baritone/launch/mixins/MixinNetworkManager.java @@ -19,12 +19,15 @@ package baritone.launch.mixins; import baritone.bot.Baritone; import baritone.bot.event.events.PacketEvent; +import baritone.bot.event.events.type.EventState; +import io.netty.channel.Channel; import io.netty.channel.ChannelHandlerContext; import io.netty.util.concurrent.Future; import io.netty.util.concurrent.GenericFutureListener; import net.minecraft.network.NetworkManager; import net.minecraft.network.Packet; import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Shadow; import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.Inject; import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; @@ -34,14 +37,26 @@ import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; * @since 8/6/2018 9:30 PM */ @Mixin(NetworkManager.class) -public class MixinNetworkManager { +public abstract class MixinNetworkManager { + + @Shadow private Channel channel; + + @Shadow protected abstract void channelRead0(ChannelHandlerContext p_channelRead0_1_, Packet p_channelRead0_2_) throws Exception; @Inject( method = "dispatchPacket", at = @At("HEAD") ) - private void dispatchPacket(Packet inPacket, final GenericFutureListener>[] futureListeners, CallbackInfo ci) { - Baritone.INSTANCE.getGameEventHandler().onSendPacket(new PacketEvent(inPacket)); + private void preDispatchPacket(Packet inPacket, final GenericFutureListener>[] futureListeners, CallbackInfo ci) { + Baritone.INSTANCE.getGameEventHandler().onSendPacket(new PacketEvent(EventState.PRE, inPacket)); + } + + @Inject( + method = "dispatchPacket", + at = @At("RETURN") + ) + private void postDispatchPacket(Packet inPacket, final GenericFutureListener>[] futureListeners, CallbackInfo ci) { + Baritone.INSTANCE.getGameEventHandler().onSendPacket(new PacketEvent(EventState.POST, inPacket)); } @Inject( @@ -49,9 +64,20 @@ public class MixinNetworkManager { at = @At( value = "INVOKE", target = "net/minecraft/network/Packet.processPacket(Lnet/minecraft/network/INetHandler;)V" - ) + ), + remap = false ) private void preProcessPacket(ChannelHandlerContext context, Packet packet, CallbackInfo ci) { - Baritone.INSTANCE.getGameEventHandler().onReceivePacket(new PacketEvent(packet)); + Baritone.INSTANCE.getGameEventHandler().onReceivePacket(new PacketEvent(EventState.PRE, packet)); + } + + @Inject( + method = "channelRead0", + at = @At("RETURN"), + remap = false + ) + private void postProcessPacket(ChannelHandlerContext context, Packet packet, CallbackInfo ci) { + if (this.channel.isOpen()) + Baritone.INSTANCE.getGameEventHandler().onReceivePacket(new PacketEvent(EventState.POST, packet)); } }