UNPKG

@jsprismarine/prismarine

Version:

Dedicated Minecraft Bedrock Edition server written in TypeScript

450 lines (449 loc) 49.4 kB
import { ChatManager } from "./chat/ChatManager.es.js"; import { PermissionManager } from "./permission/PermissionManager.es.js"; import { QueryManager } from "./query/QueryManager.es.js"; import SessionManager from "./SessionManager.es.js"; import BanManager from "./ban/BanManager.es.js"; import Timer from "./utils/Timer.es.js"; import BlockManager from "./block/BlockManager.es.js"; import { BlockMappings } from "./block/BlockMappings.es.js"; import ItemManager from "./item/ItemManager.es.js"; import Identifiers from "./network/Identifiers.es.js"; import BatchPacket from "./network/packet/BatchPacket.es.js"; import WorldManager from "./world/WorldManager.es.js"; import { CommandManager } from "./command/CommandManager.es.js"; import RaknetConnectEvent from "./events/raknet/RaknetConnectEvent.es.js"; import RaknetDisconnectEvent from "./events/raknet/RaknetDisconnectEvent.es.js"; import RaknetEncapsulatedPacketEvent from "./events/raknet/RaknetEncapsulatedPacketEvent.es.js"; import TickEvent from "./events/other/TickEvent.es.js"; import Console from "./Console.es.js"; import { EventEmitter } from "./events/EventEmitter.es.js"; import ClientConnection from "./network/ClientConnection.es.js"; import PacketRegistry from "./network/PacketRegistry.es.js"; import { version } from "./package.json.es.js"; import { RakNetListener, ServerName } from "@jsprismarine/raknet"; //#region src/Server.ts /** * JSPrismarine's main server class. * @public */ var Server = class Server extends EventEmitter { raknet; logger; config; console; packetRegistry; sessionManager = new SessionManager(); commandManager; worldManager; itemManager; blockManager; queryManager; chatManager; permissionManager; banManager; /** * If the server is stopping. * @internal */ stopping = false; /** * The current ticker timer. * @internal */ tickerTimer; /** * The current TPS. * @internal */ tps = 20; /** * The current tick. * @internal */ currentTick = 0n; /** * If the server is headless. * @internal */ headless; static MINECRAFT_TICK_TIME_MS = 1e3 / 20; /** * Creates a new server instance. * @param {object} options - The options. * @param {LoggerBuilder} options.logger - The logger. * @param {Config} options.config - The config. * @returns {Server} The server instance. */ constructor({ logger, config, headless = false }) { super(); this.headless = headless; logger.info(`Starting JSPrismarine server version §ev${version}§r for Minecraft: Bedrock Edition ${Identifiers.MinecraftVersions.at(-1)} (protocol version §e${Identifiers.Protocol}§r)`); this.logger = logger; this.config = config; this.packetRegistry = new PacketRegistry(this); this.itemManager = new ItemManager(this); this.blockManager = new BlockManager(this); this.worldManager = new WorldManager(this); if (!this.headless) this.console = new Console(this); this.commandManager = new CommandManager(this); this.queryManager = new QueryManager(this); this.chatManager = new ChatManager(this); this.permissionManager = new PermissionManager(this); this.banManager = new BanManager(this); } /** * Enables the server. * @returns {Promise<void>} A promise that resolves when the server is enabled. * @internal */ async enable() { await BlockMappings.initMappings(this); await this.config.enable(); await this.console?.enable(); await this.logger.enable(); await this.permissionManager.enable(); await this.packetRegistry.enable(); await this.itemManager.enable(); await this.blockManager.enable(); await this.banManager.enable(); await this.commandManager.enable(); await this.worldManager.enable(); this.logger.setConsole(this.console); } /** * Disables the server. * @returns {Promise<void>} A promise that resolves when the server is disabled. * @internal */ async disable() { await this.worldManager.disable(); await this.commandManager.disable(); await this.banManager.disable(); await this.blockManager.disable(); await this.itemManager.disable(); await this.permissionManager.disable(); await this.packetRegistry.disable(); await this.config.disable(); await this.logger.disable(); BlockMappings.reset(); } getMetadata() { if (!this.raknet) throw new Error("Server is not started"); return this.raknet.serverName; } /** * Reloads the server. * @returns {Promise<void>} A promise that resolves when the server is reloaded. * @remarks This method is equivalent to calling {@link Server#disable} and {@link Server#enable}. * @remarks This method and functionality is unsupported and should ideally be completely avoided. */ async reload() { await this.disable(); await this.enable(); } /** * Starts the server. * @param {string} [serverIp='0.0.0.0'] - The server IP. * @param {number} [port=19132] - The server port. * @returns {Promise<void>} A promise that resolves when the server is started. */ async bootstrap(serverIp = "0.0.0.0", port = 19132) { await this.enable(); this.raknet = new RakNetListener(this.getConfig().getMaxPlayers(), this.getConfig().getOnlineMode(), new ServerName(this), this.getLogger()); this.raknet.start(serverIp, port); this.raknet.on("openConnection", async (session) => { const event = new RaknetConnectEvent(session); await this.emit("raknetConnect", event); if (event.isCancelled()) { session.disconnect(); return; } const token = session.getAddress().toToken(); if (this.sessionManager.has(token)) { this.logger.error(`Another client with token (${token}) is already connected!`); session.disconnect("Already connected from another location"); return; } const timer = new Timer(); this.logger.debug(`${token} is attempting to connect`); this.sessionManager.add(token, new ClientConnection(session, this.logger)); this.logger.verbose(`New connection handling took §e${timer.stop()} ms§r`); }); this.raknet.on("closeConnection", async (inetAddr, reason) => { const event = new RaknetDisconnectEvent(inetAddr, reason); await this.emit("raknetDisconnect", event); const time = Date.now(); const token = inetAddr.toToken(); const session = this.sessionManager.get(token); if (!session) { this.logger.debug(`Cannot remove connection from non-existing player (${token})`); return; } await session.closePlayerSession(); this.sessionManager.remove(token); this.logger.debug(`${token} disconnected due to ${reason}`); this.logger.debug(`Player destruction took about ${Date.now() - time} ms`); }); this.raknet.on("encapsulated", async (packet, inetAddr) => { const event = new RaknetEncapsulatedPacketEvent(inetAddr, packet); await this.emit("raknetEncapsulatedPacket", event); let connection; if ((connection = this.sessionManager.get(inetAddr.toToken()) ?? null) === null) { this.logger.error(`Got a packet from a closed connection (${inetAddr.toToken()})`); return; } try { const batched = new BatchPacket(packet.content); batched.compressed = connection.hasCompression; for (const buf of await batched.asyncDecode()) { const pid = buf[0]; if (!this.packetRegistry.getPackets().has(pid)) { this.logger.warn(`Packet 0x${pid.toString(16)} isn't implemented`); continue; } const packet = new (this.packetRegistry.getPackets().get(pid))(buf); try { packet.decode(); } catch (error) { this.logger.error(error); this.logger.error(`Error while decoding packet: ${packet.constructor.name}: ${error}`); continue; } try { const handler = this.packetRegistry.getHandler(pid); this.logger.silly(`Received §b${packet.constructor.name}§r packet`); await handler.handle(packet, this, connection.getPlayerSession() ?? connection); } catch (error) { this.logger.error(`Handler error ${packet.constructor.name}-handler: (${error})`); this.logger.error(error); } } } catch (error) { this.logger.error(error); } }); this.raknet.on("raw", async (buffer, inetAddr) => { if (!this.config.getEnableQuery()) return; try { await this.queryManager.onRaw(buffer, inetAddr); } catch (error) { this.logger.verbose(`QueryManager encountered an error`); this.logger.error(error); } }); if (this.config.getEnableTicking()) { let startTime = Date.now(); let tpsStartTime = Date.now(); let lastTickTime = Date.now(); let tpsStartTick = this.getTick(); const tick = async () => { if (this.stopping) return; const event = new TickEvent(this.getTick()); this.emit("tick", event); const ticksPerSecond = 1e3 / Server.MINECRAFT_TICK_TIME_MS; await Promise.all(this.worldManager.getWorlds().map((world) => world.update(event.getTick()))); if (this.config.getEnableProcessTitle() && this.getTick() % ticksPerSecond === 0 && !this.headless) process.title = `TPS: ${this.getTPS().toFixed(2)} | Tick: ${this.getTick()} | ${process.title.split("| ").at(-1)}`; this.currentTick++; const endTime = Date.now(); const elapsedTime = endTime - startTime; const expectedElapsedTime = this.getTick() * Server.MINECRAFT_TICK_TIME_MS; const executionTime = endTime - lastTickTime; let sleepTime = Server.MINECRAFT_TICK_TIME_MS - executionTime; if (elapsedTime < expectedElapsedTime) sleepTime += expectedElapsedTime - elapsedTime; else if (elapsedTime > expectedElapsedTime) sleepTime = Math.max(0, sleepTime - (elapsedTime - expectedElapsedTime)); if (tpsStartTime !== endTime) this.tps = (this.getTick() - tpsStartTick) * 1e3 / (endTime - tpsStartTime); if (endTime - tpsStartTime >= 1e3) { tpsStartTick = this.getTick(); tpsStartTime = endTime; } this.tps = Math.min(this.tps, 20); lastTickTime = endTime; this.tickerTimer = setTimeout(tick, Math.max(0, sleepTime)); this.tickerTimer.unref(); }; tick(); } this.logger.info(`JSPrismarine is now listening on port §b${port}`); } /** * Kills the server asynchronously. * @param {object} [options] - The options. * @param {boolean} [options.crash] - If the server should crash. * @param {boolean} [options.stayAlive] - If we should let the process stay alive. * @returns {Promise<void>} A promise that resolves when the server is killed. */ async shutdown(options) { if (this.stopping) return; this.stopping = true; this.logger.info("Stopping server", "Server/kill"); await this.console?.disable(); clearInterval(this.tickerTimer); try { await this.sessionManager.kickAllPlayers("Server closed."); await this.disable(); this.raknet?.kill(); this.removeAllListeners(); console.debug("Server stopped, Goodbye!\n"); if (!options?.stayAlive) process.exit(options?.crash ? 1 : 0); } catch (error) { console.error(error); if (!options?.stayAlive) process.exit(1); } } async broadcastPacket(dataPacket) { for (const onlinePlayer of this.sessionManager.getAllPlayers()) await onlinePlayer.getNetworkSession().getConnection().sendDataPacket(dataPacket); } /** * Returns the server version. * @returns {string} The server version. * @example * ```typescript * console.log(server.getVersion()); * ``` */ getVersion() { return version; } /** * Returns the identifiers. * @returns {Identifiers} The identifiers. */ getIdentifiers() { return Identifiers; } /** * Returns the query manager. * @returns {QueryManager} The query manager. */ getQueryManager() { return this.queryManager; } /** * Returns the command manager. * @returns {CommandManager} The command manager. */ getCommandManager() { return this.commandManager; } /** * Returns the player manager. * @returns {SessionManager} The player manager. */ getSessionManager() { return this.sessionManager; } /** * Returns the world manager. * @returns {WorldManager} The world manager. */ getWorldManager() { return this.worldManager; } /** * Returns the item manager. * @returns {ItemManager} The item manager. */ getItemManager() { return this.itemManager; } /** * Returns the block manager. * @returns {BlockManager} The block manager. */ getBlockManager() { return this.blockManager; } /** * Returns the logger. * @returns {LoggerBuilder} The logger. * @example * ```typescript * // Normal log: * server.getLogger().info('Hello, world!'); * // Debug log: * server.getLogger().debug('Hello, world!'); * // Error log: * server.getLogger().error(new Error('Hello World')); * ``` */ getLogger() { return this.logger; } /** * Returns the packet registry. * @returns {PacketRegistry} The packet registry. */ getPacketRegistry() { return this.packetRegistry; } /** * Returns the raknet instance. * @returns {RakNetListener | undefined} The raknet instance. */ getRaknet() { return this.raknet; } /** * Returns the chat manager. * @returns {ChatManager} The chat manager. */ getChatManager() { return this.chatManager; } /** * Returns the config. * @returns {Config} The config. * @example * ```typescript * console.log(server.getConfig().getMaxPlayers()); // 20 * ``` */ getConfig() { return this.config; } /** * Returns the console instance. * @returns {Console | undefined} The console instance. */ getConsole() { return this.console; } /** * Returns the permission manager. * @returns {PermissionManager} The permission manager. */ getPermissionManager() { return this.permissionManager; } /** * Returns the ban manager. * @returns {BanManager} The ban manager. */ getBanManager() { return this.banManager; } /** * Returns this Prismarine instance. * @returns {Server} The Prismarine instance. */ getServer() { return this; } /** * Returns the current Tick. * @returns {number} The current Tick. */ getTick() { return Number(this.currentTick); } /** * Returns the current TPS. * @returns {number} The current TPS. */ getTPS() { return Number.parseFloat(this.tps.toFixed(2)); } }; //#endregion export { Server as default }; //# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiU2VydmVyLmVzLmpzIiwibmFtZXMiOltdLCJzb3VyY2VzIjpbIi4uL3NyYy9TZXJ2ZXIudHMiXSwic291cmNlc0NvbnRlbnQiOlsiaW1wb3J0IHsgUmFrTmV0TGlzdGVuZXIsIFNlcnZlck5hbWUgfSBmcm9tICdAanNwcmlzbWFyaW5lL3Jha25ldCc7XG5pbXBvcnQgQ29uc29sZSBmcm9tICcuL0NvbnNvbGUnO1xuaW1wb3J0IFNlc3Npb25NYW5hZ2VyIGZyb20gJy4vU2Vzc2lvbk1hbmFnZXInO1xuaW1wb3J0IEJhbk1hbmFnZXIgZnJvbSAnLi9iYW4vQmFuTWFuYWdlcic7XG5pbXBvcnQgQmxvY2tNYW5hZ2VyIGZyb20gJy4vYmxvY2svQmxvY2tNYW5hZ2VyJztcbmltcG9ydCB7IEJsb2NrTWFwcGluZ3MgfSBmcm9tICcuL2Jsb2NrL0Jsb2NrTWFwcGluZ3MnO1xuaW1wb3J0IHsgQ2hhdE1hbmFnZXIgfSBmcm9tICcuL2NoYXQvQ2hhdE1hbmFnZXInO1xuaW1wb3J0IHsgQ29tbWFuZE1hbmFnZXIgfSBmcm9tICcuL2NvbW1hbmQvQ29tbWFuZE1hbmFnZXInO1xuaW1wb3J0IHsgRXZlbnRFbWl0dGVyIH0gZnJvbSAnLi9ldmVudHMvRXZlbnRFbWl0dGVyJztcbmltcG9ydCB7IFRpY2tFdmVudCB9IGZyb20gJy4vZXZlbnRzL0V2ZW50cyc7XG5pbXBvcnQgUmFrbmV0Q29ubmVjdEV2ZW50IGZyb20gJy4vZXZlbnRzL3Jha25ldC9SYWtuZXRDb25uZWN0RXZlbnQnO1xuaW1wb3J0IFJha25ldERpc2Nvbm5lY3RFdmVudCBmcm9tICcuL2V2ZW50cy9yYWtuZXQvUmFrbmV0RGlzY29ubmVjdEV2ZW50JztcbmltcG9ydCBSYWtuZXRFbmNhcHN1bGF0ZWRQYWNrZXRFdmVudCBmcm9tICcuL2V2ZW50cy9yYWtuZXQvUmFrbmV0RW5jYXBzdWxhdGVkUGFja2V0RXZlbnQnO1xuaW1wb3J0IEl0ZW1NYW5hZ2VyIGZyb20gJy4vaXRlbS9JdGVtTWFuYWdlcic7XG5pbXBvcnQgQ2xpZW50Q29ubmVjdGlvbiBmcm9tICcuL25ldHdvcmsvQ2xpZW50Q29ubmVjdGlvbic7XG5pbXBvcnQgSWRlbnRpZmllcnMgZnJvbSAnLi9uZXR3b3JrL0lkZW50aWZpZXJzJztcbmltcG9ydCBQYWNrZXRSZWdpc3RyeSBmcm9tICcuL25ldHdvcmsvUGFja2V0UmVnaXN0cnknO1xuaW1wb3J0IHR5cGUgeyBEYXRhUGFja2V0IH0gZnJvbSAnLi9uZXR3b3JrL1BhY2tldHMnO1xuaW1wb3J0IEJhdGNoUGFja2V0IGZyb20gJy4vbmV0d29yay9wYWNrZXQvQmF0Y2hQYWNrZXQnO1xuaW1wb3J0IHsgUGVybWlzc2lvbk1hbmFnZXIgfSBmcm9tICcuL3Blcm1pc3Npb24vUGVybWlzc2lvbk1hbmFnZXInO1xuaW1wb3J0IHsgUXVlcnlNYW5hZ2VyIH0gZnJvbSAnLi9xdWVyeS9RdWVyeU1hbmFnZXInO1xuaW1wb3J0IFRpbWVyIGZyb20gJy4vdXRpbHMvVGltZXInO1xuaW1wb3J0IFdvcmxkTWFuYWdlciBmcm9tICcuL3dvcmxkL1dvcmxkTWFuYWdlcic7XG5cbmltcG9ydCB0eXBlIHsgSW5ldEFkZHJlc3MsIFJha05ldFNlc3Npb24gfSBmcm9tICdAanNwcmlzbWFyaW5lL3Jha25ldCc7XG5pbXBvcnQgdHlwZSB7IENvbmZpZyB9IGZyb20gJy4vY29uZmlnL0NvbmZpZyc7XG5cbmltcG9ydCB0eXBlIHsgTG9nZ2VyIH0gZnJvbSAnQGpzcHJpc21hcmluZS9sb2dnZXInO1xuaW1wb3J0IHsgdmVyc2lvbiB9IGZyb20gJy4uL3BhY2thZ2UuanNvbicgd2l0aCB7IHR5cGU6ICdqc29uJyB9O1xuXG4vKipcbiAqIEpTUHJpc21hcmluZSdzIG1haW4gc2VydmVyIGNsYXNzLlxuICogQHB1YmxpY1xuICovXG5leHBvcnQgZGVmYXVsdCBjbGFzcyBTZXJ2ZXIgZXh0ZW5kcyBFdmVudEVtaXR0ZXIge1xuICAgIHByaXZhdGUgcmFrbmV0OiBSYWtOZXRMaXN0ZW5lciB8IHVuZGVmaW5lZDtcbiAgICBwcml2YXRlIHJlYWRvbmx5IGxvZ2dlcjogTG9nZ2VyO1xuICAgIHByaXZhdGUgcmVhZG9ubHkgY29uZmlnOiBDb25maWc7XG4gICAgcHJpdmF0ZSByZWFkb25seSBjb25zb2xlOiBDb25zb2xlIHwgdW5kZWZpbmVkO1xuICAgIHByaXZhdGUgcmVhZG9ubHkgcGFja2V0UmVnaXN0cnk6IFBhY2tldFJlZ2lzdHJ5O1xuICAgIHByaXZhdGUgcmVhZG9ubHkgc2Vzc2lvbk1hbmFnZXIgPSBuZXcgU2Vzc2lvbk1hbmFnZXIoKTtcbiAgICBwcml2YXRlIHJlYWRvbmx5IGNvbW1hbmRNYW5hZ2VyOiBDb21tYW5kTWFuYWdlcjtcbiAgICBwcml2YXRlIHJlYWRvbmx5IHdvcmxkTWFuYWdlcjogV29ybGRNYW5hZ2VyO1xuICAgIHByaXZhdGUgcmVhZG9ubHkgaXRlbU1hbmFnZXI6IEl0ZW1NYW5hZ2VyO1xuICAgIHByaXZhdGUgcmVhZG9ubHkgYmxvY2tNYW5hZ2VyOiBCbG9ja01hbmFnZXI7XG4gICAgcHJpdmF0ZSByZWFkb25seSBxdWVyeU1hbmFnZXI6IFF1ZXJ5TWFuYWdlcjtcbiAgICBwcml2YXRlIHJlYWRvbmx5IGNoYXRNYW5hZ2VyOiBDaGF0TWFuYWdlcjtcbiAgICBwcml2YXRlIHJlYWRvbmx5IHBlcm1pc3Npb25NYW5hZ2VyOiBQZXJtaXNzaW9uTWFuYWdlcjtcbiAgICBwcml2YXRlIHJlYWRvbmx5IGJhbk1hbmFnZXI6IEJhbk1hbmFnZXI7XG5cbiAgICAvKipcbiAgICAgKiBJZiB0aGUgc2VydmVyIGlzIHN0b3BwaW5nLlxuICAgICAqIEBpbnRlcm5hbFxuICAgICAqL1xuICAgIHByaXZhdGUgc3RvcHBpbmcgPSBmYWxzZTtcblxuICAgIC8qKlxuICAgICAqIFRoZSBjdXJyZW50IHRpY2tlciB0aW1lci5cbiAgICAgKiBAaW50ZXJuYWxcbiAgICAgKi9cbiAgICBwcml2YXRlIHRpY2tlclRpbWVyOiBOb2RlSlMuVGltZW91dCB8IHVuZGVmaW5lZDtcblxuICAgIC8qKlxuICAgICAqIFRoZSBjdXJyZW50IFRQUy5cbiAgICAgKiBAaW50ZXJuYWxcbiAgICAgKi9cbiAgICBwcml2YXRlIHRwcyA9IDIwO1xuXG4gICAgLyoqXG4gICAgICogVGhlIGN1cnJlbnQgdGljay5cbiAgICAgKiBAaW50ZXJuYWxcbiAgICAgKi9cbiAgICBwcml2YXRlIGN1cnJlbnRUaWNrID0gMG47XG5cbiAgICAvKipcbiAgICAgKiBJZiB0aGUgc2VydmVyIGlzIGhlYWRsZXNzLlxuICAgICAqIEBpbnRlcm5hbFxuICAgICAqL1xuICAgIHByaXZhdGUgcmVhZG9ubHkgaGVhZGxlc3M6IGJvb2xlYW47XG5cbiAgICAvLyBUT0RPOiBNb3ZlIHRoaXMgc29tZXdoZXJlIGVsc2UuXG4gICAgcHJpdmF0ZSBzdGF0aWMgcmVhZG9ubHkgTUlORUNSQUZUX1RJQ0tfVElNRV9NUyA9IDEwMDAgLyAyMDtcblxuICAgIC8qKlxuICAgICAqIENyZWF0ZXMgYSBuZXcgc2VydmVyIGluc3RhbmNlLlxuICAgICAqIEBwYXJhbSB7b2JqZWN0fSBvcHRpb25zIC0gVGhlIG9wdGlvbnMuXG4gICAgICogQHBhcmFtIHtMb2dnZXJCdWlsZGVyfSBvcHRpb25zLmxvZ2dlciAtIFRoZSBsb2dnZXIuXG4gICAgICogQHBhcmFtIHtDb25maWd9IG9wdGlvbnMuY29uZmlnIC0gVGhlIGNvbmZpZy5cbiAgICAgKiBAcmV0dXJucyB7U2VydmVyfSBUaGUgc2VydmVyIGluc3RhbmNlLlxuICAgICAqL1xuICAgIHB1YmxpYyBjb25zdHJ1Y3Rvcih7IGxvZ2dlciwgY29uZmlnLCBoZWFkbGVzcyA9IGZhbHNlIH06IHsgbG9nZ2VyOiBMb2dnZXI7IGNvbmZpZzogQ29uZmlnOyBoZWFkbGVzcz86IGJvb2xlYW4gfSkge1xuICAgICAgICBzdXBlcigpO1xuXG4gICAgICAgIHRoaXMuaGVhZGxlc3MgPSBoZWFkbGVzcztcblxuICAgICAgICBsb2dnZXIuaW5mbyhcbiAgICAgICAgICAgIGBTdGFydGluZyBKU1ByaXNtYXJpbmUgc2VydmVyIHZlcnNpb24gwqdldiR7dmVyc2lvbn3Cp3IgZm9yIE1pbmVjcmFmdDogQmVkcm9jayBFZGl0aW9uICR7SWRlbnRpZmllcnMuTWluZWNyYWZ0VmVyc2lvbnMuYXQoLTEpfSAocHJvdG9jb2wgdmVyc2lvbiDCp2Uke0lkZW50aWZpZXJzLlByb3RvY29sfcKncilgXG4gICAgICAgICk7XG5cbiAgICAgICAgdGhpcy5sb2dnZXIgPSBsb2dnZXI7XG4gICAgICAgIHRoaXMuY29uZmlnID0gY29uZmlnO1xuICAgICAgICB0aGlzLnBhY2tldFJlZ2lzdHJ5ID0gbmV3IFBhY2tldFJlZ2lzdHJ5KHRoaXMpO1xuICAgICAgICB0aGlzLml0ZW1NYW5hZ2VyID0gbmV3IEl0ZW1NYW5hZ2VyKHRoaXMpO1xuICAgICAgICB0aGlzLmJsb2NrTWFuYWdlciA9IG5ldyBCbG9ja01hbmFnZXIodGhpcyk7XG4gICAgICAgIHRoaXMud29ybGRNYW5hZ2VyID0gbmV3IFdvcmxkTWFuYWdlcih0aGlzKTtcbiAgICAgICAgaWYgKCF0aGlzLmhlYWRsZXNzKSB0aGlzLmNvbnNvbGUgPSBuZXcgQ29uc29sZSh0aGlzKTtcbiAgICAgICAgdGhpcy5jb21tYW5kTWFuYWdlciA9IG5ldyBDb21tYW5kTWFuYWdlcih0aGlzKTtcbiAgICAgICAgdGhpcy5xdWVyeU1hbmFnZXIgPSBuZXcgUXVlcnlNYW5hZ2VyKHRoaXMpO1xuICAgICAgICB0aGlzLmNoYXRNYW5hZ2VyID0gbmV3IENoYXRNYW5hZ2VyKHRoaXMpO1xuICAgICAgICB0aGlzLnBlcm1pc3Npb25NYW5hZ2VyID0gbmV3IFBlcm1pc3Npb25NYW5hZ2VyKHRoaXMpO1xuICAgICAgICB0aGlzLmJhbk1hbmFnZXIgPSBuZXcgQmFuTWFuYWdlcih0aGlzKTtcbiAgICB9XG5cbiAgICAvKipcbiAgICAgKiBFbmFibGVzIHRoZSBzZXJ2ZXIuXG4gICAgICogQHJldHVybnMge1Byb21pc2U8dm9pZD59IEEgcHJvbWlzZSB0aGF0IHJlc29sdmVzIHdoZW4gdGhlIHNlcnZlciBpcyBlbmFibGVkLlxuICAgICAqIEBpbnRlcm5hbFxuICAgICAqL1xuICAgIHByaXZhdGUgYXN5bmMgZW5hYmxlKCk6IFByb21pc2U8dm9pZD4ge1xuICAgICAgICBhd2FpdCBCbG9ja01hcHBpbmdzLmluaXRNYXBwaW5ncyh0aGlzKTtcblxuICAgICAgICBhd2FpdCB0aGlzLmNvbmZpZy5lbmFibGUoKTtcbiAgICAgICAgYXdhaXQgdGhpcy5jb25zb2xlPy5lbmFibGUoKTtcbiAgICAgICAgYXdhaXQgdGhpcy5sb2dnZXIuZW5hYmxlKCk7XG4gICAgICAgIGF3YWl0IHRoaXMucGVybWlzc2lvbk1hbmFnZXIuZW5hYmxlKCk7XG4gICAgICAgIGF3YWl0IHRoaXMucGFja2V0UmVnaXN0cnkuZW5hYmxlKCk7XG4gICAgICAgIGF3YWl0IHRoaXMuaXRlbU1hbmFnZXIuZW5hYmxlKCk7XG4gICAgICAgIGF3YWl0IHRoaXMuYmxvY2tNYW5hZ2VyLmVuYWJsZSgpO1xuICAgICAgICBhd2FpdCB0aGlzLmJhbk1hbmFnZXIuZW5hYmxlKCk7XG4gICAgICAgIGF3YWl0IHRoaXMuY29tbWFuZE1hbmFnZXIuZW5hYmxlKCk7XG4gICAgICAgIGF3YWl0IHRoaXMud29ybGRNYW5hZ2VyLmVuYWJsZSgpO1xuXG4gICAgICAgIHRoaXMubG9nZ2VyLnNldENvbnNvbGUodGhpcy5jb25zb2xlKTtcbiAgICB9XG5cbiAgICAvKipcbiAgICAgKiBEaXNhYmxlcyB0aGUgc2VydmVyLlxuICAgICAqIEByZXR1cm5zIHtQcm9taXNlPHZvaWQ+fSBBIHByb21pc2UgdGhhdCByZXNvbHZlcyB3aGVuIHRoZSBzZXJ2ZXIgaXMgZGlzYWJsZWQuXG4gICAgICogQGludGVybmFsXG4gICAgICovXG4gICAgcHJpdmF0ZSBhc3luYyBkaXNhYmxlKCk6IFByb21pc2U8dm9pZD4ge1xuICAgICAgICBhd2FpdCB0aGlzLndvcmxkTWFuYWdlci5kaXNhYmxlKCk7XG4gICAgICAgIGF3YWl0IHRoaXMuY29tbWFuZE1hbmFnZXIuZGlzYWJsZSgpO1xuICAgICAgICBhd2FpdCB0aGlzLmJhbk1hbmFnZXIuZGlzYWJsZSgpO1xuICAgICAgICBhd2FpdCB0aGlzLmJsb2NrTWFuYWdlci5kaXNhYmxlKCk7XG4gICAgICAgIGF3YWl0IHRoaXMuaXRlbU1hbmFnZXIuZGlzYWJsZSgpO1xuICAgICAgICBhd2FpdCB0aGlzLnBlcm1pc3Npb25NYW5hZ2VyLmRpc2FibGUoKTtcbiAgICAgICAgYXdhaXQgdGhpcy5wYWNrZXRSZWdpc3RyeS5kaXNhYmxlKCk7XG4gICAgICAgIGF3YWl0IHRoaXMuY29uZmlnLmRpc2FibGUoKTtcbiAgICAgICAgYXdhaXQgdGhpcy5sb2dnZXIuZGlzYWJsZSgpO1xuXG4gICAgICAgIEJsb2NrTWFwcGluZ3MucmVzZXQoKTtcbiAgICB9XG5cbiAgICBwdWJsaWMgZ2V0TWV0YWRhdGEoKSB7XG4gICAgICAgIGlmICghdGhpcy5yYWtuZXQpIHRocm93IG5ldyBFcnJvcignU2VydmVyIGlzIG5vdCBzdGFydGVkJyk7XG4gICAgICAgIHJldHVybiB0aGlzLnJha25ldC5zZXJ2ZXJOYW1lO1xuICAgIH1cblxuICAgIC8qKlxuICAgICAqIFJlbG9hZHMgdGhlIHNlcnZlci5cbiAgICAgKiBAcmV0dXJucyB7UHJvbWlzZTx2b2lkPn0gQSBwcm9taXNlIHRoYXQgcmVzb2x2ZXMgd2hlbiB0aGUgc2VydmVyIGlzIHJlbG9hZGVkLlxuICAgICAqIEByZW1hcmtzIFRoaXMgbWV0aG9kIGlzIGVxdWl2YWxlbnQgdG8gY2FsbGluZyB7QGxpbmsgU2VydmVyI2Rpc2FibGV9IGFuZCB7QGxpbmsgU2VydmVyI2VuYWJsZX0uXG4gICAgICogQHJlbWFya3MgVGhpcyBtZXRob2QgYW5kIGZ1bmN0aW9uYWxpdHkgaXMgdW5zdXBwb3J0ZWQgYW5kIHNob3VsZCBpZGVhbGx5IGJlIGNvbXBsZXRlbHkgYXZvaWRlZC5cbiAgICAgKi9cbiAgICBwdWJsaWMgYXN5bmMgcmVsb2FkKCk6IFByb21pc2U8dm9pZD4ge1xuICAgICAgICBhd2FpdCB0aGlzLmRpc2FibGUoKTtcbiAgICAgICAgYXdhaXQgdGhpcy5lbmFibGUoKTtcbiAgICB9XG5cbiAgICAvKipcbiAgICAgKiBTdGFydHMgdGhlIHNlcnZlci5cbiAgICAgKiBAcGFyYW0ge3N0cmluZ30gW3NlcnZlcklwPScwLjAuMC4wJ10gLSBUaGUgc2VydmVyIElQLlxuICAgICAqIEBwYXJhbSB7bnVtYmVyfSBbcG9ydD0xOTEzMl0gLSBUaGUgc2VydmVyIHBvcnQuXG4gICAgICogQHJldHVybnMge1Byb21pc2U8dm9pZD59IEEgcHJvbWlzZSB0aGF0IHJlc29sdmVzIHdoZW4gdGhlIHNlcnZlciBpcyBzdGFydGVkLlxuICAgICAqL1xuICAgIHB1YmxpYyBhc3luYyBib290c3RyYXAoc2VydmVySXAgPSAnMC4wLjAuMCcsIHBvcnQgPSAxOTEzMik6IFByb21pc2U8dm9pZD4ge1xuICAgICAgICBhd2FpdCB0aGlzLmVuYWJsZSgpO1xuXG4gICAgICAgIHRoaXMucmFrbmV0ID0gbmV3IFJha05ldExpc3RlbmVyKFxuICAgICAgICAgICAgdGhpcy5nZXRDb25maWcoKS5nZXRNYXhQbGF5ZXJzKCksXG4gICAgICAgICAgICB0aGlzLmdldENvbmZpZygpLmdldE9ubGluZU1vZGUoKSxcbiAgICAgICAgICAgIG5ldyBTZXJ2ZXJOYW1lKHRoaXMpLFxuICAgICAgICAgICAgdGhpcy5nZXRMb2dnZXIoKVxuICAgICAgICApO1xuICAgICAgICB0aGlzLnJha25ldC5zdGFydChzZXJ2ZXJJcCwgcG9ydCk7XG5cbiAgICAgICAgdGhpcy5yYWtuZXQub24oJ29wZW5Db25uZWN0aW9uJywgYXN5bmMgKHNlc3Npb246IFJha05ldFNlc3Npb24pID0+IHtcbiAgICAgICAgICAgIGNvbnN0IGV2ZW50ID0gbmV3IFJha25ldENvbm5lY3RFdmVudChzZXNzaW9uKTtcbiAgICAgICAgICAgIGF3YWl0IHRoaXMuZW1pdCgncmFrbmV0Q29ubmVjdCcsIGV2ZW50KTtcblxuICAgICAgICAgICAgaWYgKGV2ZW50LmlzQ2FuY2VsbGVkKCkpIHtcbiAgICAgICAgICAgICAgICBzZXNzaW9uLmRpc2Nvbm5lY3QoKTtcbiAgICAgICAgICAgICAgICByZXR1cm47XG4gICAgICAgICAgICB9XG5cbiAgICAgICAgICAgIGNvbnN0IHRva2VuID0gc2Vzc2lvbi5nZXRBZGRyZXNzKCkudG9Ub2tlbigpO1xuICAgICAgICAgICAgaWYgKHRoaXMuc2Vzc2lvbk1hbmFnZXIuaGFzKHRva2VuKSkge1xuICAgICAgICAgICAgICAgIHRoaXMubG9nZ2VyLmVycm9yKGBBbm90aGVyIGNsaWVudCB3aXRoIHRva2VuICgke3Rva2VufSkgaXMgYWxyZWFkeSBjb25uZWN0ZWQhYCk7XG4gICAgICAgICAgICAgICAgc2Vzc2lvbi5kaXNjb25uZWN0KCdBbHJlYWR5IGNvbm5lY3RlZCBmcm9tIGFub3RoZXIgbG9jYXRpb24nKTtcbiAgICAgICAgICAgICAgICByZXR1cm47XG4gICAgICAgICAgICB9XG5cbiAgICAgICAgICAgIGNvbnN0IHRpbWVyID0gbmV3IFRpbWVyKCk7XG4gICAgICAgICAgICB0aGlzLmxvZ2dlci5kZWJ1ZyhgJHt0b2tlbn0gaXMgYXR0ZW1wdGluZyB0byBjb25uZWN0YCk7XG4gICAgICAgICAgICB0aGlzLnNlc3Npb25NYW5hZ2VyLmFkZCh0b2tlbiwgbmV3IENsaWVudENvbm5lY3Rpb24oc2Vzc2lvbiwgdGhpcy5sb2dnZXIpKTtcbiAgICAgICAgICAgIHRoaXMubG9nZ2VyLnZlcmJvc2UoYE5ldyBjb25uZWN0aW9uIGhhbmRsaW5nIHRvb2sgwqdlJHt0aW1lci5zdG9wKCl9IG1zwqdyYCk7XG4gICAgICAgIH0pO1xuXG4gICAgICAgIHRoaXMucmFrbmV0Lm9uKCdjbG9zZUNvbm5lY3Rpb24nLCBhc3luYyAoaW5ldEFkZHI6IEluZXRBZGRyZXNzLCByZWFzb246IHN0cmluZykgPT4ge1xuICAgICAgICAgICAgY29uc3QgZXZlbnQgPSBuZXcgUmFrbmV0RGlzY29ubmVjdEV2ZW50KGluZXRBZGRyLCByZWFzb24pO1xuICAgICAgICAgICAgYXdhaXQgdGhpcy5lbWl0KCdyYWtuZXREaXNjb25uZWN0JywgZXZlbnQpO1xuXG4gICAgICAgICAgICBjb25zdCB0aW1lID0gRGF0ZS5ub3coKTtcbiAgICAgICAgICAgIGNvbnN0IHRva2VuID0gaW5ldEFkZHIudG9Ub2tlbigpO1xuXG4gICAgICAgICAgICBjb25zdCBzZXNzaW9uID0gdGhpcy5zZXNzaW9uTWFuYWdlci5nZXQodG9rZW4pO1xuICAgICAgICAgICAgaWYgKCFzZXNzaW9uKSB7XG4gICAgICAgICAgICAgICAgdGhpcy5sb2dnZXIuZGVidWcoYENhbm5vdCByZW1vdmUgY29ubmVjdGlvbiBmcm9tIG5vbi1leGlzdGluZyBwbGF5ZXIgKCR7dG9rZW59KWApO1xuICAgICAgICAgICAgICAgIHJldHVybjtcbiAgICAgICAgICAgIH1cblxuICAgICAgICAgICAgYXdhaXQgc2Vzc2lvbi5jbG9zZVBsYXllclNlc3Npb24oKTtcblxuICAgICAgICAgICAgdGhpcy5zZXNzaW9uTWFuYWdlci5yZW1vdmUodG9rZW4pO1xuICAgICAgICAgICAgdGhpcy5sb2dnZXIuZGVidWcoYCR7dG9rZW59IGRpc2Nvbm5lY3RlZCBkdWUgdG8gJHtyZWFzb259YCk7XG4gICAgICAgICAgICB0aGlzLmxvZ2dlci5kZWJ1ZyhgUGxheWVyIGRlc3RydWN0aW9uIHRvb2sgYWJvdXQgJHtEYXRlLm5vdygpIC0gdGltZX0gbXNgKTtcbiAgICAgICAgfSk7XG5cbiAgICAgICAgdGhpcy5yYWtuZXQub24oJ2VuY2Fwc3VsYXRlZCcsIGFzeW5jIChwYWNrZXQ6IGFueSwgaW5ldEFkZHI6IEluZXRBZGRyZXNzKSA9PiB7XG4gICAgICAgICAgICBjb25zdCBldmVudCA9IG5ldyBSYWtuZXRFbmNhcHN1bGF0ZWRQYWNrZXRFdmVudChpbmV0QWRkciwgcGFja2V0KTtcbiAgICAgICAgICAgIGF3YWl0IHRoaXMuZW1pdCgncmFrbmV0RW5jYXBzdWxhdGVkUGFja2V0JywgZXZlbnQpO1xuXG4gICAgICAgICAgICBsZXQgY29ubmVjdGlvbjogQ2xpZW50Q29ubmVjdGlvbiB8IG51bGw7XG4gICAgICAgICAgICBpZiAoKGNvbm5lY3Rpb24gPSB0aGlzLnNlc3Npb25NYW5hZ2VyLmdldChpbmV0QWRkci50b1Rva2VuKCkpID8/IG51bGwpID09PSBudWxsKSB7XG4gICAgICAgICAgICAgICAgdGhpcy5sb2dnZXIuZXJyb3IoYEdvdCBhIHBhY2tldCBmcm9tIGEgY2xvc2VkIGNvbm5lY3Rpb24gKCR7aW5ldEFkZHIudG9Ub2tlbigpfSlgKTtcbiAgICAgICAgICAgICAgICByZXR1cm47XG4gICAgICAgICAgICB9XG5cbiAgICAgICAgICAgIHRyeSB7XG4gICAgICAgICAgICAgICAgLy8gUmVhZCBiYXRjaCBjb250ZW50IGFuZCBoYW5kbGUgdGhlbVxuICAgICAgICAgICAgICAgIGNvbnN0IGJhdGNoZWQgPSBuZXcgQmF0Y2hQYWNrZXQocGFja2V0LmNvbnRlbnQpO1xuICAgICAgICAgICAgICAgIGJhdGNoZWQuY29tcHJlc3NlZCA9IGNvbm5lY3Rpb24uaGFzQ29tcHJlc3Npb247XG5cbiAgICAgICAgICAgICAgICAvLyBSZWFkIGFsbCBwYWNrZXRzIGluc2lkZSBiYXRjaCBhbmQgaGFuZGxlIHRoZW1cbiAgICAgICAgICAgICAgICBmb3IgKGNvbnN0IGJ1ZiBvZiBhd2FpdCBiYXRjaGVkLmFzeW5jRGVjb2RlKCkpIHtcbiAgICAgICAgICAgICAgICAgICAgY29uc3QgcGlkID0gYnVmWzBdITtcblxuICAgICAgICAgICAgICAgICAgICBpZiAoIXRoaXMucGFja2V0UmVnaXN0cnkuZ2V0UGFja2V0cygpLmhhcyhwaWQpKSB7XG4gICAgICAgICAgICAgICAgICAgICAgICB0aGlzLmxvZ2dlci53YXJuKGBQYWNrZXQgMHgke3BpZC50b1N0cmluZygxNil9IGlzbid0IGltcGxlbWVudGVkYCk7XG4gICAgICAgICAgICAgICAgICAgICAgICBjb250aW51ZTtcbiAgICAgICAgICAgICAgICAgICAgfVxuXG4gICAgICAgICAgICAgICAgICAgIC8vIEdldCBwYWNrZXQgZnJvbSByZWdpc3RyeVxuICAgICAgICAgICAgICAgICAgICBjb25zdCBwYWNrZXQgPSBuZXcgKHRoaXMucGFja2V0UmVnaXN0cnkuZ2V0UGFja2V0cygpLmdldChwaWQpISkoYnVmKTtcblxuICAgICAgICAgICAgICAgICAgICB0cnkge1xuICAgICAgICAgICAgICAgICAgICAgICAgcGFja2V0LmRlY29kZSgpO1xuICAgICAgICAgICAgICAgICAgICB9IGNhdGNoIChlcnJvcjogdW5rbm93bikge1xuICAgICAgICAgICAgICAgICAgICAgICAgdGhpcy5sb2dnZXIuZXJyb3IoZXJyb3IpO1xuICAgICAgICAgICAgICAgICAgICAgICAgdGhpcy5sb2dnZXIuZXJyb3IoYEVycm9yIHdoaWxlIGRlY29kaW5nIHBhY2tldDogJHtwYWNrZXQuY29uc3RydWN0b3IubmFtZX06ICR7ZXJyb3J9YCk7XG4gICAgICAgICAgICAgICAgICAgICAgICBjb250aW51ZTtcbiAgICAgICAgICAgICAgICAgICAgfVxuXG4gICAgICAgICAgICAgICAgICAgIHRyeSB7XG4gICAgICAgICAgICAgICAgICAgICAgICBjb25zdCBoYW5kbGVyID0gdGhpcy5wYWNrZXRSZWdpc3RyeS5nZXRIYW5kbGVyKHBpZCk7XG4gICAgICAgICAgICAgICAgICAgICAgICB0aGlzLmxvZ2dlci5zaWxseShgUmVjZWl2ZWQgwqdiJHtwYWNrZXQuY29uc3RydWN0b3IubmFtZX3Cp3IgcGFja2V0YCk7XG4gICAgICAgICAgICAgICAgICAgICAgICBhd2FpdCAoaGFuZGxlciBhcyBhbnkpLmhhbmRsZShwYWNrZXQsIHRoaXMsIGNvbm5lY3Rpb24uZ2V0UGxheWVyU2Vzc2lvbigpID8/IGNvbm5lY3Rpb24pO1xuICAgICAgICAgICAgICAgICAgICB9IGNhdGNoIChlcnJvcjogdW5rbm93bikge1xuICAgICAgICAgICAgICAgICAgICAgICAgdGhpcy5sb2dnZXIuZXJyb3IoYEhhbmRsZXIgZXJyb3IgJHtwYWNrZXQuY29uc3RydWN0b3IubmFtZX0taGFuZGxlcjogKCR7ZXJyb3J9KWApO1xuICAgICAgICAgICAgICAgICAgICAgICAgdGhpcy5sb2dnZXIuZXJyb3IoZXJyb3IpO1xuICAgICAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgfSBjYXRjaCAoZXJyb3I6IHVua25vd24pIHtcbiAgICAgICAgICAgICAgICB0aGlzLmxvZ2dlci5lcnJvcihlcnJvcik7XG4gICAgICAgICAgICB9XG4gICAgICAgIH0pO1xuXG4gICAgICAgIHRoaXMucmFrbmV0Lm9uKCdyYXcnLCBhc3luYyAoYnVmZmVyOiBCdWZmZXIsIGluZXRBZGRyOiBJbmV0QWRkcmVzcykgPT4ge1xuICAgICAgICAgICAgaWYgKCF0aGlzLmNvbmZpZy5nZXRFbmFibGVRdWVyeSgpKSByZXR1cm47XG5cbiAgICAgICAgICAgIHRyeSB7XG4gICAgICAgICAgICAgICAgYXdhaXQgdGhpcy5xdWVyeU1hbmFnZXIub25SYXcoYnVmZmVyLCBpbmV0QWRkcik7XG4gICAgICAgICAgICB9IGNhdGNoIChlcnJvcjogdW5rbm93bikge1xuICAgICAgICAgICAgICAgIHRoaXMubG9nZ2VyLnZlcmJvc2UoYFF1ZXJ5TWFuYWdlciBlbmNvdW50ZXJlZCBhbiBlcnJvcmApO1xuICAgICAgICAgICAgICAgIHRoaXMubG9nZ2VyLmVycm9yKGVycm9yKTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgfSk7XG5cbiAgICAgICAgaWYgKHRoaXMuY29uZmlnLmdldEVuYWJsZVRpY2tpbmcoKSkge1xuICAgICAgICAgICAgbGV0IHN0YXJ0VGltZSA9IERhdGUubm93KCk7XG4gICAgICAgICAgICBsZXQgdHBzU3RhcnRUaW1lID0gRGF0ZS5ub3coKTtcbiAgICAgICAgICAgIGxldCBsYXN0VGlja1RpbWUgPSBEYXRlLm5vdygpO1xuICAgICAgICAgICAgbGV0IHRwc1N0YXJ0VGljayA9IHRoaXMuZ2V0VGljaygpO1xuICAgICAgICAgICAgY29uc3QgdGljayA9IGFzeW5jICgpID0+IHtcbiAgICAgICAgICAgICAgICBpZiAodGhpcy5zdG9wcGluZykgcmV0dXJuO1xuXG4gICAgICAgICAgICAgICAgY29uc3QgZXZlbnQgPSBuZXcgVGlja0V2ZW50KHRoaXMuZ2V0VGljaygpKTtcbiAgICAgICAgICAgICAgICB2b2lkIHRoaXMuZW1pdCgndGljaycsIGV2ZW50KTtcblxuICAgICAgICAgICAgICAgIGNvbnN0IHRpY2tzUGVyU2Vjb25kID0gMTAwMCAvIFNlcnZlci5NSU5FQ1JBRlRfVElDS19USU1FX01TO1xuXG4gICAgICAgICAgICAgICAgLy8gVXBkYXRlIGFsbCB3b3JsZHMuXG4gICAgICAgICAgICAgICAgYXdhaXQgUHJvbWlzZS5hbGwodGhpcy53b3JsZE1hbmFnZXIuZ2V0V29ybGRzKCkubWFwKCh3b3JsZCkgPT4gd29ybGQudXBkYXRlKGV2ZW50LmdldFRpY2soKSkpKTtcblxuICAgICAgICAgICAgICAgIGlmICh0aGlzLmNvbmZpZy5nZXRFbmFibGVQcm9jZXNzVGl0bGUoKSAmJiB0aGlzLmdldFRpY2soKSAlIHRpY2tzUGVyU2Vjb25kID09PSAwICYmICF0aGlzLmhlYWRsZXNzKSB7XG4gICAgICAgICAgICAgICAgICAgIC8vIFVwZGF0ZSB0aGUgcHJvY2VzcyB0aXRsZSB3aXRoIFRQUyBhbmQgdGljay5cbiAgICAgICAgICAgICAgICAgICAgcHJvY2Vzcy50aXRsZSA9IGBUUFM6ICR7dGhpcy5nZXRUUFMoKS50b0ZpeGVkKDIpfSB8IFRpY2s6ICR7dGhpcy5nZXRUaWNrKCl9IHwgJHtwcm9jZXNzLnRpdGxlLnNwbGl0KCd8ICcpLmF0KC0xKSF9YDtcbiAgICAgICAgICAgICAgICB9XG5cbiAgICAgICAgICAgICAgICB0aGlzLmN1cnJlbnRUaWNrKys7XG4gICAgICAgICAgICAgICAgY29uc3QgZW5kVGltZSA9IERhdGUubm93KCk7XG4gICAgICAgICAgICAgICAgY29uc3QgZWxhcHNlZFRpbWUgPSBlbmRUaW1lIC0gc3RhcnRUaW1lO1xuICAgICAgICAgICAgICAgIGNvbnN0IGV4cGVjdGVkRWxhcHNlZFRpbWUgPSB0aGlzLmdldFRpY2soKSAqIFNlcnZlci5NSU5FQ1JBRlRfVElDS19USU1FX01TO1xuICAgICAgICAgICAgICAgIGNvbnN0IGV4ZWN1dGlvblRpbWUgPSBlbmRUaW1lIC0gbGFzdFRpY2tUaW1lO1xuXG4gICAgICAgICAgICAgICAgLy8gQWRqdXN0IHNsZWVwVGltZSBiYXNlZCBvbiBleGVjdXRpb24gc3BlZWQuXG4gICAgICAgICAgICAgICAgbGV0IHNsZWVwVGltZSA9IFNlcnZlci5NSU5FQ1JBRlRfVElDS19USU1FX01TIC0gZXhlY3V0aW9uVGltZTtcbiAgICAgICAgICAgICAgICBpZiAoZWxhcHNlZFRpbWUgPCBleHBlY3RlZEVsYXBzZWRUaW1lKSB7XG4gICAgICAgICAgICAgICAgICAgIC8vIElmIHdlJ3JlIHJ1bm5pbmcgZmFzdGVyIHRoYW4gZXhwZWN0ZWQsIGluY3JlYXNlIHNsZWVwVGltZS5cbiAgICAgICAgICAgICAgICAgICAgc2xlZXBUaW1lICs9IGV4cGVjdGVkRWxhcHNlZFRpbWUgLSBlbGFwc2VkVGltZTtcbiAgICAgICAgICAgICAgICB9IGVsc2UgaWYgKGVsYXBzZWRUaW1lID4gZXhwZWN0ZWRFbGFwc2VkVGltZSkge1xuICAgICAgICAgICAgICAgICAgICAvLyBJZiB3ZSdyZSBydW5uaW5nIHNsb3dlciB0aGFuIGV4cGVjdGVkLCBkZWNyZWFzZSBzbGVlcFRpbWUgYnV0IGRvbid0IGxldCBpdCBnbyBiZWxvdyAwLlxuICAgICAgICAgICAgICAgICAgICBzbGVlcFRpbWUgPSBNYXRoLm1heCgwLCBzbGVlcFRpbWUgLSAoZWxhcHNlZFRpbWUgLSBleHBlY3RlZEVsYXBzZWRUaW1lKSk7XG4gICAgICAgICAgICAgICAgfVxuXG4gICAgICAgICAgICAgICAgLy8gQ2FsY3VsYXRlIHRwcyBiYXNlZCBvbiB0aGUgYWN0dWFsIGVsYXBzZWQgdGltZSBzaW5jZSB0aGUgc3RhcnQgb2YgdGhlIHRpY2suXG4gICAgICAgICAgICAgICAgaWYgKHRwc1N0YXJ0VGltZSAhPT0gZW5kVGltZSkge1xuICAgICAgICAgICAgICAgICAgICB0aGlzLnRwcyA9ICgodGhpcy5nZXRUaWNrKCkgLSB0cHNTdGFydFRpY2spICogMTAwMCkgLyAoZW5kVGltZSAtIHRwc1N0YXJ0VGltZSk7XG4gICAgICAgICAgICAgICAgfVxuXG4gICAgICAgICAgICAgICAgaWYgKGVuZFRpbWUgLSB0cHNTdGFydFRpbWUgPj0gMTAwMCkge1xuICAgICAgICAgICAgICAgICAgICB0cHNTdGFydFRpY2sgPSB0aGlzLmdldFRpY2soKTtcbiAgICAgICAgICAgICAgICAgICAgdHBzU3RhcnRUaW1lID0gZW5kVGltZTtcbiAgICAgICAgICAgICAgICB9XG5cbiAgICAgICAgICAgICAgICB0aGlzLnRwcyA9IE1hdGgubWluKHRoaXMudHBzLCAyMCk7IC8vIEVuc3VyZSB0cHMgZG9lcyBub3QgZXhjZWVkIDIwXG5cbiAgICAgICAgICAgICAgICBsYXN0VGlja1RpbWUgPSBlbmRUaW1lO1xuICAgICAgICAgICAgICAgIHRoaXMudGlja2VyVGltZXIgPSBzZXRUaW1lb3V0KHRpY2ssIE1hdGgubWF4KDAsIHNsZWVwVGltZSkpO1xuICAgICAgICAgICAgICAgIHRoaXMudGlja2VyVGltZXIudW5yZWYoKTtcbiAgICAgICAgICAgIH07XG5cbiAgICAgICAgICAgIC8vIFN0YXJ0IHRpY2tpbmdcbiAgICAgICAgICAgIHZvaWQgdGljaygpO1xuICAgICAgICB9XG5cbiAgICAgICAgdGhpcy5sb2dnZXIuaW5mbyhgSlNQcmlzbWFyaW5lIGlzIG5vdyBsaXN0ZW5pbmcgb24gcG9ydCDCp2Ike3BvcnR9YCk7XG4gICAgfVxuXG4gICAgLyoqXG4gICAgICogS2lsbHMgdGhlIHNlcnZlciBhc3luY2hyb25vdXNseS5cbiAgICAgKiBAcGFyYW0ge29iamVjdH0gW29wdGlvbnNdIC0gVGhlIG9wdGlvbnMuXG4gICAgICogQHBhcmFtIHtib29sZWFufSBbb3B0aW9ucy5jcmFzaF0gLSBJZiB0aGUgc2VydmVyIHNob3VsZCBjcmFzaC5cbiAgICAgKiBAcGFyYW0ge2Jvb2xlYW59IFtvcHRpb25zLnN0YXlBbGl2ZV0gLSBJZiB3ZSBzaG91bGQgbGV0IHRoZSBwcm9jZXNzIHN0YXkgYWxpdmUuXG4gICAgICogQHJldHVybnMge1Byb21pc2U8dm9pZD59IEEgcHJvbWlzZSB0aGF0IHJlc29sdmVzIHdoZW4gdGhlIHNlcnZlciBpcyBraWxsZWQuXG4gICAgICovXG4gICAgcHVibGljIGFzeW5jIHNodXRkb3duKG9wdGlvbnM/OiB7IGNyYXNoPzogYm9vbGVhbjsgc3RheUFsaXZlPzogYm9vbGVhbiB9KTogUHJvbWlzZTx2b2lkPiB7XG4gICAgICAgIGlmICh0aGlzLnN0b3BwaW5nKSByZXR1cm47XG4gICAgICAgIHRoaXMuc3RvcHBpbmcgPSB0cnVlO1xuXG4gICAgICAgIHRoaXMubG9nZ2VyLmluZm8oJ1N0b3BwaW5nIHNlcnZlcicsICdTZXJ2ZXIva2lsbCcpO1xuICAgICAgICBhd2FpdCB0aGlzLmNvbnNvbGU/LmRpc2FibGUoKTtcblxuICAgICAgICBjbGVhckludGVydmFsKHRoaXMudGlja2VyVGltZXIpO1xuXG4gICAgICAgIHRyeSB7XG4gICAgICAgICAgICAvLyBLaWNrIGFsbCBvbmxpbmUgcGxheWVycy5cbiAgICAgICAgICAgIGF3YWl0IHRoaXMuc2Vzc2lvbk1hbmFnZXIua2lja0FsbFBsYXllcnMoJ1NlcnZlciBjbG9zZWQuJyk7XG5cbiAgICAgICAgICAgIC8vIERpc2FibGUgYWxsIG1hbmFnZXJzLlxuICAgICAgICAgICAgYXdhaXQgdGhpcy5kaXNhYmxlKCk7XG5cbiAgICAgICAgICAgIC8vIGB0aGlzLnJha25ldGAgbWlnaHQgYmUgdW5kZWZpbmVkIGlmIHdlIGtpbGwgdGhlIHNlcnZlciByZWFsbHkgZWFybHkuXG4gICAgICAgICAgICB0aGlzLnJha25ldD8ua2lsbCgpO1xuXG4gICAgICAgICAgICAvLyBGaW5hbGx5LCByZW1vdmUgYWxsIGxpc3RlbmVycy5cbiAgICAgICAgICAgIHRoaXMucmVtb3ZlQWxsTGlzdGVuZXJzKCk7XG5cbiAgICAgICAgICAgIC8vIExvZ2dlciBpcyBubyBsb25nZXIgYXZhaWxhYmxlLlxuICAgICAgICAgICAgY29uc29sZS5kZWJ1ZygnU2VydmVyIHN0b3BwZWQsIEdvb2RieWUhXFxuJyk7XG5cbiAgICAgICAgICAgIGlmICghb3B0aW9ucz8uc3RheUFsaXZlKSBwcm9jZXNzLmV4aXQob3B0aW9ucz8uY3Jhc2ggPyAxIDogMCk7XG4gICAgICAgIH0gY2F0Y2ggKGVycm9yOiB1bmtub3duKSB7XG4gICAgICAgICAgICBjb25zb2xlLmVycm9yKGVycm9yKTtcbiAgICAgICAgICAgIGlmICghb3B0aW9ucz8uc3RheUFsaXZlKSBwcm9jZXNzLmV4aXQoMSk7XG4gICAgICAgIH1cbiAgICB9XG5cbiAgICBwdWJsaWMgYXN5bmMgYnJvYWRjYXN0UGFja2V0PFQgZXh0ZW5kcyBEYXRhUGFja2V0PihkYXRhUGFja2V0OiBUKTogUHJvbWlzZTx2b2lkPiB7XG4gICAgICAgIC8vIE1heWJlIGkgY2FuIGltcHJvdmUgdGhpcyBieSB1c2luZyB0aGUgVURQIGJyb2FkY2FzdCwgYWxsIHVuY29ubmVjdGVkIGNsaWVudHNcbiAgICAgICAgLy8gd2lsbCBpZ25vcmUgdGhlIGNvbm5lY3RlZCBwYWNrZXQgcHJvYmFibHksIGJ1dCBtYXkgY2F1c2UgaXNzdWVzLlxuICAgICAgICBmb3IgKGNvbnN0IG9ubGluZVBsYXllciBvZiB0aGlzLnNlc3Npb25NYW5hZ2VyLmdldEFsbFBsYXllcnMoKSkge1xuICAgICAgICAgICAgYXdhaXQgb25saW5lUGxheWVyLmdldE5ldHdvcmtTZXNzaW9uKCkuZ2V0Q29ubmVjdGlvbigpLnNlbmREYXRhUGFja2V0KGRhdGFQYWNrZXQpO1xuICAgICAgICB9XG4gICAgfVxuXG4gICAgLyoqXG4gICAgICogUmV0dXJucyB0aGUgc2VydmVyIHZlcnNpb24uXG4gICAgICogQHJldHVybnMge3N0cmluZ30gVGhlIHNlcnZlciB2ZXJzaW9uLlxuICAgICAqIEBleGFtcGxlXG4gICAgICogYGBgdHlwZXNjcmlwdFxuICAgICAqIGNvbnNvbGUubG9nKHNlcnZlci5nZXRWZXJzaW9uKCkpO1xuICAgICAqIGBgYFxuICAgICAqL1xuICAgIHB1YmxpYyBnZXRWZXJzaW9uKCk6IHN0cmluZyB7XG4gICAgICAgIHJldHVybiB2ZXJzaW9uO1xuICAgIH1cblxuICAgIC8qKlxuICAgICAqIFJldHVybnMgdGhlIGlkZW50aWZpZXJzLlxuICAgICAqIEByZXR1cm5zIHtJZGVudGlmaWVyc30gVGhlIGlkZW50aWZpZXJzLlxuICAgICAqL1xuICAgIHB1YmxpYyBnZXRJZGVudGlmaWVycygpOiB0eXBlb2YgSWRlbnRpZmllcnMge1xuICAgICAgICByZXR1cm4gSWRlbnRpZmllcnM7XG4gICAgfVxuXG4gICAgLyoqXG4gICAgICogUmV0dXJucyB0aGUgcXVlcnkgbWFuYWdlci5cbiAgICAgKiBAcmV0dXJucyB7UXVlcnlNYW5hZ2VyfSBUaGUgcXVlcnkgbWFuYWdlci5cbiAgICAgKi9cbiAgICBwdWJsaWMgZ2V0UXVlcnlNYW5hZ2VyKCk6IFF1ZXJ5TWFuYWdlciB7XG4gICAgICAgIHJldHVybiB0aGlzLnF1ZXJ5TWFuYWdlcjtcbiAgICB9XG5cbiAgICAvKipcbiAgICAgKiBSZXR1cm5zIHRoZSBjb21tYW5kIG1hbmFnZXIuXG4gICAgICogQHJldHVybnMge0NvbW1hbmRNYW5hZ2VyfSBUaGUgY29tbWFuZCBtYW5hZ2VyLlxuICAgICAqL1xuICAgIHB1YmxpYyBnZXRDb21tYW5kTWFuYWdlcigpOiBDb21tYW5kTWFuYWdlciB7XG4gICAgICAgIHJldHVybiB0aGlzLmNvbW1hbmRNYW5hZ2VyO1xuICAgIH1cblxuICAgIC8qKlxuICAgICAqIFJldHVybnMgdGhlIHBsYXllciBtYW5hZ2VyLlxuICAgICAqIEByZXR1cm5zIHtTZXNzaW9uTWFuYWdlcn0gVGhlIHBsYXllciBtYW5hZ2VyLlxuICAgICAqL1xuICAgIHB1YmxpYyBnZXRTZXNzaW9uTWFuYWdlcigpOiBTZXNzaW9uTWFuYWdlciB7XG4gICAgICAgIHJldHVybiB0aGlzLnNlc3Npb25NYW5hZ2VyO1xuICAgIH1cblxuICAgIC8qKlxuICAgICAqIFJldHVybnMgdGhlIHdvcmxkIG1hbmFnZXIuXG4gICAgICogQHJldHVybnMge1dvcmxkTWFuYWdlcn0gVGhlIHdvcmxkIG1hbmFnZXIuXG4gICAgICovXG4gICAgcHVibGljIGdldFdvcmxkTWFuYWdlcigpOiBXb3JsZE1hbmFnZXIge1xuICAgICAgICByZXR1cm4gdGhpcy53b3JsZE1hbmFnZXI7XG4gICAgfVxuXG4gICAgLyoqXG4gICAgICogUmV0dXJucyB0aGUgaXRlbSBtYW5hZ2VyLlxuICAgICAqIEByZXR1cm5zIHtJdGVtTWFuYWdlcn0gVGhlIGl0ZW0gbWFuYWdlci5cbiAgICAgKi9cbiAgICBwdWJsaWMgZ2V0SXRlbU1hbmFnZXIoKTogSXRlbU1hbmFnZXIge1xuICAgICAgICByZXR1cm4gdGhpcy5pdGVtTWFuYWdlcjtcbiAgICB9XG5cbiAgICAvKipcbiAgICAgKiBSZXR1cm5zIHRoZSBibG9jayBtYW5hZ2VyLlxuICAgICAqIEByZXR1cm5zIHtCbG9ja01hbmFnZXJ9IFRoZSBibG9jayBtYW5hZ2VyLlxuICAgICAqL1xuICAgIHB1YmxpYyBnZXRCbG9ja01hbmFnZXIoKTogQmxvY2tNYW5hZ2VyIHtcbiAgICAgICAgcmV0dXJuIHRoaXMuYmxvY2tNYW5hZ2VyO1xuICAgIH1cblxuICAgIC8qKlxuICAgICAqIFJldHVybnMgdGhlIGxvZ2dlci5cbiAgICAgKiBAcmV0dXJucyB7TG9nZ2VyQnVpbGRlcn0gVGhlIGxvZ2dlci5cbiAgICAgKiBAZXhhbXBsZVxuICAgICAqIGBgYHR5cGVzY3JpcHRcbiAgICAgKiAvLyBOb3JtYWwgbG9nOlxuICAgICAqIHNlcnZlci5nZXRMb2dnZXIoKS5pbmZvKCdIZWxsbywgd29ybGQhJyk7XG4gICAgICogLy8gRGVidWcgbG9nOlxuICAgICAqIHNlcnZlci5nZXRMb2dnZXIoKS5kZWJ1ZygnSGVsbG8sIHdvcmxkIScpO1xuICAgICAqIC8vIEVycm9yIGxvZzpcbiAgICAgKiBzZXJ2ZXIuZ2V0TG9nZ2VyKCkuZXJyb3IobmV3IEVycm9yKCdIZWxsbyBXb3JsZCcpKTtcbiAgICAgKiBgYGBcbiAgICAgKi9cbiAgICBwdWJsaWMgZ2V0TG9nZ2VyKCk6IExvZ2dlciB7XG4gICAgICAgIHJldHVybiB0aGlzLmxvZ2dlcjtcbiAgICB9XG5cbiAgICAvKipcbiAgICAgKiBSZXR1cm5zIHRoZSBwYWNrZXQgcmVnaXN0cnkuXG4gICAgICogQHJldHVybnMge1BhY2tldFJlZ2lzdHJ5fSBUaGUgcGFja2V0IHJlZ2lzdHJ5LlxuICAgICAqL1xuICAgIHB1YmxpYyBnZXRQYWNrZXRSZWdpc3RyeSgpOiBQYWNrZXRSZWdpc3RyeSB7XG4gICAgICAgIHJldHVybiB0aGlzLnBhY2tldFJlZ2lzdHJ5O1xuICAgIH1cblxuICAgIC8qKlxuICAgICAqIFJldHVybnMgdGhlIHJha25ldCBpbnN0YW5jZS5cbiAgICAgKiBAcmV0dXJucyB7UmFrTmV0TGlzdGVuZXIgfCB1bmRlZmluZWR9IFRoZSByYWtuZXQgaW5zdGFuY2UuXG4gICAgICovXG4gICAgcHVibGljIGdldFJha25ldCgpOiBSYWtOZXRMaXN0ZW5lciB8IHVuZGVmaW5lZCB7XG4gICAgICAgIHJldHVybiB0aGlzLnJha25ldDtcbiAgICB9XG5cbiAgICAvKipcbiAgICAgKiBSZXR1cm5zIHRoZSBjaGF0IG1hbmFnZXIuXG4gICAgICogQHJldHVybnMge0NoYXRNYW5hZ2VyfSBUaGUgY2hhdCBtYW5hZ2VyLlxuICAgICAqL1xuICAgIHB1YmxpYyBnZXRDaGF0TWFuYWdlcigpOiBDaGF0TWFuYWdlciB7XG4gICAgICAgIHJldHVybiB0aGlzLmNoYXRNYW5hZ2VyO1xuICAgIH1cblxuICAgIC8qKlxuICAgICAqIFJldHVybnMgdGhlIGNvbmZpZy5cbiAgICAgKiBAcmV0dXJucyB7Q29uZmlnfSBUaGUgY29uZmlnLlxuICAgICAqIEBleGFtcGxlXG4gICAgICogYGBgdHlwZXNjcmlwdFxuICAgICAqIGNvbnNvbGUubG9nKHNlcnZlci5nZXRDb25maWcoKS5nZXRNYXhQbGF5ZXJzKCkpOyAvLyAyMFxuICAgICAqIGBgYFxuICAgICAqL1xuICAgIHB1YmxpYyBnZXRDb25maWcoKTogQ29uZmlnIHtcbiAgICAgICAgcmV0dXJuIHRoaXMuY29uZmlnO1xuICAgIH1cblxuICAgIC8qKlxuICAgICAqIFJldHVybnMgdGhlIGNvbnNvbGUgaW5zdGFuY2UuXG4gICAgICogQHJldHVybnMge0NvbnNvbGUgfCB1bmRlZmluZWR9IFRoZSBjb25zb2xlIGluc3RhbmNlLlxuICAgICAqL1xuICAgIHB1YmxpYyBnZXRDb25zb2xlKCkge1xuICAgICAgICByZXR1cm4gdGhpcy5jb25zb2xlO1xuICAgIH1cblxuICAgIC8qKlxuICAgICAqIFJldHVybnMgdGhlIHBlcm1pc3Npb24gbWFuYWdlci5cbiAgICAgKiBAcmV0dXJucyB7UGVybWlzc2lvbk1hbmFnZXJ9IFRoZSBwZXJtaXNzaW9uIG1hbmFnZXIuXG4gICAgICovXG4gICAgcHVibGljIGdldFBlcm1pc3Npb25NYW5hZ2VyKCk6IFBlcm1pc3Npb25NYW5hZ2VyIHtcbiAgICAgICAgcmV0dXJuIHRoaXMucGVybWlzc2lvbk1hbmFnZXI7XG4gICAgfVxuXG4gICAgLyoqXG4gICAgICogUmV0dXJucyB0aGUgYmFuIG1hbmFnZXIuXG4gICAgICogQHJldHVybnMge0Jhbk1hbmFnZXJ9IFRoZSBiYW4gbWFuYWdlci5cbiAgICAgKi9cbiAgICBwdWJsaWMgZ2V0QmFuTWFuYWdlcigpOiBCYW5NYW5hZ2VyIHtcbiAgICAgICAgcmV0dXJuIHRoaXMuYmFuTWFuYWdlcjtcbiAgICB9XG5cbiAgICAvKipcbiAgICAgKiBSZXR1cm5zIHRoaXMgUHJpc21hcmluZSBpbnN0YW5jZS5cbiAgICAgKiBAcmV0dXJucyB7U2VydmVyfSBUaGUgUHJpc21hcmluZSBpbnN0YW5jZS5cbiAgICAgKi9cbiAgICBwdWJsaWMgZ2V0U2VydmVyKCk6IFNlcnZlciB7XG4gICAgICAgIHJldHVybiB0aGlzO1xuICAgIH1cblxuICAgIC8qKlxuICAgICAqIFJldHVybnMgdGhlIGN1cnJlbnQgVGljay5cbiAgICAgKiBAcmV0dXJucyB7bnVtYmVyfSBUaGUgY3VycmVudCBUaWNrLlxuICAgICAqL1xuICAgIHB1YmxpYyBnZXRUaWNrKCk6IG51bWJlciB7XG4gICAgICAgIHJldHVybiBOdW1iZXIodGhpcy5jdXJyZW50VGljayk7XG4gICAgfVxuXG4gICAgLyoqXG4gICAgICogUmV0dXJucyB0aGUgY3VycmVudCBUUFMuXG4gICAgICogQHJldHVybnMge251bWJlcn0gVGhlIGN1cnJlbnQgVFBTLlxuICAgICAqL1xuICAgIHB1YmxpYyBnZXRUUFMoKTogbnVtYmVyIHtcbiAgICAgICAgcmV0dXJuIE51bWJlci5wYXJzZUZsb2F0KHRoaXMudHBzLnRvRml4ZWQoMikpO1xuICAgIH1cbn1cbiJdLCJtYXBwaW5ncyI6Ijs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7OztBQWtDQSxJQUFxQixTQUFyQixNQUFxQixlQUFlLGFBQWE7Q0FDN0M7Q0FDQTtDQUNBO0NBQ0E7Q0FDQTtDQUNBLGlCQUFrQyxJQUFJLGVBQWU7Q0FDckQ7Q0FDQTtDQUNBO0NBQ0E7Q0FDQTtDQUNBO0NBQ0E7Q0FDQTs7Ozs7Q0FNQSxXQUFtQjs7Ozs7Q0FNbkI7Ozs7O0NBTUEsTUFBYzs7Ozs7Q0FNZCxjQUFzQjs7Ozs7Q0FNdEI7Q0FHQSxPQUF3Qix5QkFBeUIsTUFBTzs7Ozs7Ozs7Q0FTeEQsWUFBbUIsRUFBRSxRQUFRLFFBQVEsV0FBVyxTQUFpRTtFQUM3RyxNQUFNO0VBRU4sS0FBSyxXQUFXO0VBRWhCLE9BQU8sS0FDSCwyQ0FBMkMsUUFBUSxvQ0FBb0MsWUFBWSxrQkFBa0IsR0FBRyxFQUFFLEVBQUUsdUJBQXVCLFlBQVksU0FBUyxJQUM1SztFQUVBLEtBQUssU0FBUztFQUNkLEtBQUssU0FBUztFQUNkLEtBQUssaUJBQWlCLElBQUksZUFBZSxJQUFJO0VBQzdDLEtBQUssY0FBYyxJQUFJLFlBQVksSUFBSTtFQUN2QyxLQUFLLGVBQWUsSUFBSSxhQUFhLElBQUk7RUFDekMsS0FB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