package launcher.request; import launcher.Launcher; import launcher.Launcher.Config; import launcher.LauncherAPI; import launcher.helper.IOHelper; import launcher.helper.SecurityHelper; import launcher.serialize.HInput; import launcher.serialize.HOutput; import launcher.serialize.stream.EnumSerializer; import launcher.serialize.stream.EnumSerializer.Itf; import java.io.IOException; import java.net.Socket; import java.util.concurrent.atomic.AtomicBoolean; public abstract class Request<R> { @LauncherAPI protected final Config config; private final AtomicBoolean started = new AtomicBoolean(false); @LauncherAPI protected Request(Config config) { this.config = config == null ? Launcher.getConfig() : config; } @LauncherAPI protected Request() { this(null); } @LauncherAPI public static void requestError(String message) throws RequestException { throw new RequestException(message); } @LauncherAPI public abstract Type getType(); @LauncherAPI protected abstract R requestDo(HInput input, HOutput output) throws Throwable; @LauncherAPI @SuppressWarnings("DesignForExtension") public R request() throws Throwable { 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 (HInput input = new HInput(IOHelper.newBufferedInputStream(socket.getInputStream())); HOutput output = new HOutput(IOHelper.newBufferedOutStream(socket.getOutputStream()))) { writeHandshake(input, output); return requestDo(input, output); } } } @LauncherAPI protected final void readError(HInput input) throws IOException { String error = input.readString(0); if (!error.isEmpty()) { requestError(error); } } private void writeHandshake(HInput input, HOutput output) throws IOException { // Write handshake output.writeInt(Launcher.PROTOCOL_MAGIC); output.writeBigInteger(config.publicKey.getModulus(), SecurityHelper.RSA_KEY_LENGTH + 1); EnumSerializer.write(output, getType()); output.flush(); // Verify is accepted if (!input.readBoolean()) { requestError("Serverside not accepted this connection"); } } @LauncherAPI public enum Type implements 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(9), // 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; } @LauncherAPI public static Type read(HInput input) throws IOException { return SERIALIZER.read(input); } @Override public int getNumber() { return n; } } }