Replace specifying proxy with specifying custom auth service clients.

This commit is contained in:
Steveice10 2020-08-29 11:16:34 -07:00
parent b3cf3acbb3
commit 6c66d7e27b
6 changed files with 194 additions and 61 deletions

View file

@ -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;
}
}

View file

@ -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);

View file

@ -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() {
}
}

View file

@ -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;
}

View file

@ -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) {

View file

@ -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);
}