UNPKG

@jsprismarine/client

Version:
166 lines (165 loc) • 20.8 kB
import { import_clear_interval_async, import_set_interval_async } from "./node_modules/.pnpm/set-interval-async@3.0.3/node_modules/set-interval-async/dist/dynamic/set-interval-async2.es.js"; import { Logger } from "@jsprismarine/logger"; import { Protocol } from "@jsprismarine/prismarine"; import { ConnectionPriority, InetAddress, MessageIdentifiers, Protocol as Protocol$1, RakNetSession } from "@jsprismarine/raknet"; import Crypto, { randomBytes } from "node:crypto"; import Dgram from "node:dgram"; import { EventEmitter } from "node:events"; //#region src/index.ts var getRandomInt = (min, max) => { min = Math.ceil(min); max = Math.floor(max); return Math.floor(Math.random() * (max - min + 1)) + min; }; var PROTOCOL = 10; var DEF_MTU_SIZE = 1455; var RAKNET_TICK_LENGTH = 1 / 100; var Client = class extends EventEmitter { clientGUID = Crypto.randomBytes(8).readBigInt64BE(); logger = new Logger(); address; targetAddress; connection = null; socket = Dgram.createSocket({ type: "udp4" }); get closed() { return false; } connecting = false; connected = false; offlineHandled = false; loginHandled = false; constructor() { super(); this.address = new InetAddress("0.0.0.0", getRandomInt(46e3, 49999)); this.socket.bind(this.address.getPort(), this.address.getAddress()); } /** * Creates a packet listener on given address and port. * @param {string} address - The address to listen on. * @param {number} port - The port to listen on. * @returns {Promise<Client>} The client instance. * @public */ async connect(address = "0.0.0.0", port = 19132) { this.targetAddress = new InetAddress(address, port); this.socket.on("message", (buffer) => { this.handle(buffer); }); if (this.connection) throw new Error("Already connected/connecting to server."); this.logger.info("JSPrismarine client is now attempting to connect..."); const timer = (0, import_set_interval_async.setIntervalAsync)(async () => { if (this.closed) { await (0, import_clear_interval_async.clearIntervalAsync)(timer); return; } if (!this.connecting) { const pk = new Protocol$1.UnconnectedPing(); pk.timestamp = BigInt(Date.now()); pk.encode(); await this.sendBuffer(pk.getBuffer()); } if (this.connected && !this.loginHandled) { const pk = new Protocol.Packets.LoginPacket(); pk.encode(); const sendPk = new Protocol$1.Frame(); sendPk.reliability = 0; sendPk.content = pk.getBuffer(); this.connection.sendFrame(sendPk, ConnectionPriority.NORMAL); this.loginHandled = true; } this.connection?.update(Date.now()); }, RAKNET_TICK_LENGTH * 1e3); return this; } /** * Handles the incoming buffer. * @param {Buffer} buffer - The buffer to handle. * @private * @internal */ async handle(buffer) { const header = buffer.readUInt8(); if (this.connection && this.offlineHandled) { this.connection.handle(buffer); return; } let buf; switch (header) { case MessageIdentifiers.UNCONNECTED_PONG: buf = this.handleUnconnectedPong(buffer); await this.sendBuffer(buf); break; case MessageIdentifiers.OPEN_CONNECTION_REPLY_1: buf = this.handleOpenConnectionReply1(buffer); await this.sendBuffer(buf); break; case MessageIdentifiers.OPEN_CONNECTION_REPLY_2: this.handleOpenConnectionReply2(buffer); break; default: this.logger.warn(`Unhandled offline packet ID: ${header}`, "Client/handle"); } } handleUnconnectedPong(buffer) { const decodedPacket = new Protocol$1.UnconnectedPong(buffer); decodedPacket.decode(); if (!decodedPacket.isValid()) throw new Error("Received an invalid offline message"); const packet = new Protocol$1.OpenConnectionRequest1(); packet.protocol = PROTOCOL; packet.mtuSize = DEF_MTU_SIZE; packet.encode(); return packet.getBuffer(); } handleOpenConnectionReply1(buffer) { const decodedPacket = new Protocol$1.OpenConnectionReply1(buffer); decodedPacket.decode(); if (!decodedPacket.isValid()) throw new Error("Received an invalid offline message"); const packet = new Protocol$1.OpenConnectionRequest2(); packet.serverAddress = this.targetAddress; packet.mtuSize = DEF_MTU_SIZE; packet.clientGUID = this.clientGUID; packet.encode(); this.connecting = true; this.connection = new RakNetSession(this, DEF_MTU_SIZE, { address: this.address.getAddress(), port: this.address.getPort(), family: "IPv4", size: 0 }, randomBytes(8).readBigInt64BE()); return packet.getBuffer(); } handleOpenConnectionReply2(buffer) { const decodedPacket = new Protocol$1.OpenConnectionReply2(buffer); decodedPacket.decode(); if (!decodedPacket.isValid()) throw new Error("Received an invalid offline message"); const packet = new Protocol$1.ConnectionRequest(); packet.clientGUID = this.clientGUID; packet.requestTimestamp = BigInt(Date.now()); packet.encode(); const sendPacket = new Protocol$1.Frame(); sendPacket.reliability = 0; sendPacket.content = packet.getBuffer(); this.connection?.sendFrame(sendPacket, ConnectionPriority.NORMAL); this.offlineHandled = true; this.connected = true; } /** * Sends the buffer to the server * * @param buffer */ async sendBuffer(buffer) { this.socket.send(buffer, 0, buffer.byteLength, this.targetAddress.getPort(), this.targetAddress.getAddress()); } getSocket() { return this.socket; } getAddress() { return this.address; } async removeConnection(_connection, _reason) { throw new Error("Method not implemented."); } }; //#endregion export { Client as default }; //# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5kZXguZXMuanMiLCJuYW1lcyI6W10sInNvdXJjZXMiOlsiLi4vc3JjL2luZGV4LnRzIl0sInNvdXJjZXNDb250ZW50IjpbImltcG9ydCB7IExvZ2dlciB9IGZyb20gJ0Bqc3ByaXNtYXJpbmUvbG9nZ2VyJztcbmltcG9ydCB7IFByb3RvY29sIGFzIEpTUFByb3RvY29sIH0gZnJvbSAnQGpzcHJpc21hcmluZS9wcmlzbWFyaW5lJztcbmltcG9ydCB7IENvbm5lY3Rpb25Qcmlvcml0eSwgSW5ldEFkZHJlc3MsIE1lc3NhZ2VJZGVudGlmaWVycywgUHJvdG9jb2wsIFJha05ldFNlc3Npb24gfSBmcm9tICdAanNwcmlzbWFyaW5lL3Jha25ldCc7XG5pbXBvcnQgQ3J5cHRvLCB7IHJhbmRvbUJ5dGVzIH0gZnJvbSAnbm9kZTpjcnlwdG8nO1xuaW1wb3J0IERncmFtLCB7IHR5cGUgU29ja2V0IH0gZnJvbSAnbm9kZTpkZ3JhbSc7XG5pbXBvcnQgeyBFdmVudEVtaXR0ZXIgfSBmcm9tICdub2RlOmV2ZW50cyc7XG5pbXBvcnQgeyBjbGVhckludGVydmFsQXN5bmMsIHNldEludGVydmFsQXN5bmMgfSBmcm9tICdzZXQtaW50ZXJ2YWwtYXN5bmMvZHluYW1pYyc7XG5cbi8vIGh0dHBzOi8vc3RhY2tvdmVyZmxvdy5jb20vYS8xNTI3ODIwLzMxNDI1NTNcbmNvbnN0IGdldFJhbmRvbUludCA9IChtaW46IG51bWJlciwgbWF4OiBudW1iZXIpID0+IHtcbiAgICBtaW4gPSBNYXRoLmNlaWwobWluKTtcbiAgICBtYXggPSBNYXRoLmZsb29yKG1heCk7XG4gICAgcmV0dXJuIE1hdGguZmxvb3IoTWF0aC5yYW5kb20oKSAqIChtYXggLSBtaW4gKyAxKSkgKyBtaW47XG59O1xuXG4vLyBNaW5lY3JhZnQgcmVsYXRlZCBwcm90b2NvbFxuY29uc3QgUFJPVE9DT0wgPSAxMDtcblxuLy8gTWF4IG5ldCB0cmFuc2ZlciB1bml0XG5jb25zdCBERUZfTVRVX1NJWkUgPSAxNDU1O1xuXG4vLyBSYWtuZXQgdGlja3NcbmNvbnN0IFJBS05FVF9UUFMgPSAxMDA7XG5jb25zdCBSQUtORVRfVElDS19MRU5HVEggPSAxIC8gUkFLTkVUX1RQUztcblxuZXhwb3J0IGRlZmF1bHQgY2xhc3MgQ2xpZW50IGV4dGVuZHMgRXZlbnRFbWl0dGVyIHtcbiAgICBwcml2YXRlIGNsaWVudEdVSUQgPSBDcnlwdG8ucmFuZG9tQnl0ZXMoOCkucmVhZEJpZ0ludDY0QkUoKTtcbiAgICBwcml2YXRlIHJlYWRvbmx5IGxvZ2dlciA9IG5ldyBMb2dnZXIoKTtcbiAgICBwcml2YXRlIHJlYWRvbmx5IGFkZHJlc3M6IEluZXRBZGRyZXNzO1xuICAgIHByaXZhdGUgdGFyZ2V0QWRkcmVzcyE6IEluZXRBZGRyZXNzO1xuICAgIHByaXZhdGUgY29ubmVjdGlvbjogUmFrTmV0U2Vzc2lvbiB8IG51bGwgPSBudWxsO1xuICAgIHByaXZhdGUgcmVhZG9ubHkgc29ja2V0ID0gRGdyYW0uY3JlYXRlU29ja2V0KHsgdHlwZTogJ3VkcDQnIH0pO1xuICAgIHByaXZhdGUgZ2V0IGNsb3NlZCgpIHtcbiAgICAgICAgcmV0dXJuIGZhbHNlO1xuICAgIH1cblxuICAgIHByaXZhdGUgY29ubmVjdGluZyA9IGZhbHNlO1xuICAgIHByaXZhdGUgY29ubmVjdGVkID0gZmFsc2U7XG4gICAgcHJpdmF0ZSBvZmZsaW5lSGFuZGxlZCA9IGZhbHNlO1xuICAgIHByaXZhdGUgbG9naW5IYW5kbGVkID0gZmFsc2U7XG5cbiAgICBwdWJsaWMgY29uc3RydWN0b3IoKSB7XG4gICAgICAgIHN1cGVyKCk7XG4gICAgICAgIHRoaXMuYWRkcmVzcyA9IG5ldyBJbmV0QWRkcmVzcygnMC4wLjAuMCcsIGdldFJhbmRvbUludCg0NjAwMCwgNDk5OTkpKTtcbiAgICAgICAgdGhpcy5zb2NrZXQuYmluZCh0aGlzLmFkZHJlc3MuZ2V0UG9ydCgpLCB0aGlzLmFkZHJlc3MuZ2V0QWRkcmVzcygpKTtcbiAgICB9XG5cbiAgICAvKipcbiAgICAgKiBDcmVhdGVzIGEgcGFja2V0IGxpc3RlbmVyIG9uIGdpdmVuIGFkZHJlc3MgYW5kIHBvcnQuXG4gICAgICogQHBhcmFtIHtzdHJpbmd9IGFkZHJlc3MgLSBUaGUgYWRkcmVzcyB0byBsaXN0ZW4gb24uXG4gICAgICogQHBhcmFtIHtudW1iZXJ9IHBvcnQgLSBUaGUgcG9ydCB0byBsaXN0ZW4gb24uXG4gICAgICogQHJldHVybnMge1Byb21pc2U8Q2xpZW50Pn0gVGhlIGNsaWVudCBpbnN0YW5jZS5cbiAgICAgKiBAcHVibGljXG4gICAgICovXG4gICAgcHVibGljIGFzeW5jIGNvbm5lY3QoYWRkcmVzcyA9ICcwLjAuMC4wJywgcG9ydCA9IDE5MTMyKTogUHJvbWlzZTxDbGllbnQ+IHtcbiAgICAgICAgdGhpcy50YXJnZXRBZGRyZXNzID0gbmV3IEluZXRBZGRyZXNzKGFkZHJlc3MsIHBvcnQpO1xuXG4gICAgICAgIHRoaXMuc29ja2V0Lm9uKCdtZXNzYWdlJywgKGJ1ZmZlcjogQnVmZmVyKSA9PiB7XG4gICAgICAgICAgICB2b2lkIHRoaXMuaGFuZGxlKGJ1ZmZlcik7XG4gICAgICAgIH0pO1xuXG4gICAgICAgIGlmICh0aGlzLmNvbm5lY3Rpb24pIHRocm93IG5ldyBFcnJvcignQWxyZWFkeSBjb25uZWN0ZWQvY29ubmVjdGluZyB0byBzZXJ2ZXIuJyk7XG5cbiAgICAgICAgdGhpcy5sb2dnZXIuaW5mbygnSlNQcmlzbWFyaW5lIGNsaWVudCBpcyBub3cgYXR0ZW1wdGluZyB0byBjb25uZWN0Li4uJyk7XG5cbiAgICAgICAgY29uc3QgdGltZXIgPSBzZXRJbnRlcnZhbEFzeW5jKGFzeW5jICgpID0+IHtcbiAgICAgICAgICAgIGlmICh0aGlzLmNsb3NlZCkge1xuICAgICAgICAgICAgICAgIGF3YWl0IGNsZWFySW50ZXJ2YWxBc3luYyh0aW1lcik7XG4gICAgICAgICAgICAgICAgcmV0dXJuO1xuICAgICAgICAgICAgfVxuXG4gICAgICAgICAgICAvLyBTZW5kIGEgY2xpZW50IHBhY2tldCB0byB0aGUgc2VydmVyXG4gICAgICAgICAgICAvLyBzbyB0aGUgc2VydmVyIGdvZXMgaW4gdGFyZ2V0IG1vZGVcbiAgICAgICAgICAgIC8vIGFuZCB0aGUgbG9naW4gcHJvY2VzcyBzdGFydHNcbiAgICAgICAgICAgIGlmICghdGhpcy5jb25uZWN0aW5nKSB7XG4gICAgICAgICAgICAgICAgY29uc3QgcGsgPSBuZXcgUHJvdG9jb2wuVW5jb25uZWN0ZWRQaW5nKCk7XG4gICAgICAgICAgICAgICAgcGsudGltZXN0YW1wID0gQmlnSW50KERhdGUubm93KCkpO1xuICAgICAgICAgICAgICAgIC8vIFRPRE86IGNhbiBiZSBvbWl0dGVkLi4uIHBrLiA9IHRoaXMuY2xpZW50R1VJRDtcbiAgICAgICAgICAgICAgICBway5lbmNvZGUoKTtcbiAgICAgICAgICAgICAgICBhd2FpdCB0aGlzLnNlbmRCdWZmZXIocGsuZ2V0QnVmZmVyKCkpO1xuICAgICAgICAgICAgfVxuXG4gICAgICAgICAgICBpZiAodGhpcy5jb25uZWN0ZWQgJiYgIXRoaXMubG9naW5IYW5kbGVkKSB7XG4gICAgICAgICAgICAgICAgY29uc3QgcGsgPSBuZXcgSlNQUHJvdG9jb2wuUGFja2V0cy5Mb2dpblBhY2tldCgpO1xuICAgICAgICAgICAgICAgIHBrLmVuY29kZSgpO1xuXG4gICAgICAgICAgICAgICAgY29uc3Qgc2VuZFBrID0gbmV3IFByb3RvY29sLkZyYW1lKCk7XG4gICAgICAgICAgICAgICAgc2VuZFBrLnJlbGlhYmlsaXR5ID0gMDtcbiAgICAgICAgICAgICAgICBzZW5kUGsuY29udGVudCA9IHBrLmdldEJ1ZmZlcigpO1xuXG4gICAgICAgICAgICAgICAgdGhpcy5jb25uZWN0aW9uIS5zZW5kRnJhbWUoc2VuZFBrLCBDb25uZWN0aW9uUHJpb3JpdHkuTk9STUFMKTsgLy8gUGFja2V0IG5lZWRzIHRvIGJlIHNwbGl0dGVkXG4gICAgICAgICAgICAgICAgdGhpcy5sb2dpbkhhbmRsZWQgPSB0cnVlO1xuICAgICAgICAgICAgfVxuXG4gICAgICAgICAgICB0aGlzLmNvbm5lY3Rpb24/LnVwZGF0ZShEYXRlLm5vdygpKTtcbiAgICAgICAgfSwgUkFLTkVUX1RJQ0tfTEVOR1RIICogMTAwMCk7XG4gICAgICAgIHJldHVybiB0aGlzO1xuICAgIH1cblxuICAgIC8qKlxuICAgICAqIEhhbmRsZXMgdGhlIGluY29taW5nIGJ1ZmZlci5cbiAgICAgKiBAcGFyYW0ge0J1ZmZlcn0gYnVmZmVyIC0gVGhlIGJ1ZmZlciB0byBoYW5kbGUuXG4gICAgICogQHByaXZhdGVcbiAgICAgKiBAaW50ZXJuYWxcbiAgICAgKi9cbiAgICBwcml2YXRlIGFzeW5jIGhhbmRsZShidWZmZXI6IEJ1ZmZlcik6IFByb21pc2U8dm9pZD4ge1xuICAgICAgICBjb25zdCBoZWFkZXIgPSBidWZmZXIucmVhZFVJbnQ4KCk7IC8vIFJlYWQgcGFja2V0IGhlYWRlclxuXG4gICAgICAgIGlmICh0aGlzLmNvbm5lY3Rpb24gJiYgdGhpcy5vZmZsaW5lSGFuZGxlZCkge1xuICAgICAgICAgICAgdGhpcy5jb25uZWN0aW9uLmhhbmRsZShidWZmZXIpO1xuICAgICAgICAgICAgcmV0dXJuO1xuICAgICAgICB9XG5cbiAgICAgICAgbGV0IGJ1ZjtcbiAgICAgICAgc3dpdGNoIChoZWFkZXIpIHtcbiAgICAgICAgICAgIGNhc2UgTWVzc2FnZUlkZW50aWZpZXJzLlVOQ09OTkVDVEVEX1BPTkc6XG4gICAgICAgICAgICAgICAgYnVmID0gdGhpcy5oYW5kbGVVbmNvbm5lY3RlZFBvbmcoYnVmZmVyKTtcbiAgICAgICAgICAgICAgICBhd2FpdCB0aGlzLnNlbmRCdWZmZXIoYnVmKTtcbiAgICAgICAgICAgICAgICBicmVhaztcbiAgICAgICAgICAgIGNhc2UgTWVzc2FnZUlkZW50aWZpZXJzLk9QRU5fQ09OTkVDVElPTl9SRVBMWV8xOlxuICAgICAgICAgICAgICAgIGJ1ZiA9IHRoaXMuaGFuZGxlT3BlbkNvbm5lY3Rpb25SZXBseTEoYnVmZmVyKTtcbiAgICAgICAgICAgICAgICBhd2FpdCB0aGlzLnNlbmRCdWZmZXIoYnVmKTtcbiAgICAgICAgICAgICAgICBicmVhaztcbiAgICAgICAgICAgIGNhc2UgTWVzc2FnZUlkZW50aWZpZXJzLk9QRU5fQ09OTkVDVElPTl9SRVBMWV8yOlxuICAgICAgICAgICAgICAgIHRoaXMuaGFuZGxlT3BlbkNvbm5lY3Rpb25SZXBseTIoYnVmZmVyKTtcbiAgICAgICAgICAgICAgICBicmVhaztcbiAgICAgICAgICAgIGRlZmF1bHQ6XG4gICAgICAgICAgICAgICAgdGhpcy5sb2dnZXIud2FybihgVW5oYW5kbGVkIG9mZmxpbmUgcGFja2V0IElEOiAke2hlYWRlcn1gLCAnQ2xpZW50L2hhbmRsZScpO1xuICAgICAgICB9XG4gICAgfVxuXG4gICAgcHVibGljIGhhbmRsZVVuY29ubmVjdGVkUG9uZyhidWZmZXI6IEJ1ZmZlcik6IEJ1ZmZlciB7XG4gICAgICAgIC8vIERlY29kZSBzZXJ2ZXIgcGFja2V0XG4gICAgICAgIGNvbnN0IGRlY29kZWRQYWNrZXQgPSBuZXcgUHJvdG9jb2wuVW5jb25uZWN0ZWRQb25nKGJ1ZmZlcik7XG4gICAgICAgIGRlY29kZWRQYWNrZXQuZGVjb2RlKCk7XG5cbiAgICAgICAgLy8gQ2hlY2sgcGFja2V0IHZhbGlkaXR5XG4gICAgICAgIC8vIFRvIHJlZmFjdG9yXG4gICAgICAgIGlmICghZGVjb2RlZFBhY2tldC5pc1ZhbGlkKCkpIHtcbiAgICAgICAgICAgIHRocm93IG5ldyBFcnJvcignUmVjZWl2ZWQgYW4gaW52YWxpZCBvZmZsaW5lIG1lc3NhZ2UnKTtcbiAgICAgICAgfVxuXG4gICAgICAgIC8vIEVuY29kZSByZXNwb25zZVxuICAgICAgICBjb25zdCBwYWNrZXQgPSBuZXcgUHJvdG9jb2wuT3BlbkNvbm5lY3Rpb25SZXF1ZXN0MSgpO1xuICAgICAgICBwYWNrZXQucHJvdG9jb2wgPSBQUk9UT0NPTDtcbiAgICAgICAgcGFja2V0Lm10dVNpemUgPSBERUZfTVRVX1NJWkU7XG4gICAgICAgIHBhY2tldC5lbmNvZGUoKTtcblxuICAgICAgICAvLyBVcGRhdGUgc2Vzc2lvbiBzdGF0dXNcbiAgICAgICAgLy8gdGhpcy5zdGF0dXMgPSBDb25uZWN0aW9uU3RhdHVzLlRhcmdldHRlZDtcblxuICAgICAgICByZXR1cm4gcGFja2V0LmdldEJ1ZmZlcigpO1xuICAgIH1cblxuICAgIHB1YmxpYyBoYW5kbGVPcGVuQ29ubmVjdGlvblJlcGx5MShidWZmZXI6IEJ1ZmZlcik6IEJ1ZmZlciB7XG4gICAgICAgIC8vIERlY29kZSBzZXJ2ZXIgcGFja2V0XG4gICAgICAgIGNvbnN0IGRlY29kZWRQYWNrZXQgPSBuZXcgUHJvdG9jb2wuT3BlbkNvbm5lY3Rpb25SZXBseTEoYnVmZmVyKTtcbiAgICAgICAgZGVjb2RlZFBhY2tldC5kZWNvZGUoKTtcblxuICAgICAgICAvLyBDaGVjayBwYWNrZXQgdmFsaWRpdHlcbiAgICAgICAgLy8gVG8gcmVmYWN0b3JcbiAgICAgICAgaWYgKCFkZWNvZGVkUGFja2V0LmlzVmFsaWQoKSkge1xuICAgICAgICAgICAgdGhyb3cgbmV3IEVycm9yKCdSZWNlaXZlZCBhbiBpbnZhbGlkIG9mZmxpbmUgbWVzc2FnZScpO1xuICAgICAgICB9XG5cbiAgICAgICAgLy8gRW5jb2RlIHJlc3BvbnNlXG4gICAgICAgIGNvbnN0IHBhY2tldCA9IG5ldyBQcm90b2NvbC5PcGVuQ29ubmVjdGlvblJlcXVlc3QyKCk7XG4gICAgICAgIHBhY2tldC5zZXJ2ZXJBZGRyZXNzID0gdGhpcy50YXJnZXRBZGRyZXNzO1xuICAgICAgICBwYWNrZXQubXR1U2l6ZSA9IERFRl9NVFVfU0laRTtcbiAgICAgICAgcGFja2V0LmNsaWVudEdVSUQgPSB0aGlzLmNsaWVudEdVSUQ7XG4gICAgICAgIHBhY2tldC5lbmNvZGUoKTtcblxuICAgICAgICAvLyBVcGRhdGUgc2Vzc2lvbiBzdGF0dXNcbiAgICAgICAgdGhpcy5jb25uZWN0aW5nID0gdHJ1ZTtcbiAgICAgICAgLy8gVGhpcy5zdGF0dXMgPSBDb25uZWN0aW9uU3RhdHVzLkNvbm5lY3RlZDtcbiAgICAgICAgdGhpcy5jb25uZWN0aW9uID0gbmV3IFJha05ldFNlc3Npb24oXG4gICAgICAgICAgICB0aGlzIGFzIGFueSxcbiAgICAgICAgICAgIERFRl9NVFVfU0laRSxcbiAgICAgICAgICAgIHtcbiAgICAgICAgICAgICAgICBhZGRyZXNzOiB0aGlzLmFkZHJlc3MuZ2V0QWRkcmVzcygpLFxuICAgICAgICAgICAgICAgIHBvcnQ6IHRoaXMuYWRkcmVzcy5nZXRQb3J0KCksXG4gICAgICAgICAgICAgICAgZmFtaWx5OiAnSVB2NCcsXG4gICAgICAgICAgICAgICAgc2l6ZTogMFxuICAgICAgICAgICAgfSxcbiAgICAgICAgICAgIHJhbmRvbUJ5dGVzKDgpLnJlYWRCaWdJbnQ2NEJFKClcbiAgICAgICAgKTtcblxuICAgICAgICByZXR1cm4gcGFja2V0LmdldEJ1ZmZlcigpO1xuICAgIH1cblxuICAgIHB1YmxpYyBoYW5kbGVPcGVuQ29ubmVjdGlvblJlcGx5MihidWZmZXI6IEJ1ZmZlcik6IHZvaWQge1xuICAgICAgICAvLyBEZWNvZGUgc2VydmVyIHBhY2tldFxuICAgICAgICBjb25zdCBkZWNvZGVkUGFja2V0ID0gbmV3IFByb3RvY29sLk9wZW5Db25uZWN0aW9uUmVwbHkyKGJ1ZmZlcik7XG4gICAgICAgIGRlY29kZWRQYWNrZXQuZGVjb2RlKCk7XG5cbiAgICAgICAgLy8gQ2hlY2sgcGFja2V0IHZhbGlkaXR5XG4gICAgICAgIC8vIFRvIHJlZmFjdG9yXG4gICAgICAgIGlmICghZGVjb2RlZFBhY2tldC5pc1ZhbGlkKCkpIHtcbiAgICAgICAgICAgIHRocm93IG5ldyBFcnJvcignUmVjZWl2ZWQgYW4gaW52YWxpZCBvZmZsaW5lIG1lc3NhZ2UnKTtcbiAgICAgICAgfVxuXG4gICAgICAgIC8vIEVuY29kZSByZXNwb25zZSAoZW5jYXBzdWxhdGVkKVxuICAgICAgICBjb25zdCBwYWNrZXQgPSBuZXcgUHJvdG9jb2wuQ29ubmVjdGlvblJlcXVlc3QoKTtcbiAgICAgICAgcGFja2V0LmNsaWVudEdVSUQgPSB0aGlzLmNsaWVudEdVSUQ7XG4gICAgICAgIHBhY2tldC5yZXF1ZXN0VGltZXN0YW1wID0gQmlnSW50KERhdGUubm93KCkpO1xuICAgICAgICBwYWNrZXQuZW5jb2RlKCk7XG5cbiAgICAgICAgY29uc3Qgc2VuZFBhY2tldCA9IG5ldyBQcm90b2NvbC5GcmFtZSgpO1xuICAgICAgICBzZW5kUGFja2V0LnJlbGlhYmlsaXR5ID0gMDtcbiAgICAgICAgc2VuZFBhY2tldC5jb250ZW50ID0gcGFja2V0LmdldEJ1ZmZlcigpO1xuXG4gICAgICAgIHRoaXMuY29ubmVjdGlvbj8uc2VuZEZyYW1lKHNlbmRQYWNrZXQsIENvbm5lY3Rpb25Qcmlvcml0eS5OT1JNQUwpO1xuXG4gICAgICAgIHRoaXMub2ZmbGluZUhhbmRsZWQgPSB0cnVlO1xuICAgICAgICB0aGlzLmNvbm5lY3RlZCA9IHRydWU7IC8vIFNob3VsZCBiZS4uLiB3ZSBjYW4ndCByZWx5IG9uIGl0XG4gICAgfVxuXG4gICAgLyoqXG4gICAgICogU2VuZHMgdGhlIGJ1ZmZlciB0byB0aGUgc2VydmVyXG4gICAgICpcbiAgICAgKiBAcGFyYW0gYnVmZmVyXG4gICAgICovXG4gICAgcHVibGljIGFzeW5jIHNlbmRCdWZmZXIoYnVmZmVyOiBCdWZmZXIpOiBQcm9taXNlPHZvaWQ+IHtcbiAgICAgICAgdGhpcy5zb2NrZXQuc2VuZChcbiAgICAgICAgICAgIGJ1ZmZlciBhcyBhbnksXG4gICAgICAgICAgICAwLFxuICAgICAgICAgICAgYnVmZmVyLmJ5dGVMZW5ndGgsXG4gICAgICAgICAgICB0aGlzLnRhcmdldEFkZHJlc3MuZ2V0UG9ydCgpLFxuICAgICAgICAgICAgdGhpcy50YXJnZXRBZGRyZXNzLmdldEFkZHJlc3MoKVxuICAgICAgICApO1xuICAgIH1cblxuICAgIHB1YmxpYyBnZXRTb2NrZXQoKTogU29ja2V0IHtcbiAgICAgICAgcmV0dXJuIHRoaXMuc29ja2V0O1xuICAgIH1cblxuICAgIHB1YmxpYyBnZXRBZGRyZXNzKCk6IEluZXRBZGRyZXNzIHtcbiAgICAgICAgcmV0dXJuIHRoaXMuYWRkcmVzcztcbiAgICB9XG5cbiAgICBwdWJsaWMgYXN5bmMgcmVtb3ZlQ29ubmVjdGlvbihfY29ubmVjdGlvbjogUmFrTmV0U2Vzc2lvbiwgX3JlYXNvbj86IHN0cmluZyk6IFByb21pc2U8dm9pZD4ge1xuICAgICAgICB0aHJvdyBuZXcgRXJyb3IoJ01ldGhvZCBub3QgaW1wbGVtZW50ZWQuJyk7XG4gICAgfVxufVxuIl0sIm1hcHBpbmdzIjoiOzs7Ozs7OztBQVNBLElBQU0sZ0JBQWdCLEtBQWEsUUFBZ0I7Q0FDL0MsTUFBTSxLQUFLLEtBQUssR0FBRztDQUNuQixNQUFNLEtBQUssTUFBTSxHQUFHO0NBQ3BCLE9BQU8sS0FBSyxNQUFNLEtBQUssT0FBTyxLQUFLLE1BQU0sTUFBTSxFQUFFLElBQUk7QUFDekQ7QUFHQSxJQUFNLFdBQVc7QUFHakIsSUFBTSxlQUFlO0FBSXJCLElBQU0scUJBQXFCLElBQUk7QUFFL0IsSUFBcUIsU0FBckIsY0FBb0MsYUFBYTtDQUM3QyxhQUFxQixPQUFPLFlBQVksQ0FBQyxFQUFFLGVBQWU7Q0FDMUQsU0FBMEIsSUFBSSxPQUFPO0NBQ3JDO0NBQ0E7Q0FDQSxhQUEyQztDQUMzQyxTQUEwQixNQUFNLGFBQWEsRUFBRSxNQUFNLE9BQU8sQ0FBQztDQUM3RCxJQUFZLFNBQVM7RUFDakIsT0FBTztDQUNYO0NBRUEsYUFBcUI7Q0FDckIsWUFBb0I7Q0FDcEIsaUJBQXlCO0NBQ3pCLGVBQXVCO0NBRXZCLGNBQXFCO0VBQ2pCLE1BQU07RUFDTixLQUFLLFVBQVUsSUFBSSxZQUFZLFdBQVcsYUFBYSxNQUFPLEtBQUssQ0FBQztFQUNwRSxLQUFLLE9BQU8sS0FBSyxLQUFLLFFBQVEsUUFBUSxHQUFHLEtBQUssUUFBUSxXQUFXLENBQUM7Q0FDdEU7Ozs7Ozs7O0NBU0EsTUFBYSxRQUFRLFVBQVUsV0FBVyxPQUFPLE9BQXdCO0VBQ3JFLEtBQUssZ0JBQWdCLElBQUksWUFBWSxTQUFTLElBQUk7RUFFbEQsS0FBSyxPQUFPLEdBQUcsWUFBWSxX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