package launcher.request; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.net.Socket; import java.util.concurrent.atomic.AtomicBoolean; import launcher.Launcher; import launcher.LauncherAPI; import launcher.helper.IOHelper; import launcher.helper.SecurityHelper; import launcher.serialize.HInput; import launcher.serialize.HOutput; import launcher.serialize.stream.EnumSerializer; public abstract class Request<R> { private final AtomicBoolean started = new AtomicBoolean(false); @LauncherAPI protected final Launcher.Config config; @LauncherAPI protected Request(Launcher.Config config) { this.config = config == null ? Launcher.getConfig() : config; } @LauncherAPI protected Request() { this(null); } @LauncherAPI public abstract Type getType(); @LauncherAPI @SuppressWarnings("DesignForExtension") public R request() throws Exception { if (!started.compareAndSet(false, true)) { throw new IllegalStateException("Request already started"); } // Make request to LaunchServer try (Socket socket = IOHelper.newSocket()) { socket.connect(IOHelper.resolve(config.address)); try (InputStream is = socket.getInputStream(); OutputStream os = socket.getOutputStream(); HInput input = new HInput(is); HOutput output = new HOutput(os)) { writeHandshake(input, output); // Start request EnumSerializer.write(output, getType()); output.flush(); return requestDo(input, output); } } } @LauncherAPI protected final void readError(HInput input) throws IOException { String error = input.readString(0); if (!error.isEmpty()) { throw new RequestException(error); } } @LauncherAPI protected abstract R requestDo(HInput input, HOutput output) throws Exception; private void writeHandshake(HInput input, HOutput output) throws IOException { output.writeInt(Launcher.PROTOCOL_MAGIC); // Write license & key info output.writeBigInteger(config.publicKey.getModulus(), SecurityHelper.RSA_KEY_LENGTH + 1); output.flush(); // Verify is accepted if (!input.readBoolean()) { throw new RequestException("Serverside not accepted this connection"); } } @LauncherAPI public enum Type implements EnumSerializer.Itf { PING(0), // Ping request LAUNCHER(1), UPDATE(2), UPDATE_LIST(3), // Update requests AUTH(4), JOIN_SERVER(5), CHECK_SERVER(6), // Auth requests PROFILE_BY_USERNAME(7), PROFILE_BY_UUID(8), BATCH_PROFILE_BY_USERNAME(0), // Profile requests CUSTOM(255); // Custom requests private static final EnumSerializer<Type> SERIALIZER = new EnumSerializer<>(Type.class); private final int n; Type(int n) { this.n = n; } @Override public int getNumber() { return n; } @LauncherAPI public static Type read(HInput input) throws IOException { return SERIALIZER.read(input); } } }