"use strict";
/**
 * This version of the networking is deprecated.
 * See NetworkEngine2.ts
 */
var __importDefault = (this && this.__importDefault) || function (mod) {
    return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
const IModLoaderAPI_1 = require("modloader64_api/IModLoaderAPI");
const EventHandler_1 = require("modloader64_api/EventHandler");
const NetworkHandler_1 = require("modloader64_api/NetworkHandler");
const crypto_1 = __importDefault(require("crypto"));
const ModLoaderDefaultImpls_1 = require("modloader64_api/ModLoaderDefaultImpls");
const fs_1 = __importDefault(require("fs"));
const modloader64_1 = require("../modloader64");
const zlib_1 = __importDefault(require("zlib"));
const dgram_1 = __importDefault(require("dgram"));
const path_1 = __importDefault(require("path"));
const mluuid_1 = require("../uuid/mluuid");
const getAllFiles_1 = require("../getAllFiles");
const FakeNetworkPlayer_1 = require("./FakeNetworkPlayer");
const LobbyObjects_1 = require("./LobbyObjects");
const NetworkingEventBus_1 = require("./NetworkingEventBus");
const NetworkingPackets_1 = require("./NetworkingPackets");
let natUpnp = require('nat-upnp');
let natUpnp_client = natUpnp.createClient();
var NetworkEngine;
(function (NetworkEngine) {
    class Server {
        io;
        logger;
        masterConfig;
        config;
        modLoaderconfig;
        fakePlayer = new FakeNetworkPlayer_1.FakeNetworkPlayer();
        udpServer = dgram_1.default.createSocket('udp4');
        udpPort = -1;
        plugins = {};
        core = "";
        lobby_names = [];
        lobbyStorage = {};
        constructor(logger, config) {
            this.logger = logger;
            this.masterConfig = config;
            this.config = config.registerConfigCategory('NetworkEngine.Server');
            config.setData('NetworkEngine.Server', 'port', 8082);
            config.setData('NetworkEngine.Server', 'udpPort', 8082);
            config.setData('NetworkEngine.Server', 'patchSizeLimitMB', 10);
            this.modLoaderconfig = this.masterConfig.registerConfigCategory('ModLoader64');
            modloader64_1.internal_event_bus.on('PLUGIN_LOADED', (args) => {
                let p = args[0].meta;
                this.plugins[p.name] = { version: p.version, hash: args[0].hash };
            });
            modloader64_1.internal_event_bus.on('CORE_LOADED', (args) => {
                this.core = args[0].name;
                if (config.registerConfigCategory("NetworkEngine.Client").isSinglePlayer) {
                    this.config.port = parseInt("8082");
                    config.registerConfigCategory('ModLoader64').isServer = true;
                }
            });
            NetworkingEventBus_1.NetworkingEventBusServer.on('getLobbyStorage', (evt) => {
                evt.obj = this.getLobbyStorage(evt.lobbyName, evt.plugin);
            });
            NetworkingEventBus_1.NetworkingEventBusServer.on('createLobbyStorage', (evt) => {
                this.createLobbyStorage(evt.lobbyName, evt.plugin, evt.obj);
            });
            NetworkingEventBus_1.NetworkingEventBusServer.on('getAllLobbies', (evt) => {
                evt["r"] = this.getAllLobbies();
            });
            NetworkHandler_1.NetworkQueryBusServer.on('isPlayerConnected', (evt) => {
                evt.connected = this.io.sockets.sockets[evt.player.uuid] !== undefined;
            });
        }
        getLobbies() {
            return this.io.sockets.adapter.rooms;
        }
        doesLobbyExist(Lobby) {
            return this.getLobbies().hasOwnProperty(Lobby);
        }
        createLobbyStorage_internal(ld, owner) {
            if (this.getLobbyStorage_internal(ld.name) !== null) {
                delete this.lobbyStorage[ld.name];
            }
            this.lobbyStorage[ld.name] = {};
            this.lobbyStorage[ld.name]['ModLoader64'] = new LobbyObjects_1.LobbyStorage(ld, owner);
            return this.lobbyStorage[ld.name]['ModLoader64'];
        }
        getLobbyStorage_internal(lobbyName) {
            try {
                return this.lobbyStorage[lobbyName].ModLoader64;
            }
            catch (err) { }
            return null;
        }
        createLobbyStorage(lobbyName, plugin, obj) {
            let mainStore = this.lobbyStorage[lobbyName]['ModLoader64'];
            if (!mainStore.hasOwnProperty('data')) {
                mainStore['data'] = {};
            }
            mainStore.data[plugin.pluginName] = obj;
        }
        getAllLobbies() {
            return this.lobbyStorage;
        }
        getLobbyStorage(lobbyName, plugin) {
            try {
                return this.lobbyStorage[lobbyName].ModLoader64.data[plugin.pluginName];
            }
            catch (err) { }
            return null;
        }
        getPlayerRinfo(player) {
            if (typeof (player) === 'string') {
                if (this.io.sockets.sockets[player] === undefined)
                    return undefined;
                if (this.io.sockets.sockets[player]["ModLoader64"] === undefined)
                    return undefined;
                return this.io.sockets.sockets[player].ModLoader64["rinfo"];
            }
            else {
                if (this.io.sockets.sockets[player.uuid] === undefined)
                    return undefined;
                if (this.io.sockets.sockets[player.uuid]["ModLoader64"] === undefined)
                    return undefined;
                return this.io.sockets.sockets[player.uuid].ModLoader64["rinfo"];
            }
        }
        sendToTarget(target, internalChannel, packet) {
            this.io.to(target).emit(internalChannel, packet);
        }
        setup() {
            const server = require('http').createServer();
            this.io = require('socket.io')(server);
            server.listen(this.config.port);
            modloader64_1.internal_event_bus.on('SHUTDOWN_EVERYTHING', () => {
                this.logger.info('SHUTDOWN DETECTED.');
            });
            this.logger.info('NetworkEngine.Server set up on port ' + this.config.port + '.');
            if (!this.modLoaderconfig.isClient) {
                modloader64_1.internal_event_bus.emit('onNetworkConnect', {
                    me: this.fakePlayer,
                    patch: Buffer.alloc(1),
                });
            }
            setInterval(() => {
                let rm = [];
                let lobbies = {};
                for (let i = 0; i < this.lobby_names.length; i++) {
                    if (this.getLobbyStorage_internal(this.lobby_names[i]) !== null) {
                        if (this.getLobbies()[this.lobby_names[i]] !== undefined) {
                            lobbies[this.lobby_names[i]] = Object.keys(this.getLobbies()[this.lobby_names[i]]['sockets']).length;
                        }
                        else {
                            rm.push(this.lobby_names[i]);
                        }
                    }
                    else {
                        rm.push(this.lobby_names[i]);
                    }
                }
                if (rm.length > 0) {
                    for (let i = 0; i < rm.length; i++) {
                        let index = this.lobby_names.indexOf(rm[i]);
                        let name = this.lobby_names.splice(index, 1)[0].trim();
                        this.logger.info("lobby " + name + " terminated.");
                        EventHandler_1.bus.emit(EventHandler_1.EventsServer.ON_LOBBY_DESTROY, name);
                        delete this.lobbyStorage[name];
                    }
                }
                fs_1.default.writeFile("./lobbies.json", JSON.stringify(lobbies), () => { });
                Object.keys(this.io.sockets.sockets).forEach((player) => {
                    let rinfo = this.getPlayerRinfo(player);
                    if (rinfo !== undefined) {
                        this.udpServer.send(JSON.stringify(new NetworkingPackets_1.PingPacket(player)), rinfo.port, rinfo.address);
                    }
                    else {
                        this.sendToTarget(player, 'msg', new NetworkingPackets_1.PingPacket(player).setType(0 /* SocketType.TCP */));
                    }
                });
            }, 60 * 1000);
            (function (inst) {
                natUpnp_client.portMapping({
                    public: inst.config.port,
                    private: inst.config.port,
                    ttl: 10,
                }, function (err) {
                    if (err) {
                        inst.logger.warn("Didn't open port for TCP server.");
                    }
                    else {
                        inst.logger.info('Opened port for TCP server.');
                    }
                });
                natUpnp_client.portMapping({
                    public: inst.config.udpPort,
                    private: inst.config.udpPort,
                    ttl: 10,
                }, function (err) {
                    if (err) {
                        inst.logger.warn("Didn't open port for UDP server.");
                    }
                    else {
                        inst.logger.info('Opened port for UDP server.');
                    }
                });
                inst.udpPort = inst.config.udpPort;
                inst.udpServer.bind(inst.config.udpPort);
                NetworkHandler_1.NetworkSendBusServer.addListener('msg', (data) => {
                    if (data.player === undefined) {
                        data.player = inst.fakePlayer;
                    }
                    if (data.socketType === 0 /* SocketType.TCP */) {
                        inst.sendToTarget(data.lobby, 'msg', data);
                    }
                    else {
                        let uuids = Object.keys(inst.io.sockets.adapter.rooms[data.lobby]);
                        for (let i = 0; i < uuids.length; i++) {
                            let mlo = inst.io.sockets.sockets[uuids[i]].ModLoader64;
                            if (mlo.hasOwnProperty("rinfo")) {
                                let rinfo = mlo.rinfo;
                                inst.udpServer.send(JSON.stringify(data), rinfo.port, rinfo.address);
                            }
                        }
                    }
                });
                NetworkHandler_1.NetworkSendBusServer.addListener('toPlayer', (data) => {
                    if (data.player === undefined) {
                        data.player = inst.fakePlayer;
                    }
                    if (data.packet.socketType === 0 /* SocketType.TCP */) {
                        inst.sendToTarget(data.player.uuid, 'msg', data.packet);
                    }
                    else {
                        let rinfo = inst.getPlayerRinfo(data.player);
                        if (rinfo !== undefined) {
                            inst.udpServer.send(JSON.stringify(data.packet), rinfo.port, rinfo.address);
                        }
                        else {
                            inst.sendToTarget(data.player.uuid, 'msg', data.packet);
                        }
                    }
                });
                NetworkHandler_1.NetworkBusServer.on('UDPTestPacket', (packet) => {
                    let reply = JSON.parse(JSON.stringify(packet));
                    reply.player = inst.fakePlayer;
                    inst.udpServer.send(JSON.stringify(reply), packet.rinfo.port, packet.rinfo.address);
                });
                NetworkHandler_1.NetworkBusServer.on('UDPModeOnPacket', (packet) => {
                    inst.io.sockets.sockets[packet.player.uuid].ModLoader64["rinfo"] = packet.rinfo;
                });
                NetworkHandler_1.NetworkBusServer.on('UDPModeOffPacket', (packet) => {
                    delete inst.io.sockets.sockets[packet.player.uuid].ModLoader64["rinfo"];
                });
                NetworkHandler_1.NetworkBusServer.on('PongPacket', (packet) => {
                    let ping = packet.timestamp - packet.serverTime;
                    let roundtrip = Date.now() - packet.serverTime;
                    inst.sendToTarget(packet.player.uuid, 'msg', new NetworkingPackets_1.LatencyInfoPacket(packet.lobby, ping, roundtrip));
                });
                NetworkHandler_1.NetworkQueryBusServer.on('getOwner', (evt) => {
                    let lobby = inst.getLobbyStorage_internal(evt.lobby);
                    if (lobby === null)
                        return;
                    evt.owner = lobby.owner;
                });
                NetworkHandler_1.NetworkQueryBusServer.on('promoteOwner', (evt) => {
                    let lobby = inst.getLobbyStorage_internal(evt.lobby);
                    if (lobby === null)
                        return;
                    lobby.owner = evt.owner;
                    EventHandler_1.bus.emit(EventHandler_1.EventsServer.ON_LOBBY_OWNER_CHANGE, new EventHandler_1.EventOwnerChanged(lobby.config.name, lobby.owner));
                    inst.sendToTarget(lobby.config.name, 'newOwner', lobby.owner);
                });
                inst.io.on('connection', function (socket) {
                    inst.logger.info('Client ' + socket.id + ' connected.');
                    inst.sendToTarget(socket.id, 'uuid', { uuid: socket.id });
                    socket.on('version', function (packet) {
                        let mismatch = false;
                        try {
                            Object.keys(inst.plugins).forEach((name) => {
                                if (packet.plugins.hasOwnProperty(name)) {
                                    if (inst.plugins[name].version !== packet.plugins[name].version) {
                                        mismatch = true;
                                    }
                                    else {
                                        inst.logger.info('Plugin ' + name + ' version check passed.');
                                        if (inst.plugins[name].hash === packet.plugins[name].hash) {
                                            inst.logger.info('Plugin ' + name + ' hash check passed.');
                                        }
                                        else {
                                            mismatch = true;
                                        }
                                    }
                                }
                            });
                        }
                        catch (err) {
                            inst.sendToTarget(socket.id, 'versionBad', {
                                client: { ml: packet.ml, plugins: packet.plugins, core: packet.core },
                                server: new NetworkingPackets_1.VersionPacket(global.ModLoader.version, inst.plugins, inst.core, packet.discord),
                            });
                        }
                        if (inst.core !== packet.core) {
                            mismatch = true;
                        }
                        let evt = new IModLoaderAPI_1.VersionCheckEvent(packet.ml, packet.plugins);
                        evt.canceled = mismatch;
                        EventHandler_1.bus.emit(EventHandler_1.EventsServer.ON_VERSION_CHECK, evt);
                        mismatch = evt.canceled;
                        if (global.ModLoader.version === packet.ml && mismatch === false) {
                            inst.sendToTarget(socket.id, 'versionGood', {
                                client: packet.ml,
                                server: new NetworkingPackets_1.VersionPacket(global.ModLoader.version, inst.plugins, inst.core, packet.discord),
                                patchLimit: inst.config.patchSizeLimitMB
                            });
                            socket.join("__GLOBAL__");
                        }
                        else {
                            inst.sendToTarget(socket.id, 'versionBad', {
                                client: { ml: packet.ml, plugins: packet.plugins, core: packet.core },
                                server: new NetworkingPackets_1.VersionPacket(global.ModLoader.version, inst.plugins, inst.core, packet.discord),
                            });
                            setTimeout(function () {
                                try {
                                    socket.disconnect();
                                }
                                catch (err) { }
                            }, 1000);
                        }
                    });
                    socket.on('LobbyRequest', function (lj) {
                        if (inst.doesLobbyExist(lj.lobbyData.name) && inst.getLobbyStorage_internal(lj.lobbyData.name) !== null) {
                            // Lobby already exists.
                            let storage = inst.getLobbyStorage_internal(lj.lobbyData.name);
                            if (storage.config.key === lj.lobbyData.key) {
                                socket.join(storage.config.name);
                                EventHandler_1.bus.emit(EventHandler_1.EventsServer.ON_LOBBY_JOIN, new EventHandler_1.EventServerJoined(lj.player, lj.lobbyData.name));
                                //@ts-ignore
                                socket['ModLoader64'] = {
                                    lobby: storage.config.name,
                                    player: lj.player
                                };
                                storage.players.push(lj.player);
                                if (storage.owner.uuid === "null") {
                                    storage.owner = lj.player;
                                }
                                inst.sendToTarget(socket.id, 'LobbyReady', {
                                    storage: storage.config,
                                    udp: inst.udpPort,
                                    owner: storage.owner
                                });
                                inst.sendToTarget(lj.lobbyData.name, 'playerJoined', lj.player);
                            }
                            else {
                                inst.sendToTarget(socket.id, 'LobbyDenied_BadPassword', lj);
                            }
                        }
                        else {
                            // Lobby does not exist.
                            inst.logger.info('Creating lobby ' + lj.lobbyData.name + '.');
                            socket.join(lj.lobbyData.name);
                            let storage = inst.createLobbyStorage_internal(lj.lobbyData, lj.player);
                            EventHandler_1.bus.emit(EventHandler_1.EventsServer.ON_LOBBY_CREATE, lj.lobbyData.name);
                            EventHandler_1.bus.emit(EventHandler_1.EventsServer.ON_LOBBY_DATA, storage.config);
                            EventHandler_1.bus.emit(EventHandler_1.EventsServer.ON_LOBBY_JOIN, new EventHandler_1.EventServerJoined(lj.player, lj.lobbyData.name));
                            //@ts-ignore
                            socket['ModLoader64'] = {
                                lobby: storage.config.name,
                                player: lj.player,
                            };
                            inst.sendToTarget(socket.id, 'LobbyReady', {
                                storage: storage.config,
                                udp: inst.udpPort,
                                owner: storage.owner
                            });
                            inst.sendToTarget(lj.lobbyData.name, 'playerJoined', lj.player);
                            EventHandler_1.bus.emit(EventHandler_1.EventsServer.ON_LOBBY_OWNER_CHANGE, new EventHandler_1.EventOwnerChanged(lj.lobbyData.name, storage.owner));
                            inst.lobby_names.push(lj.lobbyData.name);
                        }
                    });
                    socket.on('playerJoined_reply', function (data) {
                        inst.sendToTarget(data.dest.uuid, 'playerJoined_bounce', data.player);
                    });
                    socket.on('msg', function (data) {
                        Object.freeze(data);
                        try {
                            NetworkHandler_1.NetworkBusServer.emit(data.packet_id, data);
                            NetworkHandler_1.NetworkChannelBusServer.emit(data.channel, data);
                            if (data.forward) {
                                socket.to(data.lobby).emit('msg', data);
                            }
                        }
                        catch (err) {
                            inst.logger.error(err.stack);
                            inst.logger.error(JSON.stringify(data));
                        }
                    });
                    socket.on('toSpecificPlayer', function (data) {
                        inst.sendToTarget(data.player.uuid, 'msg', data.packet);
                    });
                    socket.on('onCrash', function (data) {
                        console.log("Receiving crash dump...");
                        if (!fs_1.default.existsSync("./crashlogs")) {
                            fs_1.default.mkdirSync("./crashlogs");
                        }
                        let f = "./crashlogs/" + Date.now().toString(16) + ".bin";
                        fs_1.default.writeFileSync(f, JSON.parse(data.dump).dump);
                        EventHandler_1.bus.emit(IModLoaderAPI_1.ModLoaderEvents.ON_RECEIVED_CRASH_LOG, { name: path_1.default.parse(f).name, dump: zlib_1.default.inflateSync(fs_1.default.readFileSync(f)) });
                    });
                    socket.on('disconnect', () => {
                        //@ts-ignore
                        let ML = socket.ModLoader64;
                        if (ML === undefined)
                            return;
                        if (inst.getLobbyStorage_internal(ML.lobby) === null)
                            return;
                        let lobby = inst.getLobbyStorage_internal(ML.lobby);
                        if (lobby.owner === ML.player.uuid) {
                            // Owner left. Reassign.
                            let index = lobby.players.indexOf(ML.player.uuid);
                            if (index > -1) {
                                lobby.players.splice(index, 1);
                                if (lobby.players.length > 0) {
                                    lobby.owner = lobby.players[0];
                                }
                                else {
                                    lobby.owner.uuid = "null";
                                    lobby.owner.nickname = "null";
                                }
                            }
                        }
                        EventHandler_1.bus.emit(EventHandler_1.EventsServer.ON_LOBBY_LEAVE, new EventHandler_1.EventServerLeft(ML.player, ML.lobby));
                        inst.sendToTarget(ML.lobby, 'left', ML.player);
                        EventHandler_1.bus.emit(EventHandler_1.EventsServer.ON_LOBBY_OWNER_CHANGE, new EventHandler_1.EventOwnerChanged(lobby.config.name, lobby.owner));
                        inst.sendToTarget(ML.lobby, 'newOwner', lobby.owner);
                    });
                });
                inst.udpServer.on('error', (err) => {
                    inst.logger.error(`server error:\n${err.stack}`);
                    inst.udpServer.close();
                });
                inst.udpServer.on('message', (buf, rinfo) => {
                    try {
                        let msg = buf.toString();
                        if (msg.charAt(0) !== '{') {
                            return;
                        }
                        let data = JSON.parse(msg);
                        Object.defineProperty(data, "rinfo", {
                            value: rinfo,
                            writable: false,
                        });
                        Object.freeze(data);
                        if (inst.getLobbyStorage_internal(data.lobby) === null) {
                            return;
                        }
                        NetworkHandler_1.NetworkBusServer.emit(data.packet_id, data);
                        NetworkHandler_1.NetworkChannelBusServer.emit(data.channel, data);
                        if (data.forward) {
                            let json = JSON.stringify(data);
                            let players = Object.keys(inst.io.sockets.adapter.rooms[data.lobby].sockets);
                            for (let i = 0; i < players.length; i++) {
                                if (players[i] === data.player.uuid)
                                    continue;
                                let rinfo = inst.getPlayerRinfo(players[i]);
                                if (rinfo !== undefined) {
                                    inst.udpServer.send(json, rinfo.port, rinfo.address);
                                }
                                else {
                                    inst.sendToTarget(players[i], 'msg', data);
                                }
                            }
                        }
                    }
                    catch (err) {
                        inst.logger.error(err);
                    }
                });
                inst.udpServer.on('listening', () => {
                    const address = inst.udpServer.address();
                    inst.logger.info(`UDP socket listening ${address.address}:${address.port}`);
                });
            })(this);
        }
    }
    NetworkEngine.Server = Server;
    class Client {
        io = require('socket.io-client');
        socket = {};
        logger;
        config;
        modLoaderconfig;
        masterConfig;
        me;
        udpClient = dgram_1.default.createSocket('udp4');
        serverUDPPort = -1;
        isUDPEnabled = false;
        udpTestHandle;
        udpPingHandle;
        lastReceivedPing;
        packetBuffer = new Array();
        plugins = {};
        core = "";
        isConnectionReady = false;
        lastPacketBuffer = new Array();
        pluginConfiguredConnection = false;
        connectionTimer;
        discord;
        lobbyOwner;
        constructor(logger, config, discord) {
            this.discord = discord;
            this.logger = logger;
            this.config = config.registerConfigCategory('NetworkEngine.Client');
            config.setData('NetworkEngine.Client', "isSinglePlayer", false);
            config.setData('NetworkEngine.Client', 'forceServerOverride', false);
            config.setData('NetworkEngine.Client', 'ip', '127.0.0.1');
            config.setData('NetworkEngine.Client', 'port', 8082);
            config.setData('NetworkEngine.Client', 'lobby', mluuid_1.ML_UUID.getLobbyName());
            config.setData('NetworkEngine.Client', 'nickname', 'Player');
            config.setData('NetworkEngine.Client', 'password', '');
            config.setData('NetworkEngine.Client', 'forceTCPMode', false);
            this.masterConfig = config;
            this.modLoaderconfig = this.masterConfig.registerConfigCategory('ModLoader64');
            modloader64_1.internal_event_bus.on('PLUGIN_LOADED', (args) => {
                let p = args[0].meta;
                this.plugins[p.name] = { version: p.version, hash: args[0].hash };
                if (typeof args[0].instance.getServerURL === "function" && !this.config.forceServerOverride && !this.pluginConfiguredConnection && !this.config.isSinglePlayer) {
                    this.logger.info("Using plugin server configuration: " + p.name + ".");
                    let server_connection_setup = args[0].instance;
                    this.config.ip = server_connection_setup.getServerURL().split(":")[0];
                    this.config.port = parseInt(server_connection_setup.getServerURL().split(":")[1]);
                    this.pluginConfiguredConnection = true;
                }
            });
            modloader64_1.internal_event_bus.on('CORE_LOADED', (args) => {
                this.core = args[0].name;
                if (this.config.isSinglePlayer) {
                    this.logger.info("Dropping into single player mode due to config.");
                    this.config.ip = "127.0.0.1";
                    this.config.port = parseInt("8082");
                }
            });
        }
        onTick() {
            this.lastPacketBuffer.length = 0;
            while (this.packetBuffer.length > 0) {
                try {
                    let data = this.packetBuffer.shift();
                    this.lastPacketBuffer.push(data);
                    NetworkHandler_1.NetworkBus.emit(data.packet_id, data);
                    NetworkHandler_1.NetworkChannelBus.emit(data.channel, data);
                }
                catch (err) {
                    this.logger.error(err.stack);
                }
            }
        }
        setup() {
            (function (inst) {
                inst.connectionTimer = setTimeout(() => {
                    inst.logger.error("Failed to connect.");
                    process.exit(0);
                }, 30 * 1000);
                inst.logger.info('Starting up NetworkEngine.Client...');
                inst.socket = inst.io.connect('http://' + inst.config.ip + ':' + inst.config.port);
                NetworkHandler_1.NetworkSendBus.addListener('msg', (data) => {
                    data.player = inst.me;
                    data.lobby = inst.config.lobby;
                    if (data.player === undefined) {
                        inst.logger.error("Tried to send a packet with no local INetworkPlayer!");
                        return;
                    }
                    if (data.socketType === 1 /* SocketType.UDP */ &&
                        inst.isUDPEnabled &&
                        inst.isConnectionReady) {
                        inst.udpClient.send(JSON.stringify(data), inst.serverUDPPort, inst.config.ip);
                    }
                    else {
                        inst.socket.emit('msg', JSON.parse(JSON.stringify(data)));
                    }
                });
                NetworkHandler_1.NetworkSendBus.addListener('toPlayer', (data) => {
                    data.packet.player = inst.me;
                    data.packet.lobby = inst.config.lobby;
                    inst.socket.emit('toSpecificPlayer', data);
                });
                NetworkHandler_1.NetworkBus.on('UDPTestPacket', () => {
                    clearInterval(inst.udpTestHandle);
                    inst.logger.info("UDP test passed.");
                    let packet = new NetworkingPackets_1.UDPModeOnPacket(inst.config.lobby);
                    packet.player = inst.me;
                    inst.udpClient.send(JSON.stringify(packet), inst.serverUDPPort, inst.config.ip);
                    if (inst.udpPingHandle === undefined) {
                        inst.udpPingHandle = setInterval(() => {
                            if (inst.lastReceivedPing === undefined || inst.config.forceTCPMode) {
                                inst.isUDPEnabled = false;
                                inst.logger.warn('UDP disabled.');
                                NetworkHandler_1.NetworkSendBus.emit('msg', new NetworkingPackets_1.UDPModeOffPacket(inst.config.lobby));
                                clearInterval(inst.udpPingHandle);
                            }
                        }, 70 * 1000);
                    }
                });
                NetworkHandler_1.NetworkBus.on('PingPacket', (packet) => {
                    inst.lastReceivedPing = packet;
                    if (packet.socketType === 0 /* SocketType.TCP */) {
                        NetworkHandler_1.NetworkSendBus.emit('msg', new NetworkingPackets_1.PongPacket(packet.timestamp, inst.config.lobby).setType(0 /* SocketType.TCP */));
                    }
                    else {
                        NetworkHandler_1.NetworkSendBus.emit('msg', new NetworkingPackets_1.PongPacket(packet.timestamp, inst.config.lobby).setType(1 /* SocketType.UDP */));
                    }
                });
                NetworkHandler_1.NetworkBus.on('LatencyInfoPacket', (packet) => {
                    inst.me.data["ninfo"] = { ping: packet.ping, rt: packet.roundtrip };
                });
                NetworkHandler_1.NetworkQueryBus.on('getOwner', (evt) => {
                    evt.owner = inst.lobbyOwner;
                });
                inst.socket.on('connect', () => {
                    inst.logger.info('Connected.');
                    clearTimeout(inst.connectionTimer);
                });
                inst.socket.on('uuid', (data) => {
                    inst.me = new ModLoaderDefaultImpls_1.NetworkPlayer(inst.config.nickname, data.uuid);
                    inst.socket.emit('version', new NetworkingPackets_1.VersionPacket(global.ModLoader.version, inst.plugins, inst.core, inst.discord));
                });
                inst.socket.on('versionGood', (data) => {
                    if (data.server.discord !== "") {
                        inst.me.data["discord"] = data.server.discord;
                        inst.logger.info("Local INetworkPlayer linked with Discord user " + inst.me.data["discord"]);
                    }
                    inst.logger.info('Version good! ' + JSON.stringify(data.server));
                    inst.logger.info("This server has a " + data.patchLimit.toString() + "MB bps patch limit.");
                    let ld = new NetworkHandler_1.LobbyData(inst.config.lobby, crypto_1.default
                        .createHash('md5')
                        .update(Buffer.from(inst.config.password))
                        .digest('hex'));
                    EventHandler_1.bus.emit(EventHandler_1.EventsClient.CONFIGURE_LOBBY, ld);
                    if (inst.modLoaderconfig.patch !== '') {
                        let mods = "./mods";
                        if (global.ModLoader.hasOwnProperty("OVERRIDE_MODS_FOLDER")) {
                            mods = global.ModLoader.OVERRIDE_MODS_FOLDER;
                        }
                        let files = (0, getAllFiles_1.getAllFiles)(mods, []);
                        let patch_path = "";
                        for (let i = 0; i < files.length; i++) {
                            let p = path_1.default.parse(files[i]);
                            if (p.base === inst.modLoaderconfig.patch) {
                                patch_path = files[i];
                            }
                        }
                        if (patch_path !== "") {
                            let gzip = zlib_1.default.deflateSync(fs_1.default.readFileSync(path_1.default.resolve(patch_path)));
                            if (gzip.byteLength > (data.patchLimit * 1024 * 1024)) {
                                inst.logger.error("Patch file " + patch_path + " too large.");
                                process.exit(3 /* ModLoaderErrorCodes.BPS_FAILED */);
                            }
                            else {
                                ld.data['patch'] = gzip;
                                ld.data['patch_name'] = inst.modLoaderconfig.patch;
                            }
                        }
                    }
                    //@ts-ignore
                    inst.socket.emit('LobbyRequest', new LobbyObjects_1.LobbyJoin(ld, inst.me.toJSON()));
                    EventHandler_1.bus.emit(EventHandler_1.EventsClient.ON_SERVER_CONNECTION, {});
                });
                inst.socket.on('versionBad', (data) => {
                    inst.logger.info('Version bad! ' + JSON.stringify(data));
                    modloader64_1.internal_event_bus.emit('VERSION_BAD', JSON.stringify(data));
                    setTimeout(() => {
                        process.exit(4 /* ModLoaderErrorCodes.BAD_VERSION */);
                    }, 1000);
                });
                inst.socket.on('LobbyReady', (data) => {
                    let ld = data.storage;
                    let udpPort = data.udp;
                    inst.logger.info('Joined lobby ' + ld.name + '.');
                    inst.lobbyOwner = data.owner;
                    modloader64_1.internal_event_bus.emit("DISCORD_INVITE_SETUP", inst.config);
                    let p = Buffer.alloc(1);
                    if (ld.data.hasOwnProperty('patch')) {
                        p = zlib_1.default.inflateSync(ld.data.patch);
                    }
                    let udpTest = new NetworkingPackets_1.UDPTestPacket();
                    udpTest.player = inst.me;
                    udpTest.lobby = inst.config.lobby;
                    inst.isUDPEnabled = true;
                    inst.udpTestHandle = setTimeout(() => {
                        inst.isUDPEnabled = false;
                        inst.logger.warn('UDP disabled.');
                    }, 30 * 1000);
                    inst.udpClient.send(JSON.stringify(udpTest), udpPort, inst.config.ip);
                    inst.serverUDPPort = udpPort;
                    modloader64_1.internal_event_bus.emit('onNetworkConnect', {
                        me: inst.me,
                        patch: p,
                        patch_name: ld.data.patch_name
                    });
                    EventHandler_1.bus.emit(EventHandler_1.EventsClient.ON_LOBBY_JOIN, ld);
                    inst.isConnectionReady = true;
                });
                inst.socket.on('disconnect', () => {
                    inst.isConnectionReady = false;
                });
                inst.socket.on('LobbyDenied_BadPassword', (ld) => {
                    inst.logger.error('Failed to join lobby. :(');
                    setTimeout(() => {
                        process.exit(7 /* ModLoaderErrorCodes.BAD_LOBBY_PASSWORD */);
                    }, 1000);
                });
                inst.socket.on('left', (player) => {
                    EventHandler_1.bus.emit(EventHandler_1.EventsClient.ON_PLAYER_LEAVE, player);
                });
                inst.socket.on('newOwner', (owner) => {
                    inst.lobbyOwner = owner;
                    EventHandler_1.bus.emit(EventHandler_1.EventsClient.ON_LOBBY_OWNER_CHANGE, new EventHandler_1.EventOwnerChanged(inst.config.lobby, inst.lobbyOwner));
                });
                inst.socket.on('playerJoined', (player) => {
                    if (player.uuid !== inst.me.uuid) {
                        EventHandler_1.bus.emit(EventHandler_1.EventsClient.ON_PLAYER_JOIN, player);
                        inst.socket.emit('playerJoined_reply', {
                            player: inst.me,
                            dest: player,
                        });
                    }
                });
                inst.socket.on('playerJoined_bounce', (player) => {
                    if (player.uuid !== inst.me.uuid) {
                        EventHandler_1.bus.emit(EventHandler_1.EventsClient.ON_PLAYER_JOIN, player);
                    }
                });
                inst.socket.on('msg', (data) => {
                    inst.packetBuffer.push(Object.freeze(data));
                });
                inst.udpClient.on('message', (msg, rinfo) => {
                    let packet = JSON.parse(msg.toString());
                    inst.packetBuffer.push(Object.freeze(packet));
                });
            })(this);
        }
    }
    NetworkEngine.Client = Client;
})(NetworkEngine || (NetworkEngine = {}));
exports.default = NetworkEngine;
//# sourceMappingURL=NetworkEngine.js.map