UNPKG

@jsprismarine/prismarine

Version:

Dedicated Minecraft Bedrock Edition server written in TypeScript

467 lines (463 loc) 58.5 kB
'use strict'; Object.defineProperties(exports, { __esModule: { value: true }, [Symbol.toStringTag]: { value: 'Module' } }); const raknet = require('@jsprismarine/raknet'); const Console = require('./Console.cjs.cjs'); const SessionManager = require('./SessionManager.cjs.cjs'); const ban_BanManager = require('./ban/BanManager.cjs.cjs'); const block_BlockManager = require('./block/BlockManager.cjs.cjs'); const block_BlockMappings = require('./block/BlockMappings.cjs.cjs'); const chat_ChatManager = require('./chat/ChatManager.cjs.cjs'); const command_CommandManager = require('./command/CommandManager.cjs.cjs'); const events_EventEmitter = require('./events/EventEmitter.cjs.cjs'); require('@jsprismarine/math'); const events_raknet_RaknetConnectEvent = require('./events/raknet/RaknetConnectEvent.cjs.cjs'); const events_raknet_RaknetDisconnectEvent = require('./events/raknet/RaknetDisconnectEvent.cjs.cjs'); const events_raknet_RaknetEncapsulatedPacketEvent = require('./events/raknet/RaknetEncapsulatedPacketEvent.cjs.cjs'); const events_other_TickEvent = require('./events/other/TickEvent.cjs.cjs'); const item_ItemManager = require('./item/ItemManager.cjs.cjs'); const network_ClientConnection = require('./network/ClientConnection.cjs.cjs'); const network_Identifiers = require('./network/Identifiers.cjs.cjs'); const network_PacketRegistry = require('./network/PacketRegistry.cjs.cjs'); const network_packet_BatchPacket = require('./network/packet/BatchPacket.cjs.cjs'); const permission_PermissionManager = require('./permission/PermissionManager.cjs.cjs'); const query_QueryManager = require('./query/QueryManager.cjs.cjs'); const utils_Timer = require('./utils/Timer.cjs.cjs'); const world_WorldManager = require('./world/WorldManager.cjs.cjs'); const _package = require('./package.json.cjs.cjs'); class Server extends events_EventEmitter.EventEmitter { raknet; logger; config; console; packetRegistry; sessionManager = new SessionManager.default(); 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; // TODO: Move this somewhere else. 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${_package.version}§r for Minecraft: Bedrock Edition ${network_Identifiers.default.MinecraftVersions.at(-1)} (protocol version §e${network_Identifiers.default.Protocol}§r)` ); this.logger = logger; this.config = config; this.packetRegistry = new network_PacketRegistry.default(this); this.itemManager = new item_ItemManager.default(this); this.blockManager = new block_BlockManager.default(this); this.worldManager = new world_WorldManager.default(this); if (!this.headless) this.console = new Console.default(this); this.commandManager = new command_CommandManager.CommandManager(this); this.queryManager = new query_QueryManager.QueryManager(this); this.chatManager = new chat_ChatManager.ChatManager(this); this.permissionManager = new permission_PermissionManager.PermissionManager(this); this.banManager = new ban_BanManager.default(this); } /** * Enables the server. * @returns {Promise<void>} A promise that resolves when the server is enabled. * @internal */ async enable() { await block_BlockMappings.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(); block_BlockMappings.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 raknet.RakNetListener( this.getConfig().getMaxPlayers(), this.getConfig().getOnlineMode(), new raknet.ServerName(this), this.getLogger() ); this.raknet.start(serverIp, port); this.raknet.on("openConnection", async (session) => { const event = new events_raknet_RaknetConnectEvent.default(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 utils_Timer.default(); this.logger.debug(`${token} is attempting to connect`); this.sessionManager.add(token, new network_ClientConnection.default(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 events_raknet_RaknetDisconnectEvent.default(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 events_raknet_RaknetEncapsulatedPacketEvent.default(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 network_packet_BatchPacket.default(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 packet2 = new (this.packetRegistry.getPackets().get(pid))(buf); try { packet2.decode(); } catch (error) { this.logger.error(error); this.logger.error(`Error while decoding packet: ${packet2.constructor.name}: ${error}`); continue; } try { const handler = this.packetRegistry.getHandler(pid); this.logger.silly(`Received §b${packet2.constructor.name}§r packet`); await handler.handle(packet2, this, connection.getPlayerSession() ?? connection); } catch (error) { this.logger.error(`Handler error ${packet2.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 events_other_TickEvent.default(this.getTick()); void 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, sleepTime); this.tickerTimer.unref(); }; void 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 _package.version; } /** * Returns the identifiers. * @returns {Identifiers} The identifiers. */ getIdentifiers() { return network_Identifiers.default; } /** * 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)); } } exports.default = Server; //# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiU2VydmVyLmNqcy5janMiLCJzb3VyY2VzIjpbIi4uL3NyYy9TZXJ2ZXIudHMiXSwic291cmNlc0NvbnRlbnQiOlsiaW1wb3J0IHsgUmFrTmV0TGlzdGVuZXIsIFNlcnZlck5hbWUgfSBmcm9tICdAanNwcmlzbWFyaW5lL3Jha25ldCc7XG5pbXBvcnQgQ29uc29sZSBmcm9tICcuL0NvbnNvbGUnO1xuaW1wb3J0IFNlc3Npb25NYW5hZ2VyIGZyb20gJy4vU2Vzc2lvbk1hbmFnZXInO1xuaW1wb3J0IEJhbk1hbmFnZXIgZnJvbSAnLi9iYW4vQmFuTWFuYWdlcic7XG5pbXBvcnQgQmxvY2tNYW5hZ2VyIGZyb20gJy4vYmxvY2svQmxvY2tNYW5hZ2VyJztcbmltcG9ydCB7IEJsb2NrTWFwcGluZ3MgfSBmcm9tICcuL2Jsb2NrL0Jsb2NrTWFwcGluZ3MnO1xuaW1wb3J0IHsgQ2hhdE1hbmFnZXIgfSBmcm9tICcuL2NoYXQvQ2hhdE1hbmFnZXInO1xuaW1wb3J0IHsgQ29tbWFuZE1hbmFnZXIgfSBmcm9tICcuL2NvbW1hbmQvQ29tbWFuZE1hbmFnZXInO1xuaW1wb3J0IHsgRXZlbnRFbWl0dGVyIH0gZnJvbSAnLi9ldmVudHMvRXZlbnRFbWl0dGVyJztcbmltcG9ydCB7IFRpY2tFdmVudCB9IGZyb20gJy4vZXZlbnRzL0V2ZW50cyc7XG5pbXBvcnQgUmFrbmV0Q29ubmVjdEV2ZW50IGZyb20gJy4vZXZlbnRzL3Jha25ldC9SYWtuZXRDb25uZWN0RXZlbnQnO1xuaW1wb3J0IFJha25ldERpc2Nvbm5lY3RFdmVudCBmcm9tICcuL2V2ZW50cy9yYWtuZXQvUmFrbmV0RGlzY29ubmVjdEV2ZW50JztcbmltcG9ydCBSYWtuZXRFbmNhcHN1bGF0ZWRQYWNrZXRFdmVudCBmcm9tICcuL2V2ZW50cy9yYWtuZXQvUmFrbmV0RW5jYXBzdWxhdGVkUGFja2V0RXZlbnQnO1xuaW1wb3J0IEl0ZW1NYW5hZ2VyIGZyb20gJy4vaXRlbS9JdGVtTWFuYWdlcic7XG5pbXBvcnQgQ2xpZW50Q29ubmVjdGlvbiBmcm9tICcuL25ldHdvcmsvQ2xpZW50Q29ubmVjdGlvbic7XG5pbXBvcnQgSWRlbnRpZmllcnMgZnJvbSAnLi9uZXR3b3JrL0lkZW50aWZpZXJzJztcbmltcG9ydCBQYWNrZXRSZWdpc3RyeSBmcm9tICcuL25ldHdvcmsvUGFja2V0UmVnaXN0cnknO1xuaW1wb3J0IHR5cGUgeyBEYXRhUGFja2V0IH0gZnJvbSAnLi9uZXR3b3JrL1BhY2tldHMnO1xuaW1wb3J0IEJhdGNoUGFja2V0IGZyb20gJy4vbmV0d29yay9wYWNrZXQvQmF0Y2hQYWNrZXQnO1xuaW1wb3J0IHsgUGVybWlzc2lvbk1hbmFnZXIgfSBmcm9tICcuL3Blcm1pc3Npb24vUGVybWlzc2lvbk1hbmFnZXInO1xuaW1wb3J0IHsgUXVlcnlNYW5hZ2VyIH0gZnJvbSAnLi9xdWVyeS9RdWVyeU1hbmFnZXInO1xuaW1wb3J0IFRpbWVyIGZyb20gJy4vdXRpbHMvVGltZXInO1xuaW1wb3J0IFdvcmxkTWFuYWdlciBmcm9tICcuL3dvcmxkL1dvcmxkTWFuYWdlcic7XG5cbmltcG9ydCB0eXBlIHsgSW5ldEFkZHJlc3MsIFJha05ldFNlc3Npb24gfSBmcm9tICdAanNwcmlzbWFyaW5lL3Jha25ldCc7XG5pbXBvcnQgdHlwZSB7IENvbmZpZyB9IGZyb20gJy4vY29uZmlnL0NvbmZpZyc7XG5cbmltcG9ydCB0eXBlIHsgTG9nZ2VyIH0gZnJvbSAnQGpzcHJpc21hcmluZS9sb2dnZXInO1xuaW1wb3J0IHsgdmVyc2lvbiB9IGZyb20gJy4uL3BhY2thZ2UuanNvbicgd2l0aCB7IHR5cGU6ICdqc29uJyB9O1xuXG4vKipcbiAqIEpTUHJpc21hcmluZSdzIG1haW4gc2VydmVyIGNsYXNzLlxuICogQHB1YmxpY1xuICovXG5leHBvcnQgZGVmYXVsdCBjbGFzcyBTZXJ2ZXIgZXh0ZW5kcyBFdmVudEVtaXR0ZXIge1xuICAgIHByaXZhdGUgcmFrbmV0OiBSYWtOZXRMaXN0ZW5lciB8IHVuZGVmaW5lZDtcbiAgICBwcml2YXRlIHJlYWRvbmx5IGxvZ2dlcjogTG9nZ2VyO1xuICAgIHByaXZhdGUgcmVhZG9ubHkgY29uZmlnOiBDb25maWc7XG4gICAgcHJpdmF0ZSByZWFkb25seSBjb25zb2xlOiBDb25zb2xlIHwgdW5kZWZpbmVkO1xuICAgIHByaXZhdGUgcmVhZG9ubHkgcGFja2V0UmVnaXN0cnk6IFBhY2tldFJlZ2lzdHJ5O1xuICAgIHByaXZhdGUgcmVhZG9ubHkgc2Vzc2lvbk1hbmFnZXIgPSBuZXcgU2Vzc2lvbk1hbmFnZXIoKTtcbiAgICBwcml2YXRlIHJlYWRvbmx5IGNvbW1hbmRNYW5hZ2VyOiBDb21tYW5kTWFuYWdlcjtcbiAgICBwcml2YXRlIHJlYWRvbmx5IHdvcmxkTWFuYWdlcjogV29ybGRNYW5hZ2VyO1xuICAgIHByaXZhdGUgcmVhZG9ubHkgaXRlbU1hbmFnZXI6IEl0ZW1NYW5hZ2VyO1xuICAgIHByaXZhdGUgcmVhZG9ubHkgYmxvY2tNYW5hZ2VyOiBCbG9ja01hbmFnZXI7XG4gICAgcHJpdmF0ZSByZWFkb25seSBxdWVyeU1hbmFnZXI6IFF1ZXJ5TWFuYWdlcjtcbiAgICBwcml2YXRlIHJlYWRvbmx5IGNoYXRNYW5hZ2VyOiBDaGF0TWFuYWdlcjtcbiAgICBwcml2YXRlIHJlYWRvbmx5IHBlcm1pc3Npb25NYW5hZ2VyOiBQZXJtaXNzaW9uTWFuYWdlcjtcbiAgICBwcml2YXRlIHJlYWRvbmx5IGJhbk1hbmFnZXI6IEJhbk1hbmFnZXI7XG5cbiAgICAvKipcbiAgICAgKiBJZiB0aGUgc2VydmVyIGlzIHN0b3BwaW5nLlxuICAgICAqIEBpbnRlcm5hbFxuICAgICAqL1xuICAgIHByaXZhdGUgc3RvcHBpbmcgPSBmYWxzZTtcblxuICAgIC8qKlxuICAgICAqIFRoZSBjdXJyZW50IHRpY2tlciB0aW1lci5cbiAgICAgKiBAaW50ZXJuYWxcbiAgICAgKi9cbiAgICBwcml2YXRlIHRpY2tlclRpbWVyOiBOb2RlSlMuVGltZW91dCB8IHVuZGVmaW5lZDtcblxuICAgIC8qKlxuICAgICAqIFRoZSBjdXJyZW50IFRQUy5cbiAgICAgKiBAaW50ZXJuYWxcbiAgICAgKi9cbiAgICBwcml2YXRlIHRwcyA9IDIwO1xuXG4gICAgLyoqXG4gICAgICogVGhlIGN1cnJlbnQgdGljay5cbiAgICAgKiBAaW50ZXJuYWxcbiAgICAgKi9cbiAgICBwcml2YXRlIGN1cnJlbnRUaWNrID0gMG47XG5cbiAgICAvKipcbiAgICAgKiBJZiB0aGUgc2VydmVyIGlzIGhlYWRsZXNzLlxuICAgICAqIEBpbnRlcm5hbFxuICAgICAqL1xuICAgIHByaXZhdGUgcmVhZG9ubHkgaGVhZGxlc3M6IGJvb2xlYW47XG5cbiAgICAvLyBUT0RPOiBNb3ZlIHRoaXMgc29tZXdoZXJlIGVsc2UuXG4gICAgcHJpdmF0ZSBzdGF0aWMgcmVhZG9ubHkgTUlORUNSQUZUX1RJQ0tfVElNRV9NUyA9IDEwMDAgLyAyMDtcblxuICAgIC8qKlxuICAgICAqIENyZWF0ZXMgYSBuZXcgc2VydmVyIGluc3RhbmNlLlxuICAgICAqIEBwYXJhbSB7b2JqZWN0fSBvcHRpb25zIC0gVGhlIG9wdGlvbnMuXG4gICAgICogQHBhcmFtIHtMb2dnZXJCdWlsZGVyfSBvcHRpb25zLmxvZ2dlciAtIFRoZSBsb2dnZXIuXG4gICAgICogQHBhcmFtIHtDb25maWd9IG9wdGlvbnMuY29uZmlnIC0gVGhlIGNvbmZpZy5cbiAgICAgKiBAcmV0dXJucyB7U2VydmVyfSBUaGUgc2VydmVyIGluc3RhbmNlLlxuICAgICAqL1xuICAgIHB1YmxpYyBjb25zdHJ1Y3Rvcih7IGxvZ2dlciwgY29uZmlnLCBoZWFkbGVzcyA9IGZhbHNlIH06IHsgbG9nZ2VyOiBMb2dnZXI7IGNvbmZpZzogQ29uZmlnOyBoZWFkbGVzcz86IGJvb2xlYW4gfSkge1xuICAgICAgICBzdXBlcigpO1xuXG4gICAgICAgIHRoaXMuaGVhZGxlc3MgPSBoZWFkbGVzcztcblxuICAgICAgICBsb2dnZXIuaW5mbyhcbiAgICAgICAgICAgIGBTdGFydGluZyBKU1ByaXNtYXJpbmUgc2VydmVyIHZlcnNpb24gwqdldiR7dmVyc2lvbn3Cp3IgZm9yIE1pbmVjcmFmdDogQmVkcm9jayBFZGl0aW9uICR7SWRlbnRpZmllcnMuTWluZWNyYWZ0VmVyc2lvbnMuYXQoLTEpfSAocHJvdG9jb2wgdmVyc2lvbiDCp2Uke0lkZW50aWZpZXJzLlByb3RvY29sfcKncilgXG4gICAgICAgICk7XG5cbiAgICAgICAgdGhpcy5sb2dnZXIgPSBsb2dnZXI7XG4gICAgICAgIHRoaXMuY29uZmlnID0gY29uZmlnO1xuICAgICAgICB0aGlzLnBhY2tldFJlZ2lzdHJ5ID0gbmV3IFBhY2tldFJlZ2lzdHJ5KHRoaXMpO1xuICAgICAgICB0aGlzLml0ZW1NYW5hZ2VyID0gbmV3IEl0ZW1NYW5hZ2VyKHRoaXMpO1xuICAgICAgICB0aGlzLmJsb2NrTWFuYWdlciA9IG5ldyBCbG9ja01hbmFnZXIodGhpcyk7XG4gICAgICAgIHRoaXMud29ybGRNYW5hZ2VyID0gbmV3IFdvcmxkTWFuYWdlcih0aGlzKTtcbiAgICAgICAgaWYgKCF0aGlzLmhlYWRsZXNzKSB0aGlzLmNvbnNvbGUgPSBuZXcgQ29uc29sZSh0aGlzKTtcbiAgICAgICAgdGhpcy5jb21tYW5kTWFuYWdlciA9IG5ldyBDb21tYW5kTWFuYWdlcih0aGlzKTtcbiAgICAgICAgdGhpcy5xdWVyeU1hbmFnZXIgPSBuZXcgUXVlcnlNYW5hZ2VyKHRoaXMpO1xuICAgICAgICB0aGlzLmNoYXRNYW5hZ2VyID0gbmV3IENoYXRNYW5hZ2VyKHRoaXMpO1xuICAgICAgICB0aGlzLnBlcm1pc3Npb25NYW5hZ2VyID0gbmV3IFBlcm1pc3Npb25NYW5hZ2VyKHRoaXMpO1xuICAgICAgICB0aGlzLmJhbk1hbmFnZXIgPSBuZXcgQmFuTWFuYWdlcih0aGlzKTtcbiAgICB9XG5cbiAgICAvKipcbiAgICAgKiBFbmFibGVzIHRoZSBzZXJ2ZXIuXG4gICAgICogQHJldHVybnMge1Byb21pc2U8dm9pZD59IEEgcHJvbWlzZSB0aGF0IHJlc29sdmVzIHdoZW4gdGhlIHNlcnZlciBpcyBlbmFibGVkLlxuICAgICAqIEBpbnRlcm5hbFxuICAgICAqL1xuICAgIHByaXZhdGUgYXN5bmMgZW5hYmxlKCk6IFByb21pc2U8dm9pZD4ge1xuICAgICAgICBhd2FpdCBCbG9ja01hcHBpbmdzLmluaXRNYXBwaW5ncyh0aGlzKTtcblxuICAgICAgICBhd2FpdCB0aGlzLmNvbmZpZy5lbmFibGUoKTtcbiAgICAgICAgYXdhaXQgdGhpcy5jb25zb2xlPy5lbmFibGUoKTtcbiAgICAgICAgYXdhaXQgdGhpcy5sb2dnZXIuZW5hYmxlKCk7XG4gICAgICAgIGF3YWl0IHRoaXMucGVybWlzc2lvbk1hbmFnZXIuZW5hYmxlKCk7XG4gICAgICAgIGF3YWl0IHRoaXMucGFja2V0UmVnaXN0cnkuZW5hYmxlKCk7XG4gICAgICAgIGF3YWl0IHRoaXMuaXRlbU1hbmFnZXIuZW5hYmxlKCk7XG4gICAgICAgIGF3YWl0IHRoaXMuYmxvY2tNYW5hZ2VyLmVuYWJsZSgpO1xuICAgICAgICBhd2FpdCB0aGlzLmJhbk1hbmFnZXIuZW5hYmxlKCk7XG4gICAgICAgIGF3YWl0IHRoaXMuY29tbWFuZE1hbmFnZXIuZW5hYmxlKCk7XG4gICAgICAgIGF3YWl0IHRoaXMud29ybGRNYW5hZ2VyLmVuYWJsZSgpO1xuXG4gICAgICAgIHRoaXMubG9nZ2VyLnNldENvbnNvbGUodGhpcy5jb25zb2xlKTtcbiAgICB9XG5cbiAgICAvKipcbiAgICAgKiBEaXNhYmxlcyB0aGUgc2VydmVyLlxuICAgICAqIEByZXR1cm5zIHtQcm9taXNlPHZvaWQ+fSBBIHByb21pc2UgdGhhdCByZXNvbHZlcyB3aGVuIHRoZSBzZXJ2ZXIgaXMgZGlzYWJsZWQuXG4gICAgICogQGludGVybmFsXG4gICAgICovXG4gICAgcHJpdmF0ZSBhc3luYyBkaXNhYmxlKCk6IFByb21pc2U8dm9pZD4ge1xuICAgICAgICBhd2FpdCB0aGlzLndvcmxkTWFuYWdlci5kaXNhYmxlKCk7XG4gICAgICAgIGF3YWl0IHRoaXMuY29tbWFuZE1hbmFnZXIuZGlzYWJsZSgpO1xuICAgICAgICBhd2FpdCB0aGlzLmJhbk1hbmFnZXIuZGlzYWJsZSgpO1xuICAgICAgICBhd2FpdCB0aGlzLmJsb2NrTWFuYWdlci5kaXNhYmxlKCk7XG4gICAgICAgIGF3YWl0IHRoaXMuaXRlbU1hbmFnZXIuZGlzYWJsZSgpO1xuICAgICAgICBhd2FpdCB0aGlzLnBlcm1pc3Npb25NYW5hZ2VyLmRpc2FibGUoKTtcbiAgICAgICAgYXdhaXQgdGhpcy5wYWNrZXRSZWdpc3RyeS5kaXNhYmxlKCk7XG4gICAgICAgIGF3YWl0IHRoaXMuY29uZmlnLmRpc2FibGUoKTtcbiAgICAgICAgYXdhaXQgdGhpcy5sb2dnZXIuZGlzYWJsZSgpO1xuXG4gICAgICAgIEJsb2NrTWFwcGluZ3MucmVzZXQoKTtcbiAgICB9XG5cbiAgICBwdWJsaWMgZ2V0TWV0YWRhdGEoKSB7XG4gICAgICAgIGlmICghdGhpcy5yYWtuZXQpIHRocm93IG5ldyBFcnJvcignU2VydmVyIGlzIG5vdCBzdGFydGVkJyk7XG4gICAgICAgIHJldHVybiB0aGlzLnJha25ldC5zZXJ2ZXJOYW1lO1xuICAgIH1cblxuICAgIC8qKlxuICAgICAqIFJlbG9hZHMgdGhlIHNlcnZlci5cbiAgICAgKiBAcmV0dXJucyB7UHJvbWlzZTx2b2lkPn0gQSBwcm9taXNlIHRoYXQgcmVzb2x2ZXMgd2hlbiB0aGUgc2VydmVyIGlzIHJlbG9hZGVkLlxuICAgICAqIEByZW1hcmtzIFRoaXMgbWV0aG9kIGlzIGVxdWl2YWxlbnQgdG8gY2FsbGluZyB7QGxpbmsgU2VydmVyI2Rpc2FibGV9IGFuZCB7QGxpbmsgU2VydmVyI2VuYWJsZX0uXG4gICAgICogQHJlbWFya3MgVGhpcyBtZXRob2QgYW5kIGZ1bmN0aW9uYWxpdHkgaXMgdW5zdXBwb3J0ZWQgYW5kIHNob3VsZCBpZGVhbGx5IGJlIGNvbXBsZXRlbHkgYXZvaWRlZC5cbiAgICAgKi9cbiAgICBwdWJsaWMgYXN5bmMgcmVsb2FkKCk6IFByb21pc2U8dm9pZD4ge1xuICAgICAgICBhd2FpdCB0aGlzLmRpc2FibGUoKTtcbiAgICAgICAgYXdhaXQgdGhpcy5lbmFibGUoKTtcbiAgICB9XG5cbiAgICAvKipcbiAgICAgKiBTdGFydHMgdGhlIHNlcnZlci5cbiAgICAgKiBAcGFyYW0ge3N0cmluZ30gW3NlcnZlcklwPScwLjAuMC4wJ10gLSBUaGUgc2VydmVyIElQLlxuICAgICAqIEBwYXJhbSB7bnVtYmVyfSBbcG9ydD0xOTEzMl0gLSBUaGUgc2VydmVyIHBvcnQuXG4gICAgICogQHJldHVybnMge1Byb21pc2U8dm9pZD59IEEgcHJvbWlzZSB0aGF0IHJlc29sdmVzIHdoZW4gdGhlIHNlcnZlciBpcyBzdGFydGVkLlxuICAgICAqL1xuICAgIHB1YmxpYyBhc3luYyBib290c3RyYXAoc2VydmVySXAgPSAnMC4wLjAuMCcsIHBvcnQgPSAxOTEzMik6IFByb21pc2U8dm9pZD4ge1xuICAgICAgICBhd2FpdCB0aGlzLmVuYWJsZSgpO1xuXG4gICAgICAgIHRoaXMucmFrbmV0ID0gbmV3IFJha05ldExpc3RlbmVyKFxuICAgICAgICAgICAgdGhpcy5nZXRDb25maWcoKS5nZXRNYXhQbGF5ZXJzKCksXG4gICAgICAgICAgICB0aGlzLmdldENvbmZpZygpLmdldE9ubGluZU1vZGUoKSxcbiAgICAgICAgICAgIG5ldyBTZXJ2ZXJOYW1lKHRoaXMpLFxuICAgICAgICAgICAgdGhpcy5nZXRMb2dnZXIoKVxuICAgICAgICApO1xuICAgICAgICB0aGlzLnJha25ldC5zdGFydChzZXJ2ZXJJcCwgcG9ydCk7XG5cbiAgICAgICAgdGhpcy5yYWtuZXQub24oJ29wZW5Db25uZWN0aW9uJywgYXN5bmMgKHNlc3Npb246IFJha05ldFNlc3Npb24pID0+IHtcbiAgICAgICAgICAgIGNvbnN0IGV2ZW50ID0gbmV3IFJha25ldENvbm5lY3RFdmVudChzZXNzaW9uKTtcbiAgICAgICAgICAgIGF3YWl0IHRoaXMuZW1pdCgncmFrbmV0Q29ubmVjdCcsIGV2ZW50KTtcblxuICAgICAgICAgICAgaWYgKGV2ZW50LmlzQ2FuY2VsbGVkKCkpIHtcbiAgICAgICAgICAgICAgICBzZXNzaW9uLmRpc2Nvbm5lY3QoKTtcbiAgICAgICAgICAgICAgICByZXR1cm47XG4gICAgICAgICAgICB9XG5cbiAgICAgICAgICAgIGNvbnN0IHRva2VuID0gc2Vzc2lvbi5nZXRBZGRyZXNzKCkudG9Ub2tlbigpO1xuICAgICAgICAgICAgaWYgKHRoaXMuc2Vzc2lvbk1hbmFnZXIuaGFzKHRva2VuKSkge1xuICAgICAgICAgICAgICAgIHRoaXMubG9nZ2VyLmVycm9yKGBBbm90aGVyIGNsaWVudCB3aXRoIHRva2VuICgke3Rva2VufSkgaXMgYWxyZWFkeSBjb25uZWN0ZWQhYCk7XG4gICAgICAgICAgICAgICAgc2Vzc2lvbi5kaXNjb25uZWN0KCdBbHJlYWR5IGNvbm5lY3RlZCBmcm9tIGFub3RoZXIgbG9jYXRpb24nKTtcbiAgICAgICAgICAgICAgICByZXR1cm47XG4gICAgICAgICAgICB9XG5cbiAgICAgICAgICAgIGNvbnN0IHRpbWVyID0gbmV3IFRpbWVyKCk7XG4gICAgICAgICAgICB0aGlzLmxvZ2dlci5kZWJ1ZyhgJHt0b2tlbn0gaXMgYXR0ZW1wdGluZyB0byBjb25uZWN0YCk7XG4gICAgICAgICAgICB0aGlzLnNlc3Npb25NYW5hZ2VyLmFkZCh0b2tlbiwgbmV3IENsaWVudENvbm5lY3Rpb24oc2Vzc2lvbiwgdGhpcy5sb2dnZXIpKTtcbiAgICAgICAgICAgIHRoaXMubG9nZ2VyLnZlcmJvc2UoYE5ldyBjb25uZWN0aW9uIGhhbmRsaW5nIHRvb2sgwqdlJHt0aW1lci5zdG9wKCl9IG1zwqdyYCk7XG4gICAgICAgIH0pO1xuXG4gICAgICAgIHRoaXMucmFrbmV0Lm9uKCdjbG9zZUNvbm5lY3Rpb24nLCBhc3luYyAoaW5ldEFkZHI6IEluZXRBZGRyZXNzLCByZWFzb246IHN0cmluZykgPT4ge1xuICAgICAgICAgICAgY29uc3QgZXZlbnQgPSBuZXcgUmFrbmV0RGlzY29ubmVjdEV2ZW50KGluZXRBZGRyLCByZWFzb24pO1xuICAgICAgICAgICAgYXdhaXQgdGhpcy5lbWl0KCdyYWtuZXREaXNjb25uZWN0JywgZXZlbnQpO1xuXG4gICAgICAgICAgICBjb25zdCB0aW1lID0gRGF0ZS5ub3coKTtcbiAgICAgICAgICAgIGNvbnN0IHRva2VuID0gaW5ldEFkZHIudG9Ub2tlbigpO1xuXG4gICAgICAgICAgICBjb25zdCBzZXNzaW9uID0gdGhpcy5zZXNzaW9uTWFuYWdlci5nZXQodG9rZW4pO1xuICAgICAgICAgICAgaWYgKCFzZXNzaW9uKSB7XG4gICAgICAgICAgICAgICAgdGhpcy5sb2dnZXIuZGVidWcoYENhbm5vdCByZW1vdmUgY29ubmVjdGlvbiBmcm9tIG5vbi1leGlzdGluZyBwbGF5ZXIgKCR7dG9rZW59KWApO1xuICAgICAgICAgICAgICAgIHJldHVybjtcbiAgICAgICAgICAgIH1cblxuICAgICAgICAgICAgYXdhaXQgc2Vzc2lvbi5jbG9zZVBsYXllclNlc3Npb24oKTtcblxuICAgICAgICAgICAgdGhpcy5zZXNzaW9uTWFuYWdlci5yZW1vdmUodG9rZW4pO1xuICAgICAgICAgICAgdGhpcy5sb2dnZXIuZGVidWcoYCR7dG9rZW59IGRpc2Nvbm5lY3RlZCBkdWUgdG8gJHtyZWFzb259YCk7XG4gICAgICAgICAgICB0aGlzLmxvZ2dlci5kZWJ1ZyhgUGxheWVyIGRlc3RydWN0aW9uIHRvb2sgYWJvdXQgJHtEYXRlLm5vdygpIC0gdGltZX0gbXNgKTtcbiAgICAgICAgfSk7XG5cbiAgICAgICAgdGhpcy5yYWtuZXQub24oJ2VuY2Fwc3VsYXRlZCcsIGFzeW5jIChwYWNrZXQ6IGFueSwgaW5ldEFkZHI6IEluZXRBZGRyZXNzKSA9PiB7XG4gICAgICAgICAgICBjb25zdCBldmVudCA9IG5ldyBSYWtuZXRFbmNhcHN1bGF0ZWRQYWNrZXRFdmVudChpbmV0QWRkciwgcGFja2V0KTtcbiAgICAgICAgICAgIGF3YWl0IHRoaXMuZW1pdCgncmFrbmV0RW5jYXBzdWxhdGVkUGFja2V0JywgZXZlbnQpO1xuXG4gICAgICAgICAgICBsZXQgY29ubmVjdGlvbjogQ2xpZW50Q29ubmVjdGlvbiB8IG51bGw7XG4gICAgICAgICAgICBpZiAoKGNvbm5lY3Rpb24gPSB0aGlzLnNlc3Npb25NYW5hZ2VyLmdldChpbmV0QWRkci50b1Rva2VuKCkpID8/IG51bGwpID09PSBudWxsKSB7XG4gICAgICAgICAgICAgICAgdGhpcy5sb2dnZXIuZXJyb3IoYEdvdCBhIHBhY2tldCBmcm9tIGEgY2xvc2VkIGNvbm5lY3Rpb24gKCR7aW5ldEFkZHIudG9Ub2tlbigpfSlgKTtcbiAgICAgICAgICAgICAgICByZXR1cm47XG4gICAgICAgICAgICB9XG5cbiAgICAgICAgICAgIHRyeSB7XG4gICAgICAgICAgICAgICAgLy8gUmVhZCBiYXRjaCBjb250ZW50IGFuZCBoYW5kbGUgdGhlbVxuICAgICAgICAgICAgICAgIGNvbnN0IGJhdGNoZWQgPSBuZXcgQmF0Y2hQYWNrZXQocGFja2V0LmNvbnRlbnQpO1xuICAgICAgICAgICAgICAgIGJhdGNoZWQuY29tcHJlc3NlZCA9IGNvbm5lY3Rpb24uaGFzQ29tcHJlc3Npb247XG5cbiAgICAgICAgICAgICAgICAvLyBSZWFkIGFsbCBwYWNrZXRzIGluc2lkZSBiYXRjaCBhbmQgaGFuZGxlIHRoZW1cbiAgICAgICAgICAgICAgICBmb3IgKGNvbnN0IGJ1ZiBvZiBhd2FpdCBiYXRjaGVkLmFzeW5jRGVjb2RlKCkpIHtcbiAgICAgICAgICAgICAgICAgICAgY29uc3QgcGlkID0gYnVmWzBdITtcblxuICAgICAgICAgICAgICAgICAgICBpZiAoIXRoaXMucGFja2V0UmVnaXN0cnkuZ2V0UGFja2V0cygpLmhhcyhwaWQpKSB7XG4gICAgICAgICAgICAgICAgICAgICAgICB0aGlzLmxvZ2dlci53YXJuKGBQYWNrZXQgMHgke3BpZC50b1N0cmluZygxNil9IGlzbid0IGltcGxlbWVudGVkYCk7XG4gICAgICAgICAgICAgICAgICAgICAgICBjb250aW51ZTtcbiAgICAgICAgICAgICAgICAgICAgfVxuXG4gICAgICAgICAgICAgICAgICAgIC8vIEdldCBwYWNrZXQgZnJvbSByZWdpc3RyeVxuICAgICAgICAgICAgICAgICAgICBjb25zdCBwYWNrZXQgPSBuZXcgKHRoaXMucGFja2V0UmVnaXN0cnkuZ2V0UGFja2V0cygpLmdldChwaWQpISkoYnVmKTtcblxuICAgICAgICAgICAgICAgICAgICB0cnkge1xuICAgICAgICAgICAgICAgICAgICAgICAgcGFja2V0LmRlY29kZSgpO1xuICAgICAgICAgICAgICAgICAgICB9IGNhdGNoIChlcnJvcjogdW5rbm93bikge1xuICAgICAgICAgICAgICAgICAgICAgICAgdGhpcy5sb2dnZXIuZXJyb3IoZXJyb3IpO1xuICAgICAgICAgICAgICAgICAgICAgICAgdGhpcy5sb2dnZXIuZXJyb3IoYEVycm9yIHdoaWxlIGRlY29kaW5nIHBhY2tldDogJHtwYWNrZXQuY29uc3RydWN0b3IubmFtZX06ICR7ZXJyb3J9YCk7XG4gICAgICAgICAgICAgICAgICAgICAgICBjb250aW51ZTtcbiAgICAgICAgICAgICAgICAgICAgfVxuXG4gICAgICAgICAgICAgICAgICAgIHRyeSB7XG4gICAgICAgICAgICAgICAgICAgICAgICBjb25zdCBoYW5kbGVyID0gdGhpcy5wYWNrZXRSZWdpc3RyeS5nZXRIYW5kbGVyKHBpZCk7XG4gICAgICAgICAgICAgICAgICAgICAgICB0aGlzLmxvZ2dlci5zaWxseShgUmVjZWl2ZWQgwqdiJHtwYWNrZXQuY29uc3RydWN0b3IubmFtZX3Cp3IgcGFja2V0YCk7XG4gICAgICAgICAgICAgICAgICAgICAgICBhd2FpdCAoaGFuZGxlciBhcyBhbnkpLmhhbmRsZShwYWNrZXQsIHRoaXMsIGNvbm5lY3Rpb24uZ2V0UGxheWVyU2Vzc2lvbigpID8/IGNvbm5lY3Rpb24pO1xuICAgICAgICAgICAgICAgICAgICB9IGNhdGNoIChlcnJvcjogdW5rbm93bikge1xuICAgICAgICAgICAgICAgICAgICAgICAgdGhpcy5sb2dnZXIuZXJyb3IoYEhhbmRsZXIgZXJyb3IgJHtwYWNrZXQuY29uc3RydWN0b3IubmFtZX0taGFuZGxlcjogKCR7ZXJyb3J9KWApO1xuICAgICAgICAgICAgICAgICAgICAgICAgdGhpcy5sb2dnZXIuZXJyb3IoZXJyb3IpO1xuICAgICAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgfSBjYXRjaCAoZXJyb3I6IHVua25vd24pIHtcbiAgICAgICAgICAgICAgICB0aGlzLmxvZ2dlci5lcnJvcihlcnJvcik7XG4gICAgICAgICAgICB9XG4gICAgICAgIH0pO1xuXG4gICAgICAgIHRoaXMucmFrbmV0Lm9uKCdyYXcnLCBhc3luYyAoYnVmZmVyOiBCdWZmZXIsIGluZXRBZGRyOiBJbmV0QWRkcmVzcykgPT4ge1xuICAgICAgICAgICAgaWYgKCF0aGlzLmNvbmZpZy5nZXRFbmFibGVRdWVyeSgpKSByZXR1cm47XG5cbiAgICAgICAgICAgIHRyeSB7XG4gICAgICAgICAgICAgICAgYXdhaXQgdGhpcy5xdWVyeU1hbmFnZXIub25SYXcoYnVmZmVyLCBpbmV0QWRkcik7XG4gICAgICAgICAgICB9IGNhdGNoIChlcnJvcjogdW5rbm93bikge1xuICAgICAgICAgICAgICAgIHRoaXMubG9nZ2VyLnZlcmJvc2UoYFF1ZXJ5TWFuYWdlciBlbmNvdW50ZXJlZCBhbiBlcnJvcmApO1xuICAgICAgICAgICAgICAgIHRoaXMubG9nZ2VyLmVycm9yKGVycm9yKTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgfSk7XG5cbiAgICAgICAgaWYgKHRoaXMuY29uZmlnLmdldEVuYWJsZVRpY2tpbmcoKSkge1xuICAgICAgICAgICAgbGV0IHN0YXJ0VGltZSA9IERhdGUubm93KCk7XG4gICAgICAgICAgICBsZXQgdHBzU3RhcnRUaW1lID0gRGF0ZS5ub3coKTtcbiAgICAgICAgICAgIGxldCBsYXN0VGlja1RpbWUgPSBEYXRlLm5vdygpO1xuICAgICAgICAgICAgbGV0IHRwc1N0YXJ0VGljayA9IHRoaXMuZ2V0VGljaygpO1xuICAgICAgICAgICAgY29uc3QgdGljayA9IGFzeW5jICgpID0+IHtcbiAgICAgICAgICAgICAgICBpZiAodGhpcy5zdG9wcGluZykgcmV0dXJuO1xuXG4gICAgICAgICAgICAgICAgY29uc3QgZXZlbnQgPSBuZXcgVGlja0V2ZW50KHRoaXMuZ2V0VGljaygpKTtcbiAgICAgICAgICAgICAgICB2b2lkIHRoaXMuZW1pdCgndGljaycsIGV2ZW50KTtcblxuICAgICAgICAgICAgICAgIGNvbnN0IHRpY2tzUGVyU2Vjb25kID0gMTAwMCAvIFNlcnZlci5NSU5FQ1JBRlRfVElDS19USU1FX01TO1xuXG4gICAgICAgICAgICAgICAgLy8gVXBkYXRlIGFsbCB3b3JsZHMuXG4gICAgICAgICAgICAgICAgYXdhaXQgUHJvbWlzZS5hbGwodGhpcy53b3JsZE1hbmFnZXIuZ2V0V29ybGRzKCkubWFwKCh3b3JsZCkgPT4gd29ybGQudXBkYXRlKGV2ZW50LmdldFRpY2soKSkpKTtcblxuICAgICAgICAgICAgICAgIGlmICh0aGlzLmNvbmZpZy5nZXRFbmFibGVQcm9jZXNzVGl0bGUoKSAmJiB0aGlzLmdldFRpY2soKSAlIHRpY2tzUGVyU2Vjb25kID09PSAwICYmICF0aGlzLmhlYWRsZXNzKSB7XG4gICAgICAgICAgICAgICAgICAgIC8vIFVwZGF0ZSB0aGUgcHJvY2VzcyB0aXRsZSB3aXRoIFRQUyBhbmQgdGljay5cbiAgICAgICAgICAgICAgICAgICAgcHJvY2Vzcy50aXRsZSA9IGBUUFM6ICR7dGhpcy5nZXRUUFMoKS50b0ZpeGVkKDIpfSB8IFRpY2s6ICR7dGhpcy5nZXRUaWNrKCl9IHwgJHtwcm9jZXNzLnRpdGxlLnNwbGl0KCd8ICcpLmF0KC0xKSF9YDtcbiAgICAgICAgICAgICAgICB9XG5cbiAgICAgICAgICAgICAgICB0aGlzLmN1cnJlbnRUaWNrKys7XG4gICAgICAgICAgICAgICAgY29uc3QgZW5kVGltZSA9IERhdGUubm93KCk7XG4gICAgICAgICAgICAgICAgY29uc3QgZWxhcHNlZFRpbWUgPSBlbmRUaW1lIC0gc3RhcnRUaW1lO1xuICAgICAgICAgICAgICAgIGNvbnN0IGV4cGVjdGVkRWxhcHNlZFRpbWUgPSB0aGlzLmdldFRpY2soKSAqIFNlcnZlci5NSU5FQ1JBRlRfVElDS19USU1FX01TO1xuICAgICAgICAgICAgICAgIGNvbnN0IGV4ZWN1dGlvblRpbWUgPSBlbmRUaW1lIC0gbGFzdFRpY2tUaW1lO1xuXG4gICAgICAgICAgICAgICAgLy8gQWRqdXN0IHNsZWVwVGltZSBiYXNlZCBvbiBleGVjdXRpb24gc3BlZWQuXG4gICAgICAgICAgICAgICAgbGV0IHNsZWVwVGltZSA9IFNlcnZlci5NSU5FQ1JBRlRfVElDS19USU1FX01TIC0gZXhlY3V0aW9uVGltZTtcbiAgICAgICAgICAgICAgICBpZiAoZWxhcHNlZFRpbWUgPCBleHBlY3RlZEVsYXBzZWRUaW1lKSB7XG4gICAgICAgICAgICAgICAgICAgIC8vIElmIHdlJ3JlIHJ1bm5pbmcgZmFzdGVyIHRoYW4gZXhwZWN0ZWQsIGluY3JlYXNlIHNsZWVwVGltZS5cbiAgICAgICAgICAgICAgICAgICAgc2xlZXBUaW1lICs9IGV4cGVjdGVkRWxhcHNlZFRpbWUgLSBlbGFwc2VkVGltZTtcbiAgICAgICAgICAgICAgICB9IGVsc2UgaWYgKGVsYXBzZWRUaW1lID4gZXhwZWN0ZWRFbGFwc2VkVGltZSkge1xuICAgICAgICAgICAgICAgICAgICAvLyBJZiB3ZSdyZSBydW5uaW5nIHNsb3dlciB0aGFuIGV4cGVjdGVkLCBkZWNyZWFzZSBzbGVlcFRpbWUgYnV0IGRvbid0IGxldCBpdCBnbyBiZWxvdyAwLlxuICAgICAgICAgICAgICAgICAgICBzbGVlcFRpbWUgPSBNYXRoLm1heCgwLCBzbGVlcFRpbWUgLSAoZWxhcHNlZFRpbWUgLSBleHBlY3RlZEVsYXBzZWRUaW1lKSk7XG4gICAgICAgICAgICAgICAgfVxuXG4gICAgICAgICAgICAgICAgLy8gQ2FsY3VsYXRlIHRwcyBiYXNlZCBvbiB0aGUgYWN0dWFsIGVsYXBzZWQgdGltZSBzaW5jZSB0aGUgc3RhcnQgb2YgdGhlIHRpY2suXG4gICAgICAgICAgICAgICAgaWYgKHRwc1N0YXJ0VGltZSAhPT0gZW5kVGltZSkge1xuICAgICAgICAgICAgICAgICAgICB0aGlzLnRwcyA9ICgodGhpcy5nZXRUaWNrKCkgLSB0cHNTdGFydFRpY2spICogMTAwMCkgLyAoZW5kVGltZSAtIHRwc1N0YXJ0VGltZSk7XG4gICAgICAgICAgICAgICAgfVxuXG4gICAgICAgICAgICAgICAgaWYgKGVuZFRpbWUgLSB0cHNTdGFydFRpbWUgPj0gMTAwMCkge1xuICAgICAgICAgICAgICAgICAgICB0cHNTdGFydFRpY2sgPSB0aGlzLmdldFRpY2soKTtcbiAgICAgICAgICAgICAgICAgICAgdHBzU3RhcnRUaW1lID0gZW5kVGltZTtcbiAgICAgICAgICAgICAgICB9XG5cbiAgICAgICAgICAgICAgICB0aGlzLnRwcyA9IE1hdGgubWluKHRoaXMudHBzLCAyMCk7IC8vIEVuc3VyZSB0cHMgZG9lcyBub3QgZXhjZWVkIDIwXG5cbiAgICAgICAgICAgICAgICBsYXN0VGlja1RpbWUgPSBlbmRUaW1lO1xuICAgICAgICAgICAgICAgIHRoaXMudGlja2VyVGltZXIgPSBzZXRUaW1lb3V0KHRpY2ssIHNsZWVwVGltZSk7XG4gICAgICAgICAgICAgICAgdGhpcy50aWNrZXJUaW1lci51bnJlZigpO1xuICAgICAgICAgICAgfTtcblxuICAgICAgICAgICAgLy8gU3RhcnQgdGlja2luZ1xuICAgICAgICAgICAgdm9pZCB0aWNrKCk7XG4gICAgICAgIH1cblxuICAgICAgICB0aGlzLmxvZ2dlci5pbmZvKGBKU1ByaXNtYXJpbmUgaXMgbm93IGxpc3RlbmluZyBvbiBwb3J0IMKnYiR7cG9ydH1gKTtcbiAgICB9XG5cbiAgICAvKipcbiAgICAgKiBLaWxscyB0aGUgc2VydmVyIGFzeW5jaHJvbm91c2x5LlxuICAgICAqIEBwYXJhbSB7b2JqZWN0fSBbb3B0aW9uc10gLSBUaGUgb3B0aW9ucy5cbiAgICAgKiBAcGFyYW0ge2Jvb2xlYW59IFtvcHRpb25zLmNyYXNoXSAtIElmIHRoZSBzZXJ2ZXIgc2hvdWxkIGNyYXNoLlxuICAgICAqIEBwYXJhbSB7Ym9vbGVhbn0gW29wdGlvbnMuc3RheUFsaXZlXSAtIElmIHdlIHNob3VsZCBsZXQgdGhlIHByb2Nlc3Mgc3RheSBhbGl2ZS5cbiAgICAgKiBAcmV0dXJucyB7UHJvbWlzZTx2b2lkPn0gQSBwcm9taXNlIHRoYXQgcmVzb2x2ZXMgd2hlbiB0aGUgc2VydmVyIGlzIGtpbGxlZC5cbiAgICAgKi9cbiAgICBwdWJsaWMgYXN5bmMgc2h1dGRvd24ob3B0aW9ucz86IHsgY3Jhc2g/OiBib29sZWFuOyBzdGF5QWxpdmU/OiBib29sZWFuIH0pOiBQcm9taXNlPHZvaWQ+IHtcbiAgICAgICAgaWYgKHRoaXMuc3RvcHBpbmcpIHJldHVybjtcbiAgICAgICAgdGhpcy5zdG9wcGluZyA9IHRydWU7XG5cbiAgICAgICAgdGhpcy5sb2dnZXIuaW5mbygnU3RvcHBpbmcgc2VydmVyJywgJ1NlcnZlci9raWxsJyk7XG4gICAgICAgIGF3YWl0IHRoaXMuY29uc29sZT8uZGlzYWJsZSgpO1xuXG4gICAgICAgIGNsZWFySW50ZXJ2YWwodGhpcy50aWNrZXJUaW1lcik7XG5cbiAgICAgICAgdHJ5IHtcbiAgICAgICAgICAgIC8vIEtpY2sgYWxsIG9ubGluZSBwbGF5ZXJzLlxuICAgICAgICAgICAgYXdhaXQgdGhpcy5zZXNzaW9uTWFuYWdlci5raWNrQWxsUGxheWVycygnU2VydmVyIGNsb3NlZC4nKTtcblxuICAgICAgICAgICAgLy8gRGlzYWJsZSBhbGwgbWFuYWdlcnMuXG4gICAgICAgICAgICBhd2FpdCB0aGlzLmRpc2FibGUoKTtcblxuICAgICAgICAgICAgLy8gYHRoaXMucmFrbmV0YCBtaWdodCBiZSB1bmRlZmluZWQgaWYgd2Uga2lsbCB0aGUgc2VydmVyIHJlYWxseSBlYXJseS5cbiAgICAgICAgICAgIHRoaXMucmFrbmV0Py5raWxsKCk7XG5cbiAgICAgICAgICAgIC8vIEZpbmFsbHksIHJlbW92ZSBhbGwgbGlzdGVuZXJzLlxuICAgICAgICAgICAgdGhpcy5yZW1vdmVBbGxMaXN0ZW5lcnMoKTtcblxuICAgICAgICAgICAgLy8gTG9nZ2VyIGlzIG5vIGxvbmdlciBhdmFpbGFibGUuXG4gICAgICAgICAgICBjb25zb2xlLmRlYnVnKCdTZXJ2ZXIgc3RvcHBlZCwgR29vZGJ5ZSFcXG4nKTtcblxuICAgICAgICAgICAgaWYgKCFvcHRpb25zPy5zdGF5QWxpdmUpIHByb2Nlc3MuZXhpdChvcHRpb25zPy5jcmFzaCA/IDEgOiAwKTtcbiAgICAgICAgfSBjYXRjaCAoZXJyb3I6IHVua25vd24pIHtcbiAgICAgICAgICAgIGNvbnNvbGUuZXJyb3IoZXJyb3IpO1xuICAgICAgICAgICAgaWYgKCFvcHRpb25zPy5zdGF5QWxpdmUpIHByb2Nlc3MuZXhpdCgxKTtcbiAgICAgICAgfVxuICAgIH1cblxuICAgIHB1YmxpYyBhc3luYyBicm9hZGNhc3RQYWNrZXQ8VCBleHRlbmRzIERhdGFQYWNrZXQ+KGRhdGFQYWNrZXQ6IFQpOiBQcm9taXNlPHZvaWQ+IHtcbiAgICAgICAgLy8gTWF5YmUgaSBjYW4gaW1wcm92ZSB0aGlzIGJ5IHVzaW5nIHRoZSBVRFAgYnJvYWRjYXN0LCBhbGwgdW5jb25uZWN0ZWQgY2xpZW50c1xuICAgICAgICAvLyB3aWxsIGlnbm9yZSB0aGUgY29ubmVjdGVkIHBhY2tldCBwcm9iYWJseSwgYnV0IG1heSBjYXVzZSBpc3N1ZXMuXG4gICAgICAgIGZvciAoY29uc3Qgb25saW5lUGxheWVyIG9mIHRoaXMuc2Vzc2lvbk1hbmFnZXIuZ2V0QWxsUGxheWVycygpKSB7XG4gICAgICAgICAgICBhd2FpdCBvbmxpbmVQbGF5ZXIuZ2V0TmV0d29ya1Nlc3Npb24oKS5nZXRDb25uZWN0aW9uKCkuc2VuZERhdGFQYWNrZXQoZGF0YVBhY2tldCk7XG4gICAgICAgIH1cbiAgICB9XG5cbiAgICAvKipcbiAgICAgKiBSZXR1cm5zIHRoZSBzZXJ2ZXIgdmVyc2lvbi5cbiAgICAgKiBAcmV0dXJucyB7c3RyaW5nfSBUaGUgc2VydmVyIHZlcnNpb24uXG4gICAgICogQGV4YW1wbGVcbiAgICAgKiBgYGB0eXBlc2NyaXB0XG4gICAgICogY29uc29sZS5sb2coc2VydmVyLmdldFZlcnNpb24oKSk7XG4gICAgICogYGBgXG4gICAgICovXG4gICAgcHVibGljIGdldFZlcnNpb24oKTogc3RyaW5nIHtcbiAgICAgICAgcmV0dXJuIHZlcnNpb247XG4gICAgfVxuXG4gICAgLyoqXG4gICAgICogUmV0dXJucyB0aGUgaWRlbnRpZmllcnMuXG4gICAgICogQHJldHVybnMge0lkZW50aWZpZXJzfSBUaGUgaWRlbnRpZmllcnMuXG4gICAgICovXG4gICAgcHVibGljIGdldElkZW50aWZpZXJzKCk6IHR5cGVvZiBJZGVudGlmaWVycyB7XG4gICAgICAgIHJldHVybiBJZGVudGlmaWVycztcbiAgICB9XG5cbiAgICAvKipcbiAgICAgKiBSZXR1cm5zIHRoZSBxdWVyeSBtYW5hZ2VyLlxuICAgICAqIEByZXR1cm5zIHtRdWVyeU1hbmFnZXJ9IFRoZSBxdWVyeSBtYW5hZ2VyLlxuICAgICAqL1xuICAgIHB1YmxpYyBnZXRRdWVyeU1hbmFnZXIoKTogUXVlcnlNYW5hZ2VyIHtcbiAgICAgICAgcmV0dXJuIHRoaXMucXVlcnlNYW5hZ2VyO1xuICAgIH1cblxuICAgIC8qKlxuICAgICAqIFJldHVybnMgdGhlIGNvbW1hbmQgbWFuYWdlci5cbiAgICAgKiBAcmV0dXJucyB7Q29tbWFuZE1hbmFnZXJ9IFRoZSBjb21tYW5kIG1hbmFnZXIuXG4gICAgICovXG4gICAgcHVibGljIGdldENvbW1hbmRNYW5hZ2VyKCk6IENvbW1hbmRNYW5hZ2VyIHtcbiAgICAgICAgcmV0dXJuIHRoaXMuY29tbWFuZE1hbmFnZXI7XG4gICAgfVxuXG4gICAgLyoqXG4gICAgICogUmV0dXJucyB0aGUgcGxheWVyIG1hbmFnZXIuXG4gICAgICogQHJldHVybnMge1Nlc3Npb25NYW5hZ2VyfSBUaGUgcGxheWVyIG1hbmFnZXIuXG4gICAgICovXG4gICAgcHVibGljIGdldFNlc3Npb25NYW5hZ2VyKCk6IFNlc3Npb25NYW5hZ2VyIHtcbiAgICAgICAgcmV0dXJuIHRoaXMuc2Vzc2lvbk1hbmFnZXI7XG4gICAgfVxuXG4gICAgLyoqXG4gICAgICogUmV0dXJucyB0aGUgd29ybGQgbWFuYWdlci5cbiAgICAgKiBAcmV0dXJucyB7V29ybGRNYW5hZ2VyfSBUaGUgd29ybGQgbWFuYWdlci5cbiAgICAgKi9cbiAgICBwdWJsaWMgZ2V0V29ybGRNYW5hZ2VyKCk6IFdvcmxkTWFuYWdlciB7XG4gICAgICAgIHJldHVybiB0aGlzLndvcmxkTWFuYWdlcjtcbiAgICB9XG5cbiAgICAvKipcbiAgICAgKiBSZXR1cm5zIHRoZSBpdGVtIG1hbmFnZXIuXG4gICAgICogQHJldHVybnMge0l0ZW1NYW5hZ2VyfSBUaGUgaXRlbSBtYW5hZ2VyLlxuICAgICAqL1xuICAgIHB1YmxpYyBnZXRJdGVtTWFuYWdlcigpOiBJdGVtTWFuYWdlciB7XG4gICAgICAgIHJldHVybiB0aGlzLml0ZW1NYW5hZ2VyO1xuICAgIH1cblxuICAgIC8qKlxuICAgICAqIFJldHVybnMgdGhlIGJsb2NrIG1hbmFnZXIuXG4gICAgICogQHJldHVybnMge0Jsb2NrTWFuYWdlcn0gVGhlIGJsb2NrIG1hbmFnZXIuXG4gICAgICovXG4gICAgcHVibGljIGdldEJsb2NrTWFuYWdlcigpOiBCbG9ja01hbmFnZXIge1xuICAgICAgICByZXR1cm4gdGhpcy5ibG9ja01hbmFnZXI7XG4gICAgfVxuXG4gICAgLyoqXG4gICAgICogUmV0dXJucyB0aGUgbG9nZ2VyLlxuICAgICAqIEByZXR1cm5zIHtMb2dnZXJCdWlsZGVyfSBUaGUgbG9nZ2VyLlxuICAgICAqIEBleGFtcGxlXG4gICAgICogYGBgdHlwZXNjcmlwdFxuICAgICAqIC8vIE5vcm1hbCBsb2c6XG4gICAgICogc2VydmVyLmdldExvZ2dlcigpLmluZm8oJ0hlbGxvLCB3b3JsZCEnKTtcbiAgICAgKiAvLyBEZWJ1ZyBsb2c6XG4gICAgICogc2VydmVyLmdldExvZ2dlcigpLmRlYnVnKCdIZWxsbywgd29ybGQhJyk7XG4gICAgICogLy8gRXJyb3IgbG9nOlxuICAgICAqIHNlcnZlci5nZXRMb2dnZXIoKS5lcnJvcihuZXcgRXJyb3IoJ0hlbGxvIFdvcmxkJykpO1xuICAgICAqIGBgYFxuICAgICAqL1xuICAgIHB1YmxpYyBnZXRMb2dnZXIoKTogTG9nZ2VyIHtcbiAgICAgICAgcmV0dXJuIHRoaXMubG9nZ2VyO1xuICAgIH1cblxuICAgIC8qKlxuICAgICAqIFJldHVybnMgdGhlIHBhY2tldCByZWdpc3RyeS5cbiAgICAgKiBAcmV0dXJucyB7UGFja2V0UmVnaXN0cnl9IFRoZSBwYWNrZXQgcmVnaXN0cnkuXG4gICAgICovXG4gICAgcHVibGljIGdldFBhY2tldFJlZ2lzdHJ5KCk6IFBhY2tldFJlZ2lzdHJ5IHtcbiAgICAgICAgcmV0dXJuIHRoaXMucGFja2V0UmVnaXN0cnk7XG4gICAgfVxuXG4gICAgLyoqXG4gICAgICogUmV0dXJucyB0aGUgcmFrbmV0IGluc3RhbmNlLlxuICAgICAqIEByZXR1cm5zIHtSYWtOZXRMaXN0ZW5lciB8IHVuZGVmaW5lZH0gVGhlIHJha25ldCBpbnN0YW5jZS5cbiAgICAgKi9cbiAgICBwdWJsaWMgZ2V0UmFrbmV0KCk6IFJha05ldExpc3RlbmVyIHwgdW5kZWZpbmVkIHtcbiAgICAgICAgcmV0dXJuIHRoaXMucmFrbmV0O1xuICAgIH1cblxuICAgIC8qKlxuICAgICAqIFJldHVybnMgdGhlIGNoYXQgbWFuYWdlci5cbiAgICAgKiBAcmV0dXJucyB7Q2hhdE1hbmFnZXJ9IFRoZSBjaGF0IG1hbmFnZXIuXG4gICAgICovXG4gICAgcHVibGljIGdldENoYXRNYW5hZ2VyKCk6IENoYXRNYW5hZ2VyIHtcbiAgICAgICAgcmV0dXJuIHRoaXMuY2hhdE1hbmFnZXI7XG4gICAgfVxuXG4gICAgLyoqXG4gICAgICogUmV0dXJucyB0aGUgY29uZmlnLlxuICAgICAqIEByZXR1cm5zIHtDb25maWd9IFRoZSBjb25maWcuXG4gICAgICogQGV4YW1wbGVcbiAgICAgKiBgYGB0eXBlc2NyaXB0XG4gICAgICogY29uc29sZS5sb2coc2VydmVyLmdldENvbmZpZygpLmdldE1heFBsYXllcnMoKSk7IC8vIDIwXG4gICAgICogYGBgXG4gICAgICovXG4gICAgcHVibGljIGdldENvbmZpZygpOiBDb25maWcge1xuICAgICAgICByZXR1cm4gdGhpcy5jb25maWc7XG4gICAgfVxuXG4gICAgLyoqXG4gICAgICogUmV0dXJucyB0aGUgY29uc29sZSBpbnN0YW5jZS5cbiAgICAgKiBAcmV0dXJucyB7Q29uc29sZSB8IHVuZGVmaW5lZH0gVGhlIGNvbnNvbGUgaW5zdGFuY2UuXG4gICAgICovXG4gICAgcHVibGljIGdldENvbnNvbGUoKSB7XG4gICAgICAgIHJldHVybiB0aGlzLmNvbnNvbGU7XG4gICAgfVxuXG4gICAgLyoqXG4gICAgICogUmV0dXJucyB0aGUgcGVybWlzc2lvbiBtYW5hZ2VyLlxuICAgICAqIEByZXR1cm5zIHtQZXJtaXNzaW9uTWFuYWdlcn0gVGhlIHBlcm1pc3Npb24gbWFuYWdlci5cbiAgICAgKi9cbiAgICBwdWJsaWMgZ2V0UGVybWlzc2lvbk1hbmFnZXIoKTogUGVybWlzc2lvbk1hbmFnZXIge1xuICAgICAgICByZXR1cm4gdGhpcy5wZXJtaXNzaW9uTWFuYWdlcjtcbiAgICB9XG5cbiAgICAvKipcbiAgICAgKiBSZXR1cm5zIHRoZSBiYW4gbWFuYWdlci5cbiAgICAgKiBAcmV0dXJucyB7QmFuTWFuYWdlcn0gVGhlIGJhbiBtYW5hZ2VyLlxuICAgICAqL1xuICAgIHB1YmxpYyBnZXRCYW5NYW5hZ2VyKCk6IEJhbk1hbmFnZXIge1xuICAgICAgICByZXR1cm4gdGhpcy5iYW5NYW5hZ2VyO1xuICAgIH1cblxuICAgIC8qKlxuICAgICAqIFJldHVybnMgdGhpcyBQcmlzbWFyaW5lIGluc3RhbmNlLlxuICAgICAqIEByZXR1cm5zIHtTZXJ2ZXJ9IFRoZSBQcmlzbWFyaW5lIGluc3RhbmNlLlxuICAgICAqL1xuICAgIHB1YmxpYyBnZXRTZXJ2ZXIoKTogU2VydmVyIHtcbiAgICAgICAgcmV0dXJuIHRoaXM7XG4gICAgfVxuXG4gICAgLyoqXG4gICAgICogUmV0dXJucyB0aGUgY3VycmVudCBUaWNrLlxuICAgICAqIEByZXR1cm5zIHtudW1iZXJ9IFRoZSBjdXJyZW50IFRpY2suXG4gICAgICovXG4gICAgcHVibGljIGdldFRpY2soKTogbnVtYmVyIHtcbiAgICAgICAgcmV0dXJuIE51bWJlcih0aGlzLmN1cnJlbnRUaWNrKTtcbiAgICB9XG5cbiAgICAvKipcbiAgICAgKiBSZXR1cm5zIHRoZSBjdXJyZW50IFRQUy5cbiAgICAgKiBAcmV0dXJucyB7bnVtYmVyfSBUaGUgY3VycmVudCBUUFMuXG4gICAgICovXG4gICAgcHVibGljIGdldFRQUygpOiBudW1iZXIge1xuICAgICAgICByZXR1cm4gTnVtYmVyLnBhcnNlRmxvYXQodGhpcy50cHMudG9GaXhlZCgyKSk7XG4gICAgfVxufVxuIl0sIm5hbWVzIjpbIkV2ZW50RW1pdHRlciIsIlNlc3Npb25NYW5hZ2VyIiwidmVyc2lvbiIsIklkZW50aWZpZXJzIiwiUGFja2V0UmVnaXN0cnkiLCJJdGVtTWFuYWdlciIsIkJsb2NrTWFuYWdlciIsIldvcmxkTWFuYWdlciIsIkNvbnNvbGUiLCJDb21tYW5kTWFuYWdlciIsIlF1ZXJ5TWFuYWdlciIsIkNoYXRNYW5hZ2VyIiwiUGVybWlzc2lvbk1hbmFnZXIiLCJCYW5NYW5hZ2VyIiwiQmxvY2tNYXBwaW5ncyIsIlJha05ldExpc3RlbmVyIiwiU2VydmVyTmFtZSIsIlJha25ldENvbm5lY3RFdmVudCIsIlRpbWVyIiwiQ2xpZW50Q29ubmVjdGlvbiIsIlJha25ldERpc2Nvbm5lY3RFdmVudCIsIlJha25ldEVuY2Fwc3VsYXRlZFBhY2tldEV2ZW50IiwiQmF0Y2hQYWNrZXQiLCJwYWNrZXQiLCJUaWNrRXZlbnQiXSwibWFwcGluZ3MiOiI7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7O0FBa0NBLE1BQXFCLGVBQWVBLGdDQUFhLENBQUE7QUFBQSxFQUNyQyxNQUFBO0FBQUEsRUFDUyxNQUFBO0FBQUEsRUFDQSxNQUFBO0FBQUEsRUFDQSxPQUFBO0FBQUEsRUFDQSxjQUFBO0FBQUEsRUFDQSxjQUFBLEdBQWlCLElBQUlDLHNCQUFlLEVBQUE7QUFBQSxFQUNwQyxjQUFBO0FBQUEsRUFDQSxZQUFBO0FBQUEsRUFDQSxXQUFBO0FBQUEsRUFDQSxZQUFBO0FBQUEsRUFDQSxZQUFBO0FBQUEsRUFDQSxXQUFBO0FBQUEsRUFDQSxpQkFBQTtBQUFBLEVBQ0EsVUFBQTtBQUFBO0FBQUE7QUFBQTtBQUFBO0FBQUEsRUFNVCxRQUFXLEdBQUEsS0FBQTtBQUFBO0FBQUE7QUFBQTtBQUFBO0FBQUEsRUFNWCxXQUFBO0FBQUE7QUFBQTtBQUFBO0FBQUE7QUFBQSxFQU1BLEdBQU0sR0FBQSxFQUFBO0FBQUE7QUFBQTtBQUFBO0FBQUE7QUFBQSxFQU1OLFdBQWMsR0FBQSxFQUFBO0FBQUE7QUFBQTtBQUFBO0FBQUE7QUFBQSxFQU1MLFFBQUE7QUFBQTtBQUFBLEVBR2pCLE9BQXdCLHlCQUF5QixHQUFPLEdBQUEsRUFBQTtBQUFBO0FBQUE7QUFBQTtBQUFBO0FBQUE7QUFBQTtBQUFBO0FBQUEsRUFTakQsWUFBWSxFQUFFLE1BQUEsRUFBUSxNQUFRLEVBQUEsUUFBQSxHQUFXLE9BQWlFLEVBQUE7QUFDN0csSUFBTSxLQUFBLEVBQUE7QUFFTixJQUFBLElBQUEsQ0FBSyxRQUFXLEdBQUEsUUFBQTtBQUVoQixJQUFPLE1BQUEsQ0FBQSxJQUFBO0FBQUEsTUFDSCxDQUFBLHdDQUFBLEVBQTJDQyxnQkFBTyxDQUFBLGtDQUFBLEVBQXFDQywyQkFBWSxDQUFBLGlCQUFBLENBQWtCLEdBQUcsQ0FBRSxDQUFBLENBQUMsQ0FBd0IscUJBQUEsRUFBQUEsMkJBQUEsQ0FBWSxRQUFRLENBQUEsR0FBQTtBQUFBLEtBQzNLO0FBRUEsSUFBQSxJQUFBLENBQUssTUFBUyxHQUFBLE1BQUE7QUFDZCxJQUFBLElBQUEsQ0FBSyxNQUFTLEdBQUEsTUFBQTtBQUNkLElBQUssSUFBQSxDQUFBLGNBQUEsR0FBaUIsSUFBSUMsOEJBQUEsQ0FBZSxJQUFJLENBQUE7QUFDN0MsSUFBSyxJQUFBLENBQUEsV0FBQSxHQUFjLElBQUlDLHdCQUFBLENBQVksSUFBSSxDQUFBO0FBQ3ZDLElBQUssSUFBQSxDQUFBLFlBQUEsR0FBZSxJQUFJQywwQkFBQSxDQUFhLElBQUksQ0FBQTtBQUN6QyxJQUFLLElBQUEsQ0FBQSxZQUFBLEdBQWUsSUFBSUMsMEJBQUEsQ0FBYSxJQUFJLENBQUE7QUFDekMsSUFBQSxJQUFJLENBQUMsSUFBSyxDQUFBLFFBQUEsT0FBZSxPQUFVLEdBQUEsSUFBSUMsZ0JBQVEsSUFBSSxDQUFBO0FBQ25ELElBQUssSUFBQSxDQUFBLGNBQUEsR0FBaUIsSUFBSUMscUNBQUEsQ0FBZSxJQUFJLENBQUE7QUFDN0MsSUFBSyxJQUFBLENBQUEsWUFBQSxHQUFlLElBQUlDLCtCQUFBLENBQWEsSUFBSSxDQUFBO0FBQ3pDLElBQUssSUFBQSxDQUFBLFdBQUEsR0FBYyxJQUFJQyw0QkFBQSxDQUFZLElBQUksQ0FBQTtBQUN2QyxJQUFLLElBQUEsQ0FBQSxpQkFBQSxHQUFvQixJQUFJQyw4Q0FBQSxDQUFrQixJQUFJLENBQUE7QUFDbkQsSUFBSyxJQUFBLENBQUEsVUFBQSxHQUFhLElBQUlDLHNCQUFBLENBQVcsSUFBSSxDQUFBO0FBQUE7QUFDekM7QUFBQTtBQUFBO0FBQUE7QUFBQTtBQUFBLEVBT0EsTUFBYyxNQUF3QixHQUFBO0FBQ2xDLElBQU0sTUFBQUMsaUNBQUEsQ0FBYyxhQUFhLElBQUksQ0FBQTtBQUVyQyxJQUFNLE1BQUEsSUFBQSxDQUFLLE9BQU8sTUFBTyxFQUFBO0FBQ3pCLElBQU0sTUFBQSxJQUFBLENBQUssU0FBUyxNQUFPLEVBQUE7QUFDM0IsSUFBTSxNQUFBLElBQUEsQ0FBSyxPQUFPLE1BQU8sRUFBQTtBQUN6QixJQUFNLE1BQUEsSUFBQSxDQUFLLGtCQUFrQixNQUFPLEVBQUE7QUFDcEMsSUFBTSxNQUFBLElBQUEsQ0FBSyxlQUFlLE1BQU8sRUFBQTtBQUNqQyxJQUFNLE1BQUEsSUFBQSxDQUFLLFlBQVksTUFBTyxFQUFBO0FBQzlCLElBQU0sTUFBQSxJQUFBLENBQUssYUFBYSxNQUFPLEVBQUE7QUFDL0IsSUFBTSxNQUFBLElBQUEsQ0FBSyxXQUFXLE1BQU8sRUFBQTtBQUM3QixJQUFNLE1BQUEsSUFBQSxDQUFLLGVBQWUsTUFBTyxFQUFBO0FBQ2pDLElBQU0sTUFBQSxJQUFBLENBQUssYUFBYSxNQUFPLEVBQUE7QUFFL0IsSUFBSyxJQUFBLENBQUEsTUFBQSxDQUFPLFVBQVcsQ0FBQSxJQUFBLENBQUssT0FBTyxDQUFBO0FBQUE7QUFDdkM7QUFBQTtBQUFBO0FBQUE7QUFBQTtBQUFBLEVBT0EsTUFBYyxPQUF5QixHQUFBO0FBQ25DLElBQU0sTUFBQSxJQUFBLENBQUssYUFBYSxPQUFRLEVBQUE7QUFDaEMsSUFBTSxNQUFBLElBQUEsQ0FBSyxlQUFlLE9BQVEsRUFBQTtBQUNsQyxJQUFNLE1BQUEsSUFBQSxDQUFLLFdBQVcsT0FBUSxFQUFBO0FBQzlCLElBQU0sTUFBQSxJQUFBLENBQUssYUFBYSxPQUFRLEVBQUE7QUFDaEMsSUFBTSxNQUFBLElBQUEsQ0FBSyxZQUFZLE9BQVEsRUFBQTtBQUMvQixJQUFNLE1BQUEsSUFBQSxDQUFLLGtCQUFrQixPQUFRLEVBQUE7QUFDckMsSUFBTSxNQUFBLElBQUEsQ0FBSyxlQUFlLE9BQVEsRUFBQTtBQUNsQyxJQUFNLE1BQUEsSUFBQSxDQUFLLE9BQU8sT0FBUSxFQUFBO0FBQzFCLElBQU0sTUFBQSxJQUFBLENBQUssT0FBTyxPQUFRLEVBQUE7QUFFMUIsSUFBQUEsaUNBQUEsQ0FBYyxLQUFNLEVBQUE7QUFBQTtBQUN4QixFQUVPLFdBQWMsR0FBQTtBQUNqQixJQUFBLElBQUksQ0FBQyxJQUFLLENBQUEsTUFBQSxFQUFjLE1BQUEsSUFBSSxNQUFNLHVCQUF1QixDQUFBO0FBQ3pELElBQUEsT0FBTyxLQUFLLE1BQU8sQ0FBQSxVQUFBO0FBQUE7QUFDdkI7QUFBQTtBQUFBO0FBQUE7QUFBQTtBQUFBO0FBQUEsRUFRQSxNQUFhLE1BQXdCLEdBQUE7QUFDakMsSUFBQSxNQUFNLEtBQUssT0FBUSxFQUFBO0FBQ25CLElBQUEsTUFBTSxLQUFLLE1BQU8sRUFBQTtBQUFBO0FBQ3RCO0FBQUE7QUFBQTtBQUFBO0FBQUE7QUFBQTtBQUFBLEVBUUEsTUFBYSxTQUFBLENBQVUsUUFBVyxHQUFBLFNBQUEsRUFBVyxPQUFPLEtBQXNCLEVBQUE7QUFDdEUsSUFBQSxNQUFNLEtBQUssTUFBTyxFQUFBO0FBRWxCLElBQUEsSUFBQSxDQUFLLFNBQVMsSUFBSUMscUJBQUE7QUFBQSxNQUNkLElBQUEsQ0FBSyxTQUFVLEVBQUEsQ0FBRSxhQUFjLEVBQUE7QUFBQSxNQUMvQixJQUFBLENBQUssU0FBVSxFQUFBLENBQUUsYUFBYyxFQUFBO0FBQUEsTUFDL0IsSUFBSUMsa0JBQVcsSUFBSSxDQUFBO0FBQUEsTUFDbkIsS0FBSyxTQUFVO0FBQUEsS0FDbkI7QUFDQSxJQUFLLElBQUEsQ0FBQSxNQUFBLENBQU8sS0FBTSxDQUFBLFFBQUEsRUFBVSxJQUFJLENBQUE7QUFFaEMsSUFBQSxJQUFBLENBQUssTUFBTyxDQUFBLEVBQUEsQ0FBRyxnQkFBa0IsRUFBQSxPQUFPLE9BQTJCLEtBQUE7QUFDL0QsTUFBTSxNQUFBLEtBQUEsR0FBUSxJQUFJQyx3Q0FBQSxDQUFtQixPQUFPLENBQUE7QUFDNUMsTUFBTSxNQUFBLElBQUEsQ0FBSyxJQUFLLENBQUEsZUFBQSxFQUFpQixLQUFLLENBQUE7QUFFdEMsTUFBSSxJQUFBLEtBQUEsQ0FBTSxhQUFlLEVBQUE7QUFDckIsUUFBQSxPQUFBLENBQVEsVUFBVyxFQUFBO0FBQ25CLFFBQUE7QUFBQTtBQUdKLE1BQUEsTUFBTSxLQUFRLEdBQUEsT0FBQSxDQUFRLFVBQVcsRUFBQSxDQUFFLE9BQVEsRUFBQTtBQUMzQyxNQUFBLElBQUksSUFBSyxDQUFBLGNBQUEsQ0FBZSxHQUFJLENBQUEsS0FBSyxDQUFHLEVBQUE7QUFDaEMsUUFBQSxJQUFBLENBQUssTUFBTyxDQUFBLEtBQUEsQ0FBTSxDQUE4QiwyQkFBQSxFQUFBLEtBQUssQ0FBeUIsdUJBQUEsQ0FBQSxDQUFBO0FBQzlFLFFBQUEsT0FBQSxDQUFRLFdBQVcseUNBQXlDLENBQUE7QUFDNUQsUUFBQTtBQUFBO0FBR0osTUFBTSxNQUFBLEtBQUEsR0FBUSxJQUFJQyxtQkFBTSxFQUFBO0FBQ3hCLE1BQUEsSUFBQSxDQUFLLE1BQU8sQ0FBQSxLQUFBLENBQU0sQ0FBRyxFQUFBLEtBQUssQ0FBMkIseUJBQUEsQ0FBQSxDQUFBO0FBQ3JELE1BQUssSUFBQSxDQUFBLGNBQUEsQ0FBZSxJQUFJLEtBQU8sRUFBQSxJQUFJQyxpQ0FBaUIsT0FBUyxFQUFBLElBQUEsQ0FBSyxNQUFNLENBQUMsQ0FBQTtBQUN6RSxNQUFBLElBQUEsQ0FBSyxPQUFPLE9BQVEsQ0FBQSxDQUFBLCtCQUFBLEVBQWtDLEtBQU0sQ0FBQSxJQUFBLEVBQU0sQ0FBTyxLQUFBLENBQUEsQ0FBQTtBQUFBLEtBQzVFLENBQUE7QUFFRCxJQUFBLElBQUEsQ0FBSyxNQUFPLENBQUEsRUFBQSxDQUFHLGlCQUFtQixFQUFBLE9BQU8sVUFBdUIsTUFBbUIsS0FBQTtBQUMvRSxNQUFBLE1BQU0sS0FBUSxHQUFBLElBQUlDLDJDQUFzQixDQUFBLFFBQUEsRUFBVSxNQUFNLENBQUE7QUFDeEQsTUFBTSxNQUFBLElBQUEsQ0FBSyxJQUFLLENBQUEsa0JBQUEsRUFBb0IsS0FBSyxDQUFBO0FBRXpDLE1BQU0sTUFBQSxJQUFBLEdBQU8sS0FBSyxHQUFJLEVBQUE7QUFDdEIsTUFBTSxNQUFBLEtBQUEsR0FBUSxTQUFTLE9BQVEsRUFBQTtBQUUvQixNQUFBLE1BQU0sT0FBVSxHQUFBLElBQUEsQ0FBSyxjQUFlLENBQUEsR0FBQSxDQUFJLEtBQUssQ0FBQTtBQUM3QyxNQUFBLElBQUksQ0FBQyxPQUFTLEVBQUE7QUFDVixRQUFBLElBQUEsQ0FBSyxNQUFPLENBQUEsS0FBQSxDQUFNLENBQXNELG1EQUFBLEVBQUEsS0FBSyxDQUFHLENBQUEsQ0FBQSxDQUFBO0FBQ2hGLFFBQUE7QUFBQTtBQUdKLE1BQUEsTUFBTSxRQUFRLGtCQUFtQixFQUFBO0FBRWpDLE1BQUssSUFBQSxDQUFBLGNBQUEsQ0FBZSxPQUFPLEtBQUssQ0FBQTtBQUNoQyxNQUFBLElBQUEsQ0FBSyxPQUFPLEtBQU0sQ0FBQSxDQUFBLEVBQUcsS0FBSyxDQUFBLHFCQUFBLEVBQXdCLE1BQU0sQ0FBRSxDQUFBLENBQUE7QUFDMUQsTUFBQSxJQUFBLENBQUssT0FBTyxLQUFNLENBQUEsQ0FBQSw4QkFBQSxFQUFpQyxLQUFLLEdBQUksRUFBQSxHQUFJLElBQUksQ0FBSyxHQUFBLENBQUEsQ0FBQTtBQUFBLEtBQzVFLENBQUE7QUFFRCxJQUFBLElBQUEsQ0FBSyxNQUFPLENBQUEsRUFBQSxDQUFHLGNBQWdCLEVBQUEsT0FBTyxRQUFhLFFBQTBCLEtBQUE7QUFDekUsTUFBQSxNQUFNLEtBQVEsR0FBQSxJQUFJQyxtREFBOEIsQ0FBQSxRQUFBLEVBQVUsTUFBTSxD