diff --git a/src/main/java/baritone/bot/UserManager.java b/src/main/java/baritone/bot/UserManager.java index 8af4ca76..2fa23f15 100644 --- a/src/main/java/baritone/bot/UserManager.java +++ b/src/main/java/baritone/bot/UserManager.java @@ -17,6 +17,9 @@ package baritone.bot; +import baritone.Baritone; +import baritone.api.event.events.TickEvent; +import baritone.api.event.listener.AbstractGameEventListener; import baritone.bot.connect.ConnectionResult; import baritone.bot.handler.BotNetHandlerLoginClient; import baritone.utils.Helper; @@ -35,6 +38,7 @@ import java.util.ArrayList; import java.util.List; import java.util.Optional; import java.util.UUID; +import java.util.concurrent.CopyOnWriteArrayList; import static baritone.bot.connect.ConnectionStatus.*; @@ -46,9 +50,20 @@ public final class UserManager implements Helper { public static final UserManager INSTANCE = new UserManager(); - private UserManager() {} + private final List users = new CopyOnWriteArrayList<>(); - private final List users = new ArrayList<>(); + private UserManager() { + // Setup an event listener that automatically disconnects bots when we're not in-game + Baritone.INSTANCE.registerEventListener(new AbstractGameEventListener() { + + @Override + public final void onTick(TickEvent event) { + if (event.getType() == TickEvent.Type.OUT) { + UserManager.this.users.forEach(UserManager.this::disconnect); + } + } + }); + } /** * Connects a new user with the specified {@link Session} to the current server. @@ -106,7 +121,8 @@ public final class UserManager implements Helper { } /** - * Notifies the manager of an {@link IBaritoneUser} disconnect + * Notifies the manager of an {@link IBaritoneUser} disconnect, and + * removes the {@link IBaritoneUser} from the list of users. * * @param user The user that disconnected * @param state The connection state at the time of disconnect @@ -115,11 +131,35 @@ public final class UserManager implements Helper { this.users.remove(user); } - public final Optional getUserByProfile(GameProfile profile) { - return this.users.stream().filter(user -> user.getSession().getProfile().equals(profile)).findFirst(); + /** + * Disconnects the specified {@link IBaritoneUser} from its current server. + * + * @param user The user to disconnect + */ + public final void disconnect(IBaritoneUser user) { + // It's probably fine to pass null to this, because the handlers aren't doing anything with it + // noinspection ConstantConditions + user.getNetworkManager().closeChannel(null); + this.users.remove(user); } + /** + * Finds the {@link IBaritoneUser} associated with the specified {@link GameProfile} + * + * @param profile The game profile of the user + * @return The user, {@link Optional#empty()} if no match or {@code profile} is {@code null} + */ + public final Optional getUserByProfile(GameProfile profile) { + return profile == null ? Optional.empty() : this.users.stream().filter(user -> user.getProfile().equals(profile)).findFirst(); + } + + /** + * Finds the {@link IBaritoneUser} associated with the specified {@link UUID} + * + * @param uuid The uuid of the user + * @return The user, {@link Optional#empty()} if no match or {@code uuid} is {@code null} + */ public final Optional getUserByUUID(UUID uuid) { - return this.users.stream().filter(user -> user.getSession().getProfile().getId().equals(uuid)).findFirst(); + return uuid == null ? Optional.empty() : this.users.stream().filter(user -> user.getProfile().getId().equals(uuid)).findFirst(); } } diff --git a/src/main/java/baritone/bot/handler/BotNetHandlerPlayClient.java b/src/main/java/baritone/bot/handler/BotNetHandlerPlayClient.java index c5772371..045e7f3c 100644 --- a/src/main/java/baritone/bot/handler/BotNetHandlerPlayClient.java +++ b/src/main/java/baritone/bot/handler/BotNetHandlerPlayClient.java @@ -39,10 +39,13 @@ import net.minecraft.network.play.client.*; import net.minecraft.network.play.server.*; import net.minecraft.potion.Potion; import net.minecraft.potion.PotionEffect; +import net.minecraft.scoreboard.*; import net.minecraft.util.EnumHand; import net.minecraft.util.EnumHandSide; import net.minecraft.util.IThreadListener; +import net.minecraft.util.StringUtils; import net.minecraft.util.text.ITextComponent; +import net.minecraft.util.text.TextFormatting; import javax.annotation.Nonnull; @@ -117,6 +120,31 @@ public class BotNetHandlerPlayClient extends NetHandlerPlayClient { @Override public void handleScoreboardObjective(@Nonnull SPacketScoreboardObjective packetIn) { PacketThreadUtil.checkThreadAndEnqueue(packetIn, this, this.client); + + Scoreboard scoreboard = this.world.getScoreboard(); + switch (packetIn.getAction()) { + case 0: { + ScoreObjective objective = scoreboard.addScoreObjective(packetIn.getObjectiveName(), IScoreCriteria.DUMMY); + objective.setDisplayName(packetIn.getObjectiveValue()); + objective.setRenderType(packetIn.getRenderType()); + break; + } + case 1: { + ScoreObjective objective = scoreboard.getObjective(packetIn.getObjectiveName()); + if (objective != null) { + scoreboard.removeObjective(objective); + } + break; + } + case 2: { + ScoreObjective objective = scoreboard.getObjective(packetIn.getObjectiveName()); + if (objective != null) { + objective.setDisplayName(packetIn.getObjectiveName()); + objective.setRenderType(packetIn.getRenderType()); + } + break; + } + } } @Override @@ -439,11 +467,73 @@ public class BotNetHandlerPlayClient extends NetHandlerPlayClient { @Override public void handleTeams(@Nonnull SPacketTeams packetIn) { PacketThreadUtil.checkThreadAndEnqueue(packetIn, this, this.client); + + Scoreboard scoreboard = this.world.getScoreboard(); + + boolean newTeam = packetIn.getAction() == 0; + + ScorePlayerTeam team = newTeam + ? scoreboard.createTeam(packetIn.getName()) + : scoreboard.getTeam(packetIn.getName()); + + if (packetIn.getAction() == 2 || newTeam) { + team.setDisplayName(packetIn.getDisplayName()); + team.setPrefix(packetIn.getPrefix()); + team.setSuffix(packetIn.getSuffix()); + team.setColor(TextFormatting.fromColorIndex(packetIn.getColor())); + team.setFriendlyFlags(packetIn.getFriendlyFlags()); + + Team.EnumVisible visibility = Team.EnumVisible.getByName(packetIn.getNameTagVisibility()); + if (visibility != null) { + team.setNameTagVisibility(visibility); + } + + Team.CollisionRule collisionRule = Team.CollisionRule.getByName(packetIn.getCollisionRule()); + if (collisionRule != null) { + team.setCollisionRule(collisionRule); + } + } + + if (packetIn.getAction() == 3 || newTeam) { + for (String name : packetIn.getPlayers()) { + scoreboard.addPlayerToTeam(name, packetIn.getName()); + } + } + + if (packetIn.getAction() == 4) { + for (String name : packetIn.getPlayers()) { + scoreboard.removePlayerFromTeam(name, team); + } + } + + if (packetIn.getAction() == 1) { + scoreboard.removeTeam(team); + } } @Override public void handleUpdateScore(@Nonnull SPacketUpdateScore packetIn) { PacketThreadUtil.checkThreadAndEnqueue(packetIn, this, this.client); + + Scoreboard scoreboard = this.world.getScoreboard(); + ScoreObjective objective = scoreboard.getObjective(packetIn.getObjectiveName()); + + switch (packetIn.getScoreAction()) { + case CHANGE: { + if (objective != null) { + scoreboard.getOrCreateScore(packetIn.getPlayerName(), objective).setScorePoints(packetIn.getScoreValue()); + } + break; + } + case REMOVE: { + if (StringUtils.isNullOrEmpty(packetIn.getObjectiveName())) { + scoreboard.removeObjectiveFromEntity(packetIn.getPlayerName(), null); + } else if (objective != null) { + scoreboard.removeObjectiveFromEntity(packetIn.getPlayerName(), objective); + } + break; + } + } } @Override @@ -490,9 +580,9 @@ public class BotNetHandlerPlayClient extends NetHandlerPlayClient { Potion potion = Potion.getPotionById(packetIn.getEffectId()); if (potion != null) { - PotionEffect potioneffect = new PotionEffect(potion, packetIn.getDuration(), packetIn.getAmplifier(), packetIn.getIsAmbient(), packetIn.doesShowParticles()); - potioneffect.setPotionDurationMax(packetIn.isMaxDuration()); - ((EntityLivingBase) entity).addPotionEffect(potioneffect); + PotionEffect effect = new PotionEffect(potion, packetIn.getDuration(), packetIn.getAmplifier(), packetIn.getIsAmbient(), packetIn.doesShowParticles()); + effect.setPotionDurationMax(packetIn.isMaxDuration()); + ((EntityLivingBase) entity).addPotionEffect(effect); } } } diff --git a/src/main/java/baritone/event/GameEventHandler.java b/src/main/java/baritone/event/GameEventHandler.java index b42b3e06..f0e74c97 100644 --- a/src/main/java/baritone/event/GameEventHandler.java +++ b/src/main/java/baritone/event/GameEventHandler.java @@ -26,8 +26,8 @@ import baritone.utils.BlockStateInterface; import baritone.utils.Helper; import net.minecraft.world.chunk.Chunk; -import java.util.ArrayList; import java.util.List; +import java.util.concurrent.CopyOnWriteArrayList; /** * @author Brady @@ -37,7 +37,7 @@ public final class GameEventHandler implements IGameEventListener, Helper { private final Baritone baritone; - private final List listeners = new ArrayList<>(); + private final List listeners = new CopyOnWriteArrayList<>(); public GameEventHandler(Baritone baritone) { this.baritone = baritone; diff --git a/src/main/java/baritone/utils/ExampleBaritoneControl.java b/src/main/java/baritone/utils/ExampleBaritoneControl.java index 5df05f91..859000b7 100644 --- a/src/main/java/baritone/utils/ExampleBaritoneControl.java +++ b/src/main/java/baritone/utils/ExampleBaritoneControl.java @@ -467,7 +467,7 @@ public class ExampleBaritoneControl extends Behavior implements Helper { } // TODO: Temporary command to test bots offline if (msg.equals("bot")) { - UserManager.INSTANCE.connect(new Session("Lol" + System.currentTimeMillis() % 1000, UUID.randomUUID().toString(), "", "")); + UserManager.INSTANCE.connect(new Session("Bot" + System.currentTimeMillis() % 1000, UUID.randomUUID().toString(), "", "")); return true; } return false;