import {Logger} from "./logger";
import {MessageC2SHello, MessageC2SHello_packetInfo} from "./protocol/message_c2s";
import {State} from "./protocol/state";
import {decode, encode} from "./serde";
import {
MessageS2CGoodbye,
MessageS2CGoodbye_packetInfo,
MessageS2CHello,
MessageS2CHello_packetInfo
} from "./protocol/message_s2c";
const logger = new Logger("Gateway");
export interface GatewayClient {
state: State;
socket: WebSocket;
username: string | null;
version: number | null;
}
function toHexString(byteArray) {
return Array.from(byteArray, function(byte) {
return ('0' + (byte & 0xFF).toString(16)).slice(-2);
}).join('')
}
export async function gateway_connect(gateway_url: string, username: string): GatewayClient {
logger.info("FAST CONNECT - Connecting to gateway socket at " + gateway_url);
let ws = await _websocket_connect(gateway_url);
logger.debug("[fastconnect] connected to gateway, performing handshake with server");
let client: GatewayClient = {
state: State.Handshake,
socket: ws,
username: null,
version: null
};
let handshake_start_msg = MessageC2SHello.encode({
version: 1,
requestedUsername: username,
nextState: State.Play
}).finish();
client.socket.send(encode(MessageC2SHello_packetInfo.type, handshake_start_msg));
client.socket.addEventListener('message', async (msg) => {
let pkt_info = decode(new Uint8Array(await msg.data.arrayBuffer()));
let pkt_id = pkt_info[0];
let pkt_data = pkt_info[1];
if (pkt_id == 0) {
// not a real message, skip
return;
}
if (client.state == State.Handshake) {
if (pkt_id == MessageS2CHello_packetInfo.type) {
let pkt = MessageS2CHello.decode(pkt_data);
logger.info(`FAST CONNECT - Handshake finished with server with protocol v${pkt.version}, assigned username ${pkt.givenUsername}, switching to state ${pkt.nextState}`);
client.state = pkt.nextState;
client.username = pkt.givenUsername;
client.version = pkt.version;
} else if (pkt_id == MessageS2CGoodbye_packetInfo.type) {
let pkt = MessageS2CGoodbye.decode(pkt_data);
logger.error(`Disconnected by server. Reason: ${pkt.reason}`);
client.state = State.Handshake;
client.username = null;
client.version = null;
throw "Disconnected by server";
} else {
logger.warn(`server sent unexpected packet ${pkt_id} for state Handshake`);
}
} else if (client.state == State.Play) {
// TODO
}
})
}
let socket: WebSocket | undefined = undefined;
function _websocket_connect(url: string): Promise<WebSocket> {
if (socket && socket.readyState < 2) {
// reuse socket connection
return Promise.resolve(socket);
}
return new Promise((resolve, reject) => {
socket = new WebSocket(url);
socket.onopen = () => {
// @ts-ignore if here, guaranteed that `socket` != undefined
resolve(socket);
}
socket.onerror = (err) => {
reject(err);
}
});
}