diff --git a/src/api/java/baritone/api/bot/IUserManager.java b/src/api/java/baritone/api/bot/IUserManager.java index 7d326c6e0..25ccaea80 100644 --- a/src/api/java/baritone/api/bot/IUserManager.java +++ b/src/api/java/baritone/api/bot/IUserManager.java @@ -17,7 +17,9 @@ package baritone.api.bot; +import baritone.api.bot.connect.ConnectionStatus; import baritone.api.bot.connect.IConnectionResult; +import baritone.api.event.events.TickEvent; import com.mojang.authlib.GameProfile; import net.minecraft.util.Session; @@ -32,7 +34,9 @@ import java.util.UUID; public interface IUserManager { /** - * Connects a new user with the specified {@link Session} to the current server. + * Connects a new user with the specified {@link Session} to the current server. Returns + * a {@link IConnectionResult} describing the result of the attempted connection as well + * as a {@link IBaritoneUser} instance if it was {@link ConnectionStatus#SUCCESS}. * * @param session The user session * @return The result of the attempted connection @@ -40,7 +44,8 @@ public interface IUserManager { IConnectionResult connect(Session session); /** - * Disconnects the specified {@link IBaritoneUser} from its current server. + * Disconnects the specified {@link IBaritoneUser} from its current server. All valid users + * are automatically disconnected when the current game state becomes {@link TickEvent.Type#OUT}. * * @param user The user to disconnect */ @@ -53,7 +58,9 @@ public interface IUserManager { * @return The user, {@link Optional#empty()} if no match or {@code profile} is {@code null} */ default Optional getUserByProfile(GameProfile profile) { - return profile == null ? Optional.empty() : users().stream().filter(user -> user.getProfile().equals(profile)).findFirst(); + return profile == null + ? Optional.empty() + : this.getUsers().stream().filter(user -> user.getProfile().equals(profile)).findFirst(); } /** @@ -63,11 +70,13 @@ public interface IUserManager { * @return The user, {@link Optional#empty()} if no match or {@code uuid} is {@code null} */ default Optional getUserByUUID(UUID uuid) { - return uuid == null ? Optional.empty() : users().stream().filter(user -> user.getProfile().getId().equals(uuid)).findFirst(); + return uuid == null + ? Optional.empty() + : this.getUsers().stream().filter(user -> user.getProfile().getId().equals(uuid)).findFirst(); } /** * @return All of the users held by this manager */ - List users(); + List getUsers(); } diff --git a/src/main/java/baritone/BaritoneProvider.java b/src/main/java/baritone/BaritoneProvider.java index 1efc0b677..a7933516e 100644 --- a/src/main/java/baritone/BaritoneProvider.java +++ b/src/main/java/baritone/BaritoneProvider.java @@ -58,7 +58,7 @@ public final class BaritoneProvider implements IBaritoneProvider { public List getAllBaritones() { List baritones = new ArrayList<>(); baritones.add(getPrimaryBaritone()); - for (IBaritoneUser ibu : UserManager.INSTANCE.users()) { + for (IBaritoneUser ibu : UserManager.INSTANCE.getUsers()) { baritones.add(ibu.getBaritone()); } return baritones; diff --git a/src/main/java/baritone/bot/BaritoneUser.java b/src/main/java/baritone/bot/BaritoneUser.java index 12aa9eb83..04fb7440f 100644 --- a/src/main/java/baritone/bot/BaritoneUser.java +++ b/src/main/java/baritone/bot/BaritoneUser.java @@ -20,8 +20,8 @@ package baritone.bot; import baritone.Baritone; import baritone.api.IBaritone; import baritone.api.bot.IBaritoneUser; -import baritone.api.bot.IUserManager; import baritone.api.utils.IPlayerController; +import baritone.bot.spec.BotMinecraft; import baritone.bot.spec.BotWorld; import baritone.bot.spec.EntityBot; import com.mojang.authlib.GameProfile; @@ -44,6 +44,7 @@ public class BaritoneUser implements IBaritoneUser { private GameProfile profile; private INetHandlerPlayClient netHandlerPlayClient; + private BotMinecraft mc; private BotWorld world; private EntityBot player; private IPlayerController playerController; @@ -51,6 +52,7 @@ public class BaritoneUser implements IBaritoneUser { private final Baritone baritone; BaritoneUser(UserManager manager, NetworkManager networkManager, Session session) { + this.mc = BotMinecraft.allocate(this); this.manager = manager; this.networkManager = networkManager; this.session = session; @@ -107,4 +109,8 @@ public class BaritoneUser implements IBaritoneUser { public IBaritone getBaritone() { return baritone; } + + public BotMinecraft getMinecraft() { + return this.mc; + } } diff --git a/src/main/java/baritone/bot/UserManager.java b/src/main/java/baritone/bot/UserManager.java index 81f3d5ca5..4f2daa51d 100644 --- a/src/main/java/baritone/bot/UserManager.java +++ b/src/main/java/baritone/bot/UserManager.java @@ -151,14 +151,16 @@ public final class UserManager implements IUserManager, Helper { @Override 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); + if (this.users.contains(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); + } } @Override - public final List users() { + public final List getUsers() { return Collections.unmodifiableList(this.users); } } diff --git a/src/main/java/baritone/bot/handler/BotNetHandlerLoginClient.java b/src/main/java/baritone/bot/handler/BotNetHandlerLoginClient.java index 96ae4c0a3..b34bd47f7 100644 --- a/src/main/java/baritone/bot/handler/BotNetHandlerLoginClient.java +++ b/src/main/java/baritone/bot/handler/BotNetHandlerLoginClient.java @@ -62,52 +62,16 @@ public class BotNetHandlerLoginClient extends NetHandlerLoginClient { private final BaritoneUser user; public BotNetHandlerLoginClient(NetworkManager networkManager, BaritoneUser user) { - super(networkManager, Minecraft.getMinecraft(), null); + super(networkManager, user.getMinecraft(), null); this.networkManager = networkManager; - this.mc = Minecraft.getMinecraft(); + this.mc = user.getMinecraft(); this.user = user; } - @Override - public void handleEncryptionRequest(SPacketEncryptionRequest packetIn) { - SecretKey secretkey = CryptManager.createNewSharedKey(); - PublicKey publicKey = packetIn.getPublicKey(); - - // Setup joinServer payload info - GameProfile profile = this.user.getSession().getProfile(); - String authenticationToken = this.user.getSession().getToken(); - String serverId = new BigInteger(CryptManager.getServerIdHash(packetIn.getServerId(), publicKey, secretkey)).toString(16); - - if (this.mc.getCurrentServerData() != null && this.mc.getCurrentServerData().isOnLAN()) { - try { - this.mc.getSessionService().joinServer(profile, authenticationToken, serverId); - } catch (AuthenticationException e) { - // Couldn't connect to auth servers but will continue to join LAN - } - } else { - try { - this.mc.getSessionService().joinServer(profile, authenticationToken, serverId); - } catch (AuthenticationUnavailableException e) { - this.networkManager.closeChannel(new TextComponentTranslation("disconnect.loginFailedInfo", new TextComponentTranslation("disconnect.loginFailedInfo.serversUnavailable"))); - return; - } catch (InvalidCredentialsException e) { - this.networkManager.closeChannel(new TextComponentTranslation("disconnect.loginFailedInfo", new TextComponentTranslation("disconnect.loginFailedInfo.invalidSession"))); - return; - } catch (AuthenticationException e) { - this.networkManager.closeChannel(new TextComponentTranslation("disconnect.loginFailedInfo", e.getMessage())); - return; - } - } - - // noinspection unchecked - this.networkManager.sendPacket(new CPacketEncryptionResponse(secretkey, publicKey, packetIn.getVerifyToken()), - future -> BotNetHandlerLoginClient.this.networkManager.enableEncryption(secretkey)); - } - @Override public void handleLoginSuccess(SPacketLoginSuccess packetIn) { this.networkManager.setConnectionState(EnumConnectionState.PLAY); - this.networkManager.setNetHandler(new BotNetHandlerPlayClient(this.networkManager, this.user, Minecraft.getMinecraft(), packetIn.getProfile())); + this.networkManager.setNetHandler(new BotNetHandlerPlayClient(this.networkManager, this.user, this.mc, packetIn.getProfile())); } @Override diff --git a/src/main/java/baritone/bot/spec/BotMinecraft.java b/src/main/java/baritone/bot/spec/BotMinecraft.java new file mode 100644 index 000000000..e54aaab92 --- /dev/null +++ b/src/main/java/baritone/bot/spec/BotMinecraft.java @@ -0,0 +1,73 @@ +/* + * This file is part of Baritone. + * + * Baritone is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Baritone is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Baritone. If not, see . + */ + +package baritone.bot.spec; + +import baritone.api.bot.IBaritoneUser; +import baritone.api.utils.Helper; +import baritone.utils.ObjectAllocator; +import com.google.common.util.concurrent.ListenableFuture; +import com.mojang.authlib.minecraft.MinecraftSessionService; +import net.minecraft.client.Minecraft; +import net.minecraft.client.main.GameConfiguration; +import net.minecraft.client.multiplayer.ServerData; +import net.minecraft.util.Session; + +import javax.annotation.Nonnull; +import javax.annotation.Nullable; +import java.util.concurrent.Callable; + +/** + * @author Brady + * @since 3/3/2020 + */ +public final class BotMinecraft extends Minecraft implements Helper { + + private IBaritoneUser user; + + private BotMinecraft(GameConfiguration gameConfig) { + super(gameConfig); + } + + @Nonnull + @Override + public Session getSession() { + return this.user.getSession(); + } + + @Nullable + @Override + public ServerData getCurrentServerData() { + return mc.getCurrentServerData(); + } + + @Override + public MinecraftSessionService getSessionService() { + return mc.getSessionService(); + } + + @Override + public ListenableFuture addScheduledTask(Callable callableToSchedule) { + return mc.addScheduledTask(callableToSchedule); + } + + public static BotMinecraft allocate(IBaritoneUser user) { + BotMinecraft mc = ObjectAllocator.allocate(BotMinecraft.class); + mc.user = user; + return mc; + } +} diff --git a/src/main/java/baritone/event/GameEventHandler.java b/src/main/java/baritone/event/GameEventHandler.java index 7027d2e2b..feaf96385 100644 --- a/src/main/java/baritone/event/GameEventHandler.java +++ b/src/main/java/baritone/event/GameEventHandler.java @@ -68,9 +68,6 @@ public final class GameEventHandler implements IEventBus, Helper { @Override public final void onSendChatMessage(ChatEvent event) { - // Ensure UserManager is created to prevent a ConcurrentModificationException - Objects.requireNonNull(UserManager.INSTANCE); - listeners.forEach(l -> l.onSendChatMessage(event)); } diff --git a/src/main/java/baritone/utils/ObjectAllocator.java b/src/main/java/baritone/utils/ObjectAllocator.java new file mode 100644 index 000000000..b63406c38 --- /dev/null +++ b/src/main/java/baritone/utils/ObjectAllocator.java @@ -0,0 +1,55 @@ +/* + * This file is part of Baritone. + * + * Baritone is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Baritone is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Baritone. If not, see . + */ + +package baritone.utils; + +import sun.misc.Unsafe; + +import java.lang.reflect.Field; + +/** + * Hacky util to allocate objects without needing to invoke their constructor. + * + * @author Brady + * @since 3/3/2020 + */ +public final class ObjectAllocator { + + private static final Unsafe theUnsafe; + + static { + try { + Class clazz = Class.forName("sun.misc.Unsafe"); + Field field = clazz.getDeclaredField("theUnsafe"); + field.setAccessible(true); + theUnsafe = (Unsafe) field.get(null); + } catch (Exception e) { + throw new RuntimeException(e); + } + } + + private ObjectAllocator() {} + + public static T allocate(Class clazz) { + try { + // noinspection unchecked + return (T) theUnsafe.allocateInstance(clazz); + } catch (Exception e) { + throw new RuntimeException(e); + } + } +}