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