protocol_v3 0.1.5

MMOSG Protocol version 3 - an extensible websocket protocol for online games
Documentation
const protocol = {
    defaultConfig: {
        types: {
            "String": { // encode and decode work with regular JS arrays of bytes, then it's converted to Uint8Array.
                // this is just an implementation of what we have over in the rust program
                encode(string) { // THANKS, STACKOVERFLOW
                    var utf8 = unescape(encodeURIComponent(string));
                    var arr = [Math.floor(string.length / 256), string.length % 256]; // convert length to a big endian (network order) byte array
                    for (var i = 0; i < utf8.length; i++) {
                        arr.push(utf8.charCodeAt(i)); // push in the actual data
                    }
                    return arr;
                },
                decode(bytes) {
                    var length = bytes.shift() * 256 + bytes.shift(); // go from two big endian bytes (network order, duh) to js number length; probably LE. endianness is cursed.
                    // just use big endian for everything, dipshits
                    return new TextDecoder().decode(new Uint8Array(bytes.splice(0, length)));
                }
            },
            "u8": {
                encode(data) {
                    return [data];
                },
                decode(bytes) {
                    return bytes.shift();
                }
            },
            "u16": {
                encode(data) {
                    return [Math.floor(data / 256), data % 256];
                },
                decode(bytes) {
                    return bytes.shift() * 256 + bytes.shift();
                }
            },
            "f32": {
                decode(bytes) { // THANKS, STACKOVERFLOW
                    var buf = new ArrayBuffer(4);
                    var view = new DataView(buf);
                    for (var x = 0; x < 4; x++) {
                        view.setUint8(x, bytes.shift());
                    }
                    return view.getFloat32(0);
                },
                encode(data) {
                    var buf = new ArrayBuffer(4);
                    var view = new DataView(buf);
                    view.setFloat32(0, data);
                    var ret = [];
                    for (var x = 0; x < 4; x++) {
                        ret.push(view.getUint8(x));
                    }
                    return ret;
                }
            },
            "bool": {
                decode(bytes) {
                    return bytes.shift() != 0;
                },
                encode(data) {
                    return [data ? 1 : 0];
                }
            }
        }
    },
    async connectV3(config, uri, secure = false) { // TODO: make this handle URIs better, right now it makes a lot of assumptions
        let manifest = await (await fetch(secure ? "https" : "http" + "://" + uri + "/manifest")).json();
        console.log(manifest);
        var obj = {
            appname: manifest.application_name,
            toServer: manifest.incoming_protocol,
            fromServer: manifest.outgoing_protocol,
            socket: new WebSocket(secure ? "wss" : "ws" + "://" + uri),
            sendHandle(name) {
                var op = undefined;
                var socket = this.socket;
                this.toServer.operations.forEach(item => {
                    if (item.name == name) {
                        op = item;
                    }
                });
                return (...args) => {
                    var out = [op.opcode];
                    for (var i = 0; i < op.args.length; i++) {
                        out.push(...config.types[op.args[i]].encode(args[i]));
                    }
                    socket.send(new Uint8Array(out));
                }
            },
            listen(listener) {
                this.socket.addEventListener("message", (msgdata) => {
                    var bytearray = Array.from(new Uint8Array(msgdata.data));
                    var opcode = bytearray.shift();
                    var type = undefined;
                    this.fromServer.operations.forEach(op => {
                        if (op.opcode == opcode) {
                            type = op;
                        }
                    });
                    if (type) {
                        var retProps = [];
                        type.args.forEach(rtype => {
                            retProps.push(config.types[rtype].decode(bytearray));
                        });
                        listener(type.name, retProps);
                    }
                    else {
                        console.warn("Invalid operation code " + opcode);
                    }
                });
            },
            onOpen(callback) {
                this.socket.addEventListener("open", callback);
            }
        };
        obj.socket.binaryType = "arraybuffer";
        return obj;
    }
}