~starkingdoms/starkingdoms

ref: cc06268120ce59ec3fce2bcedbc5594f26a86a10 starkingdoms/client/src/gateway.ts -rw-r--r-- 3.3 KiB
cc062681 — c0repwn3r basic handshake with server 2 years ago
                                                                                
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
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);
        }
    });
}