Newer
Older
KeeperJerry_Launcher / Launcher / source / request / Request.java
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);
		}
	}
}