diff --git a/example/com/github/steveice10/mc/protocol/test/MinecraftProtocolTest.java b/example/com/github/steveice10/mc/protocol/test/MinecraftProtocolTest.java index 5236b61a..5a16a8e1 100644 --- a/example/com/github/steveice10/mc/protocol/test/MinecraftProtocolTest.java +++ b/example/com/github/steveice10/mc/protocol/test/MinecraftProtocolTest.java @@ -2,6 +2,8 @@ package com.github.steveice10.mc.protocol.test; import com.github.steveice10.mc.auth.data.GameProfile; import com.github.steveice10.mc.auth.exception.request.RequestException; +import com.github.steveice10.mc.auth.service.AuthenticationService; +import com.github.steveice10.mc.auth.service.SessionService; import com.github.steveice10.mc.protocol.MinecraftConstants; import com.github.steveice10.mc.protocol.MinecraftProtocol; import com.github.steveice10.mc.protocol.ServerLoginHandler; @@ -55,8 +57,11 @@ public class MinecraftProtocolTest { public static void main(String[] args) { if(SPAWN_SERVER) { + SessionService sessionService = new SessionService(); + sessionService.setProxy(AUTH_PROXY); + Server server = new Server(HOST, PORT, MinecraftProtocol.class, new TcpSessionFactory()); - server.setGlobalFlag(MinecraftConstants.AUTH_PROXY_KEY, AUTH_PROXY); + server.setGlobalFlag(MinecraftConstants.SESSION_SERVICE_KEY, sessionService); server.setGlobalFlag(MinecraftConstants.VERIFY_USERS_KEY, VERIFY_USERS); server.setGlobalFlag(MinecraftConstants.SERVER_INFO_BUILDER_KEY, new ServerInfoBuilder() { @Override @@ -81,7 +86,7 @@ public class MinecraftProtocolTest { 1, new String[] {"minecraft:world"}, getDimensionTag(), - "minecraft:overworld", + getOverworldTag(), "minecraft:world", 100, 0, @@ -156,9 +161,12 @@ public class MinecraftProtocolTest { } private static void status() { + SessionService sessionService = new SessionService(); + sessionService.setProxy(AUTH_PROXY); + MinecraftProtocol protocol = new MinecraftProtocol(SubProtocol.STATUS); Client client = new Client(HOST, PORT, protocol, new TcpSessionFactory(PROXY)); - client.getSession().setFlag(MinecraftConstants.AUTH_PROXY_KEY, AUTH_PROXY); + client.getSession().setFlag(MinecraftConstants.SESSION_SERVICE_KEY, sessionService); client.getSession().setFlag(MinecraftConstants.SERVER_INFO_HANDLER_KEY, new ServerInfoHandler() { @Override public void handle(Session session, ServerStatusInfo info) { @@ -190,8 +198,17 @@ public class MinecraftProtocolTest { private static void login() { MinecraftProtocol protocol = null; if(VERIFY_USERS) { + try { - protocol = new MinecraftProtocol(USERNAME, PASSWORD); + AuthenticationService authService = new AuthenticationService(); + authService.setUsername(USERNAME); + authService.setPassword(PASSWORD); + authService.setProxy(AUTH_PROXY); + authService.login(); + + // Can also use "new MinecraftProtocol(USERNAME, PASSWORD)" + // if you don't need a proxy or any other customizations. + protocol = new MinecraftProtocol(authService); System.out.println("Successfully authenticated user."); } catch(RequestException e) { e.printStackTrace(); @@ -201,8 +218,11 @@ public class MinecraftProtocolTest { protocol = new MinecraftProtocol(USERNAME); } + SessionService sessionService = new SessionService(); + sessionService.setProxy(AUTH_PROXY); + Client client = new Client(HOST, PORT, protocol, new TcpSessionFactory(PROXY)); - client.getSession().setFlag(MinecraftConstants.AUTH_PROXY_KEY, AUTH_PROXY); + client.getSession().setFlag(MinecraftConstants.SESSION_SERVICE_KEY, sessionService); client.getSession().addListener(new SessionAdapter() { @Override public void packetReceived(PacketReceivedEvent event) { @@ -230,27 +250,28 @@ public class MinecraftProtocolTest { private static CompoundTag getDimensionTag() { CompoundTag tag = new CompoundTag(""); ListTag dimensionTag = new ListTag("dimension"); - CompoundTag overworldTag = new CompoundTag(""); - overworldTag.put(new StringTag("name", "minecraft:overworld")); - overworldTag.put(new ByteTag("natural", (byte) 1)); - overworldTag.put(new FloatTag("ambient_light", 0f)); - overworldTag.put(new ByteTag("shrunk", (byte) 0)); - overworldTag.put(new ByteTag("ultrawarm", (byte) 0)); - overworldTag.put(new ByteTag("has_ceiling", (byte) 0)); - overworldTag.put(new ByteTag("has_skylight", (byte) 1)); - overworldTag.put(new ByteTag("piglin_safe", (byte) 0)); - overworldTag.put(new ByteTag("natural", (byte) 1)); - overworldTag.put(new FloatTag("ambient_light", 0)); - overworldTag.put(new StringTag("infiniburn", "minecraft:infiniburn_overworld")); - overworldTag.put(new ByteTag("respawn_anchor_works", (byte) 0)); - overworldTag.put(new ByteTag("has_skylight", (byte) 1)); - overworldTag.put(new ByteTag("bed_works", (byte) 1)); - overworldTag.put(new ByteTag("has_raids", (byte) 1)); - overworldTag.put(new IntTag("logical_height", 256)); - overworldTag.put(new ByteTag("shrunk", (byte) 0)); - overworldTag.put(new ByteTag("ultrawarm", (byte) 0)); + CompoundTag overworldTag = getOverworldTag(); dimensionTag.add(overworldTag); overworldTag.put(tag); return tag; } + + private static CompoundTag getOverworldTag() { + CompoundTag overworldTag = new CompoundTag(""); + overworldTag.put(new StringTag("name", "minecraft:overworld")); + overworldTag.put(new ByteTag("piglin_safe", (byte) 0)); + overworldTag.put(new ByteTag("natural", (byte) 1)); + overworldTag.put(new FloatTag("ambient_light", 0f)); + overworldTag.put(new StringTag("infiniburn", "minecraft:infiniburn_overworld")); + overworldTag.put(new ByteTag("respawn_anchor_works", (byte) 0)); + overworldTag.put(new ByteTag("has_skylight", (byte) 1)); + overworldTag.put(new ByteTag("bed_works", (byte) 1)); + overworldTag.put(new StringTag("effects", "minecraft:overworld")); + overworldTag.put(new ByteTag("has_raids", (byte) 1)); + overworldTag.put(new IntTag("logical_height", 256)); + overworldTag.put(new FloatTag("coordinate_scale", 1f)); + overworldTag.put(new ByteTag("ultrawarm", (byte) 0)); + overworldTag.put(new ByteTag("has_ceiling", (byte) 0)); + return overworldTag; + } } diff --git a/src/main/java/com/github/steveice10/mc/protocol/ClientListener.java b/src/main/java/com/github/steveice10/mc/protocol/ClientListener.java index 730750f7..226279c7 100644 --- a/src/main/java/com/github/steveice10/mc/protocol/ClientListener.java +++ b/src/main/java/com/github/steveice10/mc/protocol/ClientListener.java @@ -34,9 +34,11 @@ import lombok.NonNull; import javax.crypto.KeyGenerator; import javax.crypto.SecretKey; -import java.net.Proxy; import java.security.NoSuchAlgorithmException; +/** + * Handles making initial login and status requests for clients. + */ @AllArgsConstructor public class ClientListener extends SessionAdapter { private final @NonNull SubProtocol targetSubProtocol; @@ -56,9 +58,7 @@ public class ClientListener extends SessionAdapter { throw new IllegalStateException("Failed to generate shared key.", e); } - SessionService sessionService = new SessionService(); - sessionService.setProxy(event.getSession().getFlag(MinecraftConstants.AUTH_PROXY_KEY, Proxy.NO_PROXY)); - + SessionService sessionService = event.getSession().getFlag(MinecraftConstants.SESSION_SERVICE_KEY, new SessionService()); GameProfile profile = event.getSession().getFlag(MinecraftConstants.PROFILE_KEY); String serverId = sessionService.getServerId(packet.getServerId(), packet.getPublicKey(), key); String accessToken = event.getSession().getFlag(MinecraftConstants.ACCESS_TOKEN_KEY); diff --git a/src/main/java/com/github/steveice10/mc/protocol/MinecraftConstants.java b/src/main/java/com/github/steveice10/mc/protocol/MinecraftConstants.java index f42f185a..70b4dc66 100644 --- a/src/main/java/com/github/steveice10/mc/protocol/MinecraftConstants.java +++ b/src/main/java/com/github/steveice10/mc/protocol/MinecraftConstants.java @@ -1,23 +1,80 @@ package com.github.steveice10.mc.protocol; -public class MinecraftConstants { +import com.github.steveice10.mc.auth.data.GameProfile; +import com.github.steveice10.mc.auth.service.SessionService; + +/** + * Class containing various constants for Minecraft sessions. + */ +public final class MinecraftConstants { // General Constants + + /** + * Current supported game version. + */ public static final String GAME_VERSION = "1.16.2"; + + /** + * Current supported protocol version. + */ public static final int PROTOCOL_VERSION = 751; // General Key Constants + + /** + * Session flag where the user {@link GameProfile} is stored. + */ public static final String PROFILE_KEY = "profile"; - public static final String AUTH_PROXY_KEY = "auth-proxy"; + + /** + * Session flag for providing a custom {@link SessionService} instance. + */ + public static final String SESSION_SERVICE_KEY = "session-service"; // Client Key Constants + + /** + * Session flag where the user's access token is stored. Client only. + */ public static final String ACCESS_TOKEN_KEY = "access-token"; + + /** + * Session flag for providing a custom server info response handler. Client only. + */ public static final String SERVER_INFO_HANDLER_KEY = "server-info-handler"; + + /** + * Session flag for providing a custom ping time response handler. Client only. + */ public static final String SERVER_PING_TIME_HANDLER_KEY = "server-ping-time-handler"; // Server Key Constants + + /** + * Session flag for determining whether to verify users. Server only. + */ public static final String VERIFY_USERS_KEY = "verify-users"; + + /** + * Session flag for providing a custom server info response builder. Server only. + */ public static final String SERVER_INFO_BUILDER_KEY = "info-builder"; + + /** + * Session flag for providing a custom server login handler. Server only. + */ public static final String SERVER_LOGIN_HANDLER_KEY = "login-handler"; + + /** + * Session flag for storing the current ping time. Server only. + */ public static final String PING_KEY = "ping"; + + /** + * Session flag for determining the packet compression threshold. Server only. + */ public static final String SERVER_COMPRESSION_THRESHOLD = "compression-threshold"; + + private MinecraftConstants() { + } } diff --git a/src/main/java/com/github/steveice10/mc/protocol/MinecraftProtocol.java b/src/main/java/com/github/steveice10/mc/protocol/MinecraftProtocol.java index cfbd0ddb..48c29a00 100644 --- a/src/main/java/com/github/steveice10/mc/protocol/MinecraftProtocol.java +++ b/src/main/java/com/github/steveice10/mc/protocol/MinecraftProtocol.java @@ -175,6 +175,9 @@ import java.security.GeneralSecurityException; import java.security.Key; import java.util.UUID; +/** + * Implements the Minecraft protocol. + */ @NoArgsConstructor(access = AccessLevel.PRIVATE) public class MinecraftProtocol extends PacketProtocol { private SubProtocol subProtocol = SubProtocol.HANDSHAKE; @@ -182,17 +185,36 @@ public class MinecraftProtocol extends PacketProtocol { private AESEncryption encryption = null; private SubProtocol targetSubProtocol; + + /** + * The player's identity. + */ @Getter private GameProfile profile = null; + + /** + * Authentication client token. + */ @Getter private String clientToken = ""; + + /** + * Authentication access token. + */ @Getter private String accessToken = ""; + /** + * Whether to add the default client and server listeners for performing initial login. + */ @Getter @Setter private boolean useDefaultListeners = true; + /** + * Constructs a new MinecraftProtocol instance, starting with a specific {@link SubProtocol}. + * @param subProtocol {@link SubProtocol} to start with. + */ public MinecraftProtocol(SubProtocol subProtocol) { if(subProtocol != SubProtocol.LOGIN && subProtocol != SubProtocol.STATUS) { throw new IllegalArgumentException("Only login and status modes are permitted."); @@ -204,46 +226,51 @@ public class MinecraftProtocol extends PacketProtocol { } } + /** + * Constructs a new MinecraftProtocol instance, adopting the given username. + * @param username Username to adopt. + */ public MinecraftProtocol(String username) { this(SubProtocol.LOGIN); - this.profile = new GameProfile((UUID) null, username); } + /** + * Constructs a new MinecraftProtocol instance, logging in with the given password credentials. + * @param username Username to log in as. Must be the same as when the access token was generated. + * @param password Password to log in with. + * @throws RequestException If the log in request fails. + */ public MinecraftProtocol(String username, String password) throws RequestException { - this(username, password, Proxy.NO_PROXY); + this(createAuthServiceForPasswordLogin(username, password)); } + /** + * Constructs a new MinecraftProtocol instance, logging in with the given token credentials. + * @param username Username to log in as. Must be the same as when the access token was generated. + * @param clientToken Client token to log in as. Must be the same as when the access token was generated. + * @param accessToken Access token to log in with. + * @throws RequestException If the log in request fails. + */ public MinecraftProtocol(String username, String clientToken, String accessToken) throws RequestException { - this(username, clientToken, accessToken, Proxy.NO_PROXY); + this(createAuthServiceForTokenLogin(username, clientToken, accessToken)); } - public MinecraftProtocol(String username, String password, Proxy proxy) throws RequestException { - this(username, UUID.randomUUID().toString(), password, false, proxy); - } - - public MinecraftProtocol(String username, String clientToken, String accessToken, Proxy proxy) throws RequestException { - this(username, clientToken, accessToken, true, proxy); - } - - private MinecraftProtocol(String username, String clientToken, String using, boolean token, Proxy authProxy) throws RequestException { - this(SubProtocol.LOGIN); - - AuthenticationService auth = new AuthenticationService(clientToken); - auth.setProxy(authProxy); - auth.setUsername(username); - if(token) { - auth.setAccessToken(using); - } else { - auth.setPassword(using); - } - - auth.login(); - this.profile = auth.getSelectedProfile(); - this.clientToken = auth.getClientToken(); - this.accessToken = auth.getAccessToken(); + /** + * Constructs a new MinecraftProtocol instance, copying authentication information + * from a logged-in {@link AuthenticationService}. + * @param authService {@link AuthenticationService} to copy from. + */ + public MinecraftProtocol(AuthenticationService authService) { + this(authService.getSelectedProfile(), authService.getClientToken(), authService.getAccessToken()); } + /** + * Constructs a new MinecraftProtocol from authentication information. + * @param profile GameProfile to use. + * @param clientToken Client token to use. + * @param accessToken Access token to use. + */ public MinecraftProtocol(GameProfile profile, String clientToken, String accessToken) { this(SubProtocol.LOGIN); this.profile = profile; @@ -251,6 +278,22 @@ public class MinecraftProtocol extends PacketProtocol { this.accessToken = accessToken; } + private static AuthenticationService createAuthServiceForPasswordLogin(String username, String password) throws RequestException { + AuthenticationService auth = new AuthenticationService(UUID.randomUUID().toString()); + auth.setUsername(username); + auth.setPassword(password); + auth.login(); + return auth; + } + + private static AuthenticationService createAuthServiceForTokenLogin(String username, String clientToken, String accessToken) throws RequestException { + AuthenticationService auth = new AuthenticationService(clientToken); + auth.setUsername(username); + auth.setAccessToken(accessToken); + auth.login(); + return auth; + } + @Override public String getSRVRecordPrefix() { return "_minecraft"; @@ -297,6 +340,10 @@ public class MinecraftProtocol extends PacketProtocol { } } + /** + * Gets the current {@link SubProtocol} the client is in. + * @return The current {@link SubProtocol}. + */ public SubProtocol getSubProtocol() { return this.subProtocol; } diff --git a/src/main/java/com/github/steveice10/mc/protocol/ServerListener.java b/src/main/java/com/github/steveice10/mc/protocol/ServerListener.java index f4b17b29..52ea7095 100644 --- a/src/main/java/com/github/steveice10/mc/protocol/ServerListener.java +++ b/src/main/java/com/github/steveice10/mc/protocol/ServerListener.java @@ -31,7 +31,6 @@ import com.github.steveice10.packetlib.event.session.PacketSentEvent; import com.github.steveice10.packetlib.event.session.SessionAdapter; import javax.crypto.SecretKey; -import java.net.Proxy; import java.security.KeyPair; import java.security.KeyPairGenerator; import java.security.NoSuchAlgorithmException; @@ -40,6 +39,9 @@ import java.util.Arrays; import java.util.Random; import java.util.UUID; +/** + * Handles initial login and status requests for servers. + */ public class ServerListener extends SessionAdapter { private static final int DEFAULT_COMPRESSION_THRESHOLD = 256; @@ -190,9 +192,7 @@ public class ServerListener extends SessionAdapter { public void run() { GameProfile profile = null; if(this.key != null) { - SessionService sessionService = new SessionService(); - sessionService.setProxy(this.session.getFlag(MinecraftConstants.AUTH_PROXY_KEY, Proxy.NO_PROXY)); - + SessionService sessionService = this.session.getFlag(MinecraftConstants.SESSION_SERVICE_KEY, new SessionService()); try { profile = sessionService.getProfileByServer(username, sessionService.getServerId(SERVER_ID, KEY_PAIR.getPublic(), this.key)); } catch(RequestException e) { diff --git a/src/main/java/com/github/steveice10/mc/protocol/ServerLoginHandler.java b/src/main/java/com/github/steveice10/mc/protocol/ServerLoginHandler.java index a9ceda74..066b0f1a 100644 --- a/src/main/java/com/github/steveice10/mc/protocol/ServerLoginHandler.java +++ b/src/main/java/com/github/steveice10/mc/protocol/ServerLoginHandler.java @@ -1,7 +1,15 @@ package com.github.steveice10.mc.protocol; +import com.github.steveice10.mc.protocol.data.SubProtocol; import com.github.steveice10.packetlib.Session; +/** + * Interface for handling a session logging in to a server. + */ public interface ServerLoginHandler { + /** + * Called when a session completes the initial login process and is now in the {@link SubProtocol}.GAME. + * @param session Session that logged in. + */ public void loggedIn(Session session); }