UNPKG

@jsprismarine/raknet

Version:
94 lines (93 loc) 14.2 kB
import InetAddress from "../utils/InetAddress.es.js"; import { MessageIdentifiers } from "./MessageIdentifiers.es.js"; import IncompatibleProtocolVersion from "./connection/IncompatibleProtocolVersion.es.js"; import OpenConnectionReply1 from "./connection/OpenConnectionReply1.es.js"; import OpenConnectionReply2 from "./connection/OpenConnectionReply2.es.js"; import OpenConnectionRequest2 from "./connection/OpenConnectionRequest2.es.js"; import UnconnectedPing from "./offline/UnconnectedPing.es.js"; import UnconnectedPong from "./offline/UnconnectedPong.es.js"; import { MAX_MTU_SIZE, OFFLINE_MESSAGE_DATA_ID } from "../Constants.es.js"; import BinaryStream from "@jsprismarine/jsbinaryutils"; //#region src/protocol/OfflineHandler.ts var OfflineHandler = class { listener; constructor(listener) { this.listener = listener; } process(msg, rinfo) { switch (msg[0]) { case MessageIdentifiers.UNCONNECTED_PING_OPEN_CONNECTIONS: if (!this.listener.allowIncomingConnections()) return; case MessageIdentifiers.UNCONNECTED_PING: const ping = new UnconnectedPing(msg); ping.decode(); const pong = new UnconnectedPong(); pong.timestamp = ping.timestamp; pong.serverGuid = this.listener.getServerGuid(); pong.serverName = this.listener.serverName.toString(); this.listener.sendPacket(pong, rinfo); break; case MessageIdentifiers.OPEN_CONNECTION_REQUEST_1: if (msg[1 + OFFLINE_MESSAGE_DATA_ID.byteLength] !== 11) { const response = new IncompatibleProtocolVersion(); response.protocol = 11; response.serverGUID = this.listener.getServerGuid(); this.listener.sendPacket(response, rinfo); return; } const reply1 = new OpenConnectionReply1(); reply1.serverGUID = this.listener.getServerGuid(); if (msg.byteLength + 28 > 1492) reply1.mtuSize = MAX_MTU_SIZE; else reply1.mtuSize = msg.byteLength + 28; this.listener.sendPacket(reply1, rinfo); break; case MessageIdentifiers.OPEN_CONNECTION_REQUEST_2: const request = new OpenConnectionRequest2(msg); request.decode(); const addrSession = this.listener.getSessionByAddress(rinfo); const addressInUse = addrSession !== null && !addrSession.isDisconnected(); const guidSession = this.listener.getSessionByGUID(request.clientGUID); const guidInUse = guidSession !== null && !guidSession.isDisconnected(); const reply2 = new OpenConnectionReply2(); reply2.serverGuid = this.listener.getServerGuid(); reply2.clientAddress = new InetAddress(rinfo.address, rinfo.port, 4); reply2.mtuSize = request.mtuSize; if (addressInUse && guidInUse) { if (addrSession === guidSession) { this.listener.sendPacket(reply2, rinfo); return; } this.sendAlreadyConnected(rinfo); return; } if (!addressInUse && guidInUse || addrSession && !guidInUse) { this.sendAlreadyConnected(rinfo); return; } if (!this.listener.allowIncomingConnections()) { const str = new BinaryStream(); str.writeByte(MessageIdentifiers.NO_FREE_INCOMING_CONNECTIONS); str.write(OFFLINE_MESSAGE_DATA_ID); str.writeLong(this.listener.getServerGuid()); this.listener.sendBuffer(str.getBuffer(), rinfo); return; } this.listener.addSession(rinfo, request.mtuSize, request.clientGUID); this.listener.sendPacket(reply2, rinfo); break; case MessageIdentifiers.QUERY: this.listener.emit("raw", msg, new InetAddress(rinfo.address, rinfo.port)); break; default: throw new Error(`Unknown unconnected packet with ID=${msg[0].toString(16)}`); } } sendAlreadyConnected(remote) { const str = new BinaryStream(); str.writeByte(MessageIdentifiers.ALREADY_CONNECTED); str.write(OFFLINE_MESSAGE_DATA_ID); str.writeLong(this.listener.getServerGuid()); this.listener.sendBuffer(str.getBuffer(), remote); } }; //#endregion export { OfflineHandler }; //# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiT2ZmbGluZUhhbmRsZXIuZXMuanMiLCJuYW1lcyI6W10sInNvdXJjZXMiOlsiLi4vLi4vc3JjL3Byb3RvY29sL09mZmxpbmVIYW5kbGVyLnRzIl0sInNvdXJjZXNDb250ZW50IjpbImltcG9ydCB7IEluZXRBZGRyZXNzLCBNQVhfTVRVX1NJWkUsIE1JTkVDUkFGVF9QUk9UT0NPTF9WRVJTSU9OLCBPRkZMSU5FX01FU1NBR0VfREFUQV9JRCwgVURQX0hFQURFUl9TSVpFIH0gZnJvbSAnLi4vJztcblxuaW1wb3J0IEJpbmFyeVN0cmVhbSBmcm9tICdAanNwcmlzbWFyaW5lL2pzYmluYXJ5dXRpbHMnO1xuaW1wb3J0IHR5cGUgeyBSZW1vdGVJbmZvIH0gZnJvbSAnbm9kZTpkZ3JhbSc7XG5pbXBvcnQgdHlwZSBTZXJ2ZXJTb2NrZXQgZnJvbSAnLi4vU2VydmVyU29ja2V0JztcbmltcG9ydCB7IE1lc3NhZ2VJZGVudGlmaWVycyB9IGZyb20gJy4vTWVzc2FnZUlkZW50aWZpZXJzJztcbmltcG9ydCBJbmNvbXBhdGlibGVQcm90b2NvbFZlcnNpb24gZnJvbSAnLi9jb25uZWN0aW9uL0luY29tcGF0aWJsZVByb3RvY29sVmVyc2lvbic7XG5pbXBvcnQgT3BlbkNvbm5lY3Rpb25SZXBseTEgZnJvbSAnLi9jb25uZWN0aW9uL09wZW5Db25uZWN0aW9uUmVwbHkxJztcbmltcG9ydCBPcGVuQ29ubmVjdGlvblJlcGx5MiBmcm9tICcuL2Nvbm5lY3Rpb24vT3BlbkNvbm5lY3Rpb25SZXBseTInO1xuaW1wb3J0IE9wZW5Db25uZWN0aW9uUmVxdWVzdDIgZnJvbSAnLi9jb25uZWN0aW9uL09wZW5Db25uZWN0aW9uUmVxdWVzdDInO1xuaW1wb3J0IFVuY29ubmVjdGVkUGluZyBmcm9tICcuL29mZmxpbmUvVW5jb25uZWN0ZWRQaW5nJztcbmltcG9ydCBVbmNvbm5lY3RlZFBvbmcgZnJvbSAnLi9vZmZsaW5lL1VuY29ubmVjdGVkUG9uZyc7XG5cbmV4cG9ydCBjbGFzcyBPZmZsaW5lSGFuZGxlciB7XG4gICAgcHVibGljIGNvbnN0cnVjdG9yKHByaXZhdGUgcmVhZG9ubHkgbGlzdGVuZXI6IFNlcnZlclNvY2tldCkge31cblxuICAgIHB1YmxpYyBwcm9jZXNzKG1zZzogQnVmZmVyLCByaW5mbzogUmVtb3RlSW5mbyk6IHZvaWQge1xuICAgICAgICBzd2l0Y2ggKG1zZ1swXSkge1xuICAgICAgICAgICAgLy8gaHR0cHM6Ly9naXRodWIuY29tL2ZhY2Vib29rYXJjaGl2ZS9SYWtOZXQvYmxvYi9tYXN0ZXIvU291cmNlL1Jha1BlZXIuY3BwI0w0NjM4XG4gICAgICAgICAgICBjYXNlIE1lc3NhZ2VJZGVudGlmaWVycy5VTkNPTk5FQ1RFRF9QSU5HX09QRU5fQ09OTkVDVElPTlM6XG4gICAgICAgICAgICAgICAgaWYgKCF0aGlzLmxpc3RlbmVyLmFsbG93SW5jb21pbmdDb25uZWN0aW9ucygpKSB7XG4gICAgICAgICAgICAgICAgICAgIHJldHVybjtcbiAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICBjYXNlIE1lc3NhZ2VJZGVudGlmaWVycy5VTkNPTk5FQ1RFRF9QSU5HOlxuICAgICAgICAgICAgICAgIGNvbnN0IHBpbmcgPSBuZXcgVW5jb25uZWN0ZWRQaW5nKG1zZyk7XG4gICAgICAgICAgICAgICAgcGluZy5kZWNvZGUoKTtcblxuICAgICAgICAgICAgICAgIGNvbnN0IHBvbmcgPSBuZXcgVW5jb25uZWN0ZWRQb25nKCk7XG4gICAgICAgICAgICAgICAgcG9uZy50aW1lc3RhbXAgPSBwaW5nLnRpbWVzdGFtcDtcbiAgICAgICAgICAgICAgICBwb25nLnNlcnZlckd1aWQgPSB0aGlzLmxpc3RlbmVyLmdldFNlcnZlckd1aWQoKTtcbiAgICAgICAgICAgICAgICBwb25nLnNlcnZlck5hbWUgPSB0aGlzLmxpc3RlbmVyLnNlcnZlck5hbWUudG9TdHJpbmcoKTtcbiAgICAgICAgICAgICAgICB0aGlzLmxpc3RlbmVyLnNlbmRQYWNrZXQocG9uZywgcmluZm8pO1xuICAgICAgICAgICAgICAgIGJyZWFrO1xuICAgICAgICAgICAgLy8gaHR0cHM6Ly9naXRodWIuY29tL2ZhY2Vib29rYXJjaGl2ZS9SYWtOZXQvYmxvYi9tYXN0ZXIvU291cmNlL1Jha1BlZXIuY3BwI0w1MTI3XG4gICAgICAgICAgICBjYXNlIE1lc3NhZ2VJZGVudGlmaWVycy5PUEVOX0NPTk5FQ1RJT05fUkVRVUVTVF8xOlxuICAgICAgICAgICAgICAgIC8vIERvbid0IHdhc3RlIHJlc291cmNlcyBieSBhbGxvY2F0aW5nIGEgcGFja2V0IGlmIHdlIGtub3cgdmVyc2lvbiBtaXNtYXRjaGVzXG4gICAgICAgICAgICAgICAgY29uc3QgcmVtb3RlUHJvdG9jb2wgPSBtc2dbMSArIE9GRkxJTkVfTUVTU0FHRV9EQVRBX0lELmJ5dGVMZW5ndGhdO1xuICAgICAgICAgICAgICAgIC8vIFRPRE86IHNldHRlciBmb3IgY3VzdG9tIHByb3RvY29sIHZlcnNpb25cbiAgICAgICAgICAgICAgICBpZiAocmVtb3RlUHJvdG9jb2wgIT09IE1JTkVDUkFGVF9QUk9UT0NPTF9WRVJTSU9OKSB7XG4gICAgICAgICAgICAgICAgICAgIGNvbnN0IHJlc3BvbnNlID0gbmV3IEluY29tcGF0aWJsZVByb3RvY29sVmVyc2lvbigpO1xuICAgICAgICAgICAgICAgICAgICByZXNwb25zZS5wcm90b2NvbCA9IE1JTkVDUkFGVF9QUk9UT0NPTF9WRVJTSU9OO1xuICAgICAgICAgICAgICAgICAgICByZXNwb25zZS5zZXJ2ZXJHVUlEID0gdGhpcy5saXN0ZW5lci5nZXRTZXJ2ZXJHdWlkKCk7XG4gICAgICAgICAgICAgICAgICAgIHRoaXMubGlzdGVuZXIuc2VuZFBhY2tldChyZXNwb25zZSwgcmluZm8pO1xuICAgICAgICAgICAgICAgICAgICByZXR1cm47XG4gICAgICAgICAgICAgICAgfVxuXG4gICAgICAgICAgICAgICAgY29uc3QgcmVwbHkxID0gbmV3IE9wZW5Db25uZWN0aW9uUmVwbHkxKCk7XG4gICAgICAgICAgICAgICAgcmVwbHkxLnNlcnZlckdVSUQgPSB0aGlzLmxpc3RlbmVyLmdldFNlcnZlckd1aWQoKTtcblxuICAgICAgICAgICAgICAgIGlmIChtc2cuYnl0ZUxlbmd0aCArIFVEUF9IRUFERVJfU0laRSA+IE1BWF9NVFVfU0laRSkge1xuICAgICAgICAgICAgICAgICAgICByZXBseTEubXR1U2l6ZSA9IE1BWF9NVFVfU0laRTtcbiAgICAgICAgICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgICAgICAgICByZXBseTEubXR1U2l6ZSA9IG1zZy5ieXRlTGVuZ3RoICsgVURQX0hFQURFUl9TSVpFO1xuICAgICAgICAgICAgICAgIH1cblxuICAgICAgICAgICAgICAgIHRoaXMubGlzdGVuZXIuc2VuZFBhY2tldChyZXBseTEsIHJpbmZvKTtcbiAgICAgICAgICAgICAgICBicmVhaztcbiAgICAgICAgICAgIC8vIGh0dHBzOi8vZ2l0aHViLmNvbS9mYWNlYm9va2FyY2hpdmUvUmFrTmV0L2Jsb2IvbWFzdGVyL1NvdXJjZS9SYWtQZWVyLmNwcCNMNTE5OFxuICAgICAgICAgICAgY2FzZSBNZXNzYWdlSWRlbnRpZmllcnMuT1BFTl9DT05ORUNUSU9OX1JFUVVFU1RfMjpcbiAgICAgICAgICAgICAgICBjb25zdCByZXF1ZXN0ID0gbmV3IE9wZW5Db25uZWN0aW9uUmVxdWVzdDIobXNnKTtcbiAgICAgICAgICAgICAgICByZXF1ZXN0LmRlY29kZSgpO1xuXG4gICAgICAgICAgICAgICAgY29uc3QgYWRkclNlc3Npb24gPSB0aGlzLmxpc3RlbmVyLmdldFNlc3Npb25CeUFkZHJlc3MocmluZm8pO1xuICAgICAgICAgICAgICAgIGNvbnN0IGFkZHJlc3NJblVzZSA9IGFkZHJTZXNzaW9uICE9PSBudWxsICYmICFhZGRyU2Vzc2lvbi5pc0Rpc2Nvbm5lY3RlZCgpOyAvLyBpc0FjdGl2ZSdpc2hcbiAgICAgICAgICAgICAgICBjb25zdCBndWlkU2Vzc2lvbiA9IHRoaXMubGlzdGVuZXIuZ2V0U2Vzc2lvbkJ5R1VJRChyZXF1ZXN0LmNsaWVudEdVSUQpO1xuICAgICAgICAgICAgICAgIGNvbnN0IGd1aWRJblVzZSA9IGd1aWRTZXNzaW9uICE9PSBudWxsICYmICFndWlkU2Vzc2lvbi5pc0Rpc2Nvbm5lY3RlZCgpO1xuXG4gICAgICAgICAgICAgICAgY29uc3QgcmVwbHkyID0gbmV3IE9wZW5Db25uZWN0aW9uUmVwbHkyKCk7XG4gICAgICAgICAgICAgICAgcmVwbHkyLnNlcnZlckd1aWQgPSB0aGlzLmxpc3RlbmVyLmdldFNlcnZlckd1aWQoKTtcbiAgICAgICAgICAgICAgICByZXBseTIuY2xpZW50QWRkcmVzcyA9IG5ldyBJbmV0QWRkcmVzcyhyaW5mby5hZGRyZXNzLCByaW5mby5wb3J0LCA0KTtcbiAgICAgICAgICAgICAgICByZXBseTIubXR1U2l6ZSA9IHJlcXVlc3QubXR1U2l6ZTtcblxuICAgICAgICAgICAgICAgIGlmIChhZGRyZXNzSW5Vc2UgJiYgZ3VpZEluVXNlKSB7XG4gICAgICAgICAgICAgICAgICAgIGlmIChhZGRyU2Vzc2lvbiA9PT0gZ3VpZFNlc3Npb24pIHtcbiAgICAgICAgICAgICAgICAgICAgICAgIHRoaXMubGlzdGVuZXIuc2VuZFBhY2tldChyZXBseTIsIHJpbmZvKTtcbiAgICAgICAgICAgICAgICAgICAgICAgIHJldHVybjtcbiAgICAgICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgICAgICB0aGlzLnNlbmRBbHJlYWR5Q29ubmVjdGVkKHJpbmZvKTtcbiAgICAgICAgICAgICAgICAgICAgcmV0dXJuO1xuICAgICAgICAgICAgICAgIH1cblxuICAgICAgICAgICAgICAgIGlmICgoIWFkZHJlc3NJblVzZSAmJiBndWlkSW5Vc2UpIHx8IChhZGRyU2Vzc2lvbiAmJiAhZ3VpZEluVXNlKSkge1xuICAgICAgICAgICAgICAgICAgICB0aGlzLnNlbmRBbHJlYWR5Q29ubmVjdGVkKHJpbmZvKTtcbiAgICAgICAgICAgICAgICAgICAgcmV0dXJuO1xuICAgICAgICAgICAgICAgIH1cblxuICAgICAgICAgICAgICAgIGlmICghdGhpcy5saXN0ZW5lci5hbGxvd0luY29taW5nQ29ubmVjdGlvbnMoKSkge1xuICAgICAgICAgICAgICAgICAgICBjb25zdCBzdHIgPSBuZXcgQmluYXJ5U3RyZWFtKCk7XG4gICAgICAgICAgICAgICAgICAgIHN0ci53cml0ZUJ5dGUoTWVzc2FnZUlkZW50aWZpZXJzLk5PX0ZSRUVfSU5DT01JTkdfQ09OTkVDVElPTlMpO1xuICAgICAgICAgICAgICAgICAgICBzdHIud3JpdGUoT0ZGTElORV9NRVNTQUdFX0RBVEFfSUQpO1xuICAgICAgICAgICAgICAgICAgICBzdHIud3JpdGVMb25nKHRoaXMubGlzdGVuZXIuZ2V0U2VydmVyR3VpZCgpKTtcbiAgICAgICAgICAgICAgICAgICAgdGhpcy5saXN0ZW5lci5zZW5kQnVmZmVyKHN0ci5nZXRCdWZmZXIoKSwgcmluZm8pO1xuICAgICAgICAgICAgICAgICAgICByZXR1cm47XG4gICAgICAgICAgICAgICAgfVxuXG4gICAgICAgICAgICAgICAgdGhpcy5saXN0ZW5lci5hZGRTZXNzaW9uKHJpbmZvLCByZXF1ZXN0Lm10dVNpemUsIHJlcXVlc3QuY2xpZW50R1VJRCk7XG5cbiAgICAgICAgICAgICAgICB0aGlzLmxpc3RlbmVyLnNlbmRQYWNrZXQocmVwbHkyLCByaW5mbyk7XG4gICAgICAgICAgICAgICAgYnJlYWs7XG4gICAgICAgICAgICBjYXNlIE1lc3NhZ2VJZGVudGlmaWVycy5RVUVSWTpcbiAgICAgICAgICAgICAgICB0aGlzLmxpc3RlbmVyLmVtaXQoJ3JhdycsIG1zZywgbmV3IEluZXRBZGRyZXNzKHJpbmZvLmFkZHJlc3MsIHJpbmZvLnBvcnQpKTtcbiAgICAgICAgICAgICAgICBicmVhaztcbiAgICAgICAgICAgIGRlZmF1bHQ6XG4gICAgICAgICAgICAgICAgdGhyb3cgbmV3IEVycm9yKGBVbmtub3duIHVuY29ubmVjdGVkIHBhY2tldCB3aXRoIElEPSR7bXNnWzBdIS50b1N0cmluZygxNil9YCk7XG4gICAgICAgIH1cbiAgICB9XG5cbiAgICBwcml2YXRlIHNlbmRBbHJlYWR5Q29ubmVjdGVkKHJlbW90ZTogUmVtb3RlSW5mbyk6IHZvaWQge1xuICAgICAgICBjb25zdCBzdHIgPSBuZXcgQmluYXJ5U3RyZWFtKCk7XG4gICAgICAgIHN0ci53cml0ZUJ5dGUoTWVzc2FnZUlkZW50aWZpZXJzLkFMUkVBRFlfQ09OTkVDVEVEKTtcbiAgICAgICAgc3RyLndyaXRlKE9GRkxJTkVfTUVTU0FHRV9EQVRBX0lEKTtcbiAgICAgICAgc3RyLndyaXRlTG9uZyh0aGlzLmxpc3RlbmVyLmdldFNlcnZlckd1aWQoKSk7XG4gICAgICAgIHRoaXMubGlzdGVuZXIuc2VuZEJ1ZmZlcihzdHIuZ2V0QnVmZmVyKCksIHJlbW90ZSk7XG4gICAgfVxufVxuIl0sIm1hcHBpbmdzIjoiOzs7Ozs7Ozs7OztBQWFBLElBQWEsaUJBQWIsTUFBNEI7Q0FDWTtDQUFwQyxZQUFtQixVQUF5QztFQUF4QixLQUFBLFdBQUE7Q0FBeUI7Q0FFN0QsUUFBZSxLQUFhLE9BQXlCO0VBQ2pELFFBQVEsSUFBSSxJQUFaO0dBRUksS0FBSyxtQkFBbUIsbUNBQ3BCLElBQUksQ0FBQyxLQUFLLFNBQVMseUJBQXlCLEdBQ3hDO0dBRVIsS0FBSyxtQkFBbUI7SUFDcEIsTUFBTSxPQUFPLElBQUksZ0JBQWdCLEdBQUc7SUFDcEMsS0FBSyxPQUFPO0lBRVosTUFBTSxPQUFPLElBQUksZ0JBQWdCO0lBQ2pDLEtBQUssWUFBWSxLQUFLO0lBQ3RCLEtBQUssYUFBYSxLQUFLLFNBQVMsY0FBYztJQUM5QyxLQUFLLGFBQWEsS0FBSyxTQUFTLFdBQVcsU0FBUztJQUNwRCxLQUFLLFNBQVMsV0FBVyxNQUFNLEtBQUs7SUFDcEM7R0FFSixLQUFLLG1CQUFtQjtJQUlwQixJQUZ1QixJQUFJLElBQUksd0JBQXdCLGdCQUFBLElBRUo7S0FDL0MsTUFBTSxXQUFXLElBQUksNEJBQTRCO0tBQ2pELFNBQVMsV0FBQTtLQUNULFNBQVMsYUFBYSxLQUFLLFNBQVMsY0FBYztLQUNsRCxLQUFLLFNBQVMsV0FBVyxVQUFVLEtBQUs7S0FDeEM7SUFDSjtJQUVBLE1BQU0sU0FBUyxJQUFJLHFCQUFxQjtJQUN4QyxPQUFPLGFBQWEsS0FBSyxTQUFTLGNBQWM7SUFFaEQsSUFBSSxJQUFJLGFBQUEsS0FBQSxNQUNKLE9BQU8sVUFBVTtTQUVqQixPQUFPLFVBQVUsSUFBSSxhQUFBO0lBR3pCLEtBQUssU0FBUyxXQUFXLFFBQVEsS0FBSztJQUN0QztHQUVKLEtBQUssbUJBQW1CO0lBQ3BCLE1BQU0sVUFBVSxJQUFJLHVCQUF1QixHQUFHO0lBQzlDLFFBQVEsT0FBTztJQUVmLE1BQU0sY0FBYyxLQUFLLFNBQVMsb0JBQW9CLEtBQUs7SUFDM0QsTUFBTSxlQUFlLGdCQUFnQixRQUFRLENBQUMsWUFBWSxlQUFlO0lBQ3pFLE1BQU0sY0FBYyxLQUFLLFNBQVMsaUJBQWlCLFFBQVEsVUFBVTtJQUNyRSxNQUFNLFlBQVksZ0JBQWdCLFFBQVEsQ0FBQyxZQUFZLGVBQWU7SUFFdEUsTUFBTSxTQUFTLElBQUkscUJBQXFCO0lBQ3hDLE9BQU8sYUFBYSxLQUFLLFNBQVMsY0FBYztJQUNoRCxPQUFPLGdCQUFnQixJQUFJLFlBQVksTUFBTSxTQUFTLE1BQU0sTUFBTSxDQUFDO0lBQ25FLE9BQU8sVUFBVSxRQUFRO0lBRXpCLElBQUksZ0JBQWdCLFdBQVc7S0FDM0IsSUFBSSxnQkFBZ0IsYUFBYTtNQUM3QixLQUFLLFNBQVMsV0FBVyxRQUFRLEtBQUs7TUFDdEM7S0FDSjtLQUNBLEtBQUsscUJBQXFCLEtBQUs7S0FDL0I7SUFDSjtJQUVBLElBQUssQ0FBQyxnQkFBZ0IsYUFBZSxlQUFlLENBQUMsV0FBWTtLQUM3RCxLQUFLLHFCQUFxQixLQUFLO0tBQy9CO0lBQ0o7SUFFQSxJQUFJLENBQUMsS0FBSyxTQUFTLHlCQUF5QixHQUFHO0tBQzNDLE1BQU0sTUFBTSxJQUFJLGFBQWE7S0FDN0IsSUFBSSxVQUFVLG1CQUFtQiw0QkFBNEI7S0FDN0QsSUFBSSxNQUFNLHVCQUF1QjtLQUNqQyxJQUFJLFVBQVUsS0FBSyxTQUFTLGNBQWMsQ0FBQztLQUMzQyxLQUFLLFNBQVMsV0FBVyxJQUFJLFVBQVUsR0FBRyxLQUFLO0tBQy9DO0lBQ0o7SUFFQSxLQUFLLFNBQVMsV0FBVyxPQUFPLFFBQVEsU0FBUyxRQUFRLFVBQVU7SUFFbkUsS0FBSyxTQUFTLFdBQVcsUUFBUSxLQUFLO0lBQ3RDO0dBQ0osS0FBSyxtQkFBbUI7SUFDcEIsS0FBSyxTQUFTLEtBQUssT0FBTyxLQUFLLElBQUksWUFBWSxNQUFNLFNBQVMsTUFBTSxJQUFJLENBQUM7SUFDekU7R0FDSixTQUNJLE1BQU0sSUFBSSxNQUFNLHNDQUFzQyxJQUFJLEdBQUksU0FBUyxFQUFFLEdBQUc7RUFDcEY7Q0FDSjtDQUVBLHFCQUE2QixRQUEwQjtFQUNuRCxNQUFNLE1BQU0sSUFBSSxhQUFhO0VBQzdCLElBQUksVUFBVSxtQkFBbUIsaUJBQWlCO0VBQ2xELElBQUksTUFBTSx1QkFBdUI7RUFDakMsSUFBSSxVQUFVLEtBQUssU0FBUyxjQUFjLENBQUM7RUFDM0MsS0FBSyxTQUFTLFdBQVcsSUFBSSxVQUFVLEdBQUcsTUFBTTtDQUNwRDtBQUNKIn0=