UNPKG

@jsprismarine/prismarine

Version:

Dedicated Minecraft Bedrock Edition server written in TypeScript

463 lines (461 loc) 57 kB
import { RakNetListener, ServerName } from '@jsprismarine/raknet'; import Console from './Console.es.js'; import SessionManager from './SessionManager.es.js'; import BanManager from './ban/BanManager.es.js'; import BlockManager from './block/BlockManager.es.js'; import { BlockMappings } from './block/BlockMappings.es.js'; import { ChatManager } from './chat/ChatManager.es.js'; import { CommandManager } from './command/CommandManager.es.js'; import { EventEmitter } from './events/EventEmitter.es.js'; import '@jsprismarine/math'; 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 ItemManager from './item/ItemManager.es.js'; import ClientConnection from './network/ClientConnection.es.js'; import Identifiers from './network/Identifiers.es.js'; import PacketRegistry from './network/PacketRegistry.es.js'; import BatchPacket from './network/packet/BatchPacket.es.js'; import { PermissionManager } from './permission/PermissionManager.es.js'; import { QueryManager } from './query/QueryManager.es.js'; import Timer from './utils/Timer.es.js'; import WorldManager from './world/WorldManager.es.js'; import { version } from './package.json.es.js'; 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; // 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${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 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 TickEvent(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 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)); } } export { Server as default }; //# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiU2VydmVyLmVzLmpzIiwic291cmNlcyI6WyIuLi9zcmMvU2VydmVyLnRzIl0sInNvdXJjZXNDb250ZW50IjpbImltcG9ydCB7IFJha05ldExpc3RlbmVyLCBTZXJ2ZXJOYW1lIH0gZnJvbSAnQGpzcHJpc21hcmluZS9yYWtuZXQnO1xuaW1wb3J0IENvbnNvbGUgZnJvbSAnLi9Db25zb2xlJztcbmltcG9ydCBTZXNzaW9uTWFuYWdlciBmcm9tICcuL1Nlc3Npb25NYW5hZ2VyJztcbmltcG9ydCBCYW5NYW5hZ2VyIGZyb20gJy4vYmFuL0Jhbk1hbmFnZXInO1xuaW1wb3J0IEJsb2NrTWFuYWdlciBmcm9tICcuL2Jsb2NrL0Jsb2NrTWFuYWdlcic7XG5pbXBvcnQgeyBCbG9ja01hcHBpbmdzIH0gZnJvbSAnLi9ibG9jay9CbG9ja01hcHBpbmdzJztcbmltcG9ydCB7IENoYXRNYW5hZ2VyIH0gZnJvbSAnLi9jaGF0L0NoYXRNYW5hZ2VyJztcbmltcG9ydCB7IENvbW1hbmRNYW5hZ2VyIH0gZnJvbSAnLi9jb21tYW5kL0NvbW1hbmRNYW5hZ2VyJztcbmltcG9ydCB7IEV2ZW50RW1pdHRlciB9IGZyb20gJy4vZXZlbnRzL0V2ZW50RW1pdHRlcic7XG5pbXBvcnQgeyBUaWNrRXZlbnQgfSBmcm9tICcuL2V2ZW50cy9FdmVudHMnO1xuaW1wb3J0IFJha25ldENvbm5lY3RFdmVudCBmcm9tICcuL2V2ZW50cy9yYWtuZXQvUmFrbmV0Q29ubmVjdEV2ZW50JztcbmltcG9ydCBSYWtuZXREaXNjb25uZWN0RXZlbnQgZnJvbSAnLi9ldmVudHMvcmFrbmV0L1Jha25ldERpc2Nvbm5lY3RFdmVudCc7XG5pbXBvcnQgUmFrbmV0RW5jYXBzdWxhdGVkUGFja2V0RXZlbnQgZnJvbSAnLi9ldmVudHMvcmFrbmV0L1Jha25ldEVuY2Fwc3VsYXRlZFBhY2tldEV2ZW50JztcbmltcG9ydCBJdGVtTWFuYWdlciBmcm9tICcuL2l0ZW0vSXRlbU1hbmFnZXInO1xuaW1wb3J0IENsaWVudENvbm5lY3Rpb24gZnJvbSAnLi9uZXR3b3JrL0NsaWVudENvbm5lY3Rpb24nO1xuaW1wb3J0IElkZW50aWZpZXJzIGZyb20gJy4vbmV0d29yay9JZGVudGlmaWVycyc7XG5pbXBvcnQgUGFja2V0UmVnaXN0cnkgZnJvbSAnLi9uZXR3b3JrL1BhY2tldFJlZ2lzdHJ5JztcbmltcG9ydCB0eXBlIHsgRGF0YVBhY2tldCB9IGZyb20gJy4vbmV0d29yay9QYWNrZXRzJztcbmltcG9ydCBCYXRjaFBhY2tldCBmcm9tICcuL25ldHdvcmsvcGFja2V0L0JhdGNoUGFja2V0JztcbmltcG9ydCB7IFBlcm1pc3Npb25NYW5hZ2VyIH0gZnJvbSAnLi9wZXJtaXNzaW9uL1Blcm1pc3Npb25NYW5hZ2VyJztcbmltcG9ydCB7IFF1ZXJ5TWFuYWdlciB9IGZyb20gJy4vcXVlcnkvUXVlcnlNYW5hZ2VyJztcbmltcG9ydCBUaW1lciBmcm9tICcuL3V0aWxzL1RpbWVyJztcbmltcG9ydCBXb3JsZE1hbmFnZXIgZnJvbSAnLi93b3JsZC9Xb3JsZE1hbmFnZXInO1xuXG5pbXBvcnQgdHlwZSB7IEluZXRBZGRyZXNzLCBSYWtOZXRTZXNzaW9uIH0gZnJvbSAnQGpzcHJpc21hcmluZS9yYWtuZXQnO1xuaW1wb3J0IHR5cGUgeyBDb25maWcgfSBmcm9tICcuL2NvbmZpZy9Db25maWcnO1xuXG5pbXBvcnQgdHlwZSB7IExvZ2dlciB9IGZyb20gJ0Bqc3ByaXNtYXJpbmUvbG9nZ2VyJztcbmltcG9ydCB7IHZlcnNpb24gfSBmcm9tICcuLi9wYWNrYWdlLmpzb24nIHdpdGggeyB0eXBlOiAnanNvbicgfTtcblxuLyoqXG4gKiBKU1ByaXNtYXJpbmUncyBtYWluIHNlcnZlciBjbGFzcy5cbiAqIEBwdWJsaWNcbiAqL1xuZXhwb3J0IGRlZmF1bHQgY2xhc3MgU2VydmVyIGV4dGVuZHMgRXZlbnRFbWl0dGVyIHtcbiAgICBwcml2YXRlIHJha25ldDogUmFrTmV0TGlzdGVuZXIgfCB1bmRlZmluZWQ7XG4gICAgcHJpdmF0ZSByZWFkb25seSBsb2dnZXI6IExvZ2dlcjtcbiAgICBwcml2YXRlIHJlYWRvbmx5IGNvbmZpZzogQ29uZmlnO1xuICAgIHByaXZhdGUgcmVhZG9ubHkgY29uc29sZTogQ29uc29sZSB8IHVuZGVmaW5lZDtcbiAgICBwcml2YXRlIHJlYWRvbmx5IHBhY2tldFJlZ2lzdHJ5OiBQYWNrZXRSZWdpc3RyeTtcbiAgICBwcml2YXRlIHJlYWRvbmx5IHNlc3Npb25NYW5hZ2VyID0gbmV3IFNlc3Npb25NYW5hZ2VyKCk7XG4gICAgcHJpdmF0ZSByZWFkb25seSBjb21tYW5kTWFuYWdlcjogQ29tbWFuZE1hbmFnZXI7XG4gICAgcHJpdmF0ZSByZWFkb25seSB3b3JsZE1hbmFnZXI6IFdvcmxkTWFuYWdlcjtcbiAgICBwcml2YXRlIHJlYWRvbmx5IGl0ZW1NYW5hZ2VyOiBJdGVtTWFuYWdlcjtcbiAgICBwcml2YXRlIHJlYWRvbmx5IGJsb2NrTWFuYWdlcjogQmxvY2tNYW5hZ2VyO1xuICAgIHByaXZhdGUgcmVhZG9ubHkgcXVlcnlNYW5hZ2VyOiBRdWVyeU1hbmFnZXI7XG4gICAgcHJpdmF0ZSByZWFkb25seSBjaGF0TWFuYWdlcjogQ2hhdE1hbmFnZXI7XG4gICAgcHJpdmF0ZSByZWFkb25seSBwZXJtaXNzaW9uTWFuYWdlcjogUGVybWlzc2lvbk1hbmFnZXI7XG4gICAgcHJpdmF0ZSByZWFkb25seSBiYW5NYW5hZ2VyOiBCYW5NYW5hZ2VyO1xuXG4gICAgLyoqXG4gICAgICogSWYgdGhlIHNlcnZlciBpcyBzdG9wcGluZy5cbiAgICAgKiBAaW50ZXJuYWxcbiAgICAgKi9cbiAgICBwcml2YXRlIHN0b3BwaW5nID0gZmFsc2U7XG5cbiAgICAvKipcbiAgICAgKiBUaGUgY3VycmVudCB0aWNrZXIgdGltZXIuXG4gICAgICogQGludGVybmFsXG4gICAgICovXG4gICAgcHJpdmF0ZSB0aWNrZXJUaW1lcjogTm9kZUpTLlRpbWVvdXQgfCB1bmRlZmluZWQ7XG5cbiAgICAvKipcbiAgICAgKiBUaGUgY3VycmVudCBUUFMuXG4gICAgICogQGludGVybmFsXG4gICAgICovXG4gICAgcHJpdmF0ZSB0cHMgPSAyMDtcblxuICAgIC8qKlxuICAgICAqIFRoZSBjdXJyZW50IHRpY2suXG4gICAgICogQGludGVybmFsXG4gICAgICovXG4gICAgcHJpdmF0ZSBjdXJyZW50VGljayA9IDBuO1xuXG4gICAgLyoqXG4gICAgICogSWYgdGhlIHNlcnZlciBpcyBoZWFkbGVzcy5cbiAgICAgKiBAaW50ZXJuYWxcbiAgICAgKi9cbiAgICBwcml2YXRlIHJlYWRvbmx5IGhlYWRsZXNzOiBib29sZWFuO1xuXG4gICAgLy8gVE9ETzogTW92ZSB0aGlzIHNvbWV3aGVyZSBlbHNlLlxuICAgIHByaXZhdGUgc3RhdGljIHJlYWRvbmx5IE1JTkVDUkFGVF9USUNLX1RJTUVfTVMgPSAxMDAwIC8gMjA7XG5cbiAgICAvKipcbiAgICAgKiBDcmVhdGVzIGEgbmV3IHNlcnZlciBpbnN0YW5jZS5cbiAgICAgKiBAcGFyYW0ge29iamVjdH0gb3B0aW9ucyAtIFRoZSBvcHRpb25zLlxuICAgICAqIEBwYXJhbSB7TG9nZ2VyQnVpbGRlcn0gb3B0aW9ucy5sb2dnZXIgLSBUaGUgbG9nZ2VyLlxuICAgICAqIEBwYXJhbSB7Q29uZmlnfSBvcHRpb25zLmNvbmZpZyAtIFRoZSBjb25maWcuXG4gICAgICogQHJldHVybnMge1NlcnZlcn0gVGhlIHNlcnZlciBpbnN0YW5jZS5cbiAgICAgKi9cbiAgICBwdWJsaWMgY29uc3RydWN0b3IoeyBsb2dnZXIsIGNvbmZpZywgaGVhZGxlc3MgPSBmYWxzZSB9OiB7IGxvZ2dlcjogTG9nZ2VyOyBjb25maWc6IENvbmZpZzsgaGVhZGxlc3M/OiBib29sZWFuIH0pIHtcbiAgICAgICAgc3VwZXIoKTtcblxuICAgICAgICB0aGlzLmhlYWRsZXNzID0gaGVhZGxlc3M7XG5cbiAgICAgICAgbG9nZ2VyLmluZm8oXG4gICAgICAgICAgICBgU3RhcnRpbmcgSlNQcmlzbWFyaW5lIHNlcnZlciB2ZXJzaW9uIMKnZXYke3ZlcnNpb259wqdyIGZvciBNaW5lY3JhZnQ6IEJlZHJvY2sgRWRpdGlvbiAke0lkZW50aWZpZXJzLk1pbmVjcmFmdFZlcnNpb25zLmF0KC0xKX0gKHByb3RvY29sIHZlcnNpb24gwqdlJHtJZGVudGlmaWVycy5Qcm90b2NvbH3Cp3IpYFxuICAgICAgICApO1xuXG4gICAgICAgIHRoaXMubG9nZ2VyID0gbG9nZ2VyO1xuICAgICAgICB0aGlzLmNvbmZpZyA9IGNvbmZpZztcbiAgICAgICAgdGhpcy5wYWNrZXRSZWdpc3RyeSA9IG5ldyBQYWNrZXRSZWdpc3RyeSh0aGlzKTtcbiAgICAgICAgdGhpcy5pdGVtTWFuYWdlciA9IG5ldyBJdGVtTWFuYWdlcih0aGlzKTtcbiAgICAgICAgdGhpcy5ibG9ja01hbmFnZXIgPSBuZXcgQmxvY2tNYW5hZ2VyKHRoaXMpO1xuICAgICAgICB0aGlzLndvcmxkTWFuYWdlciA9IG5ldyBXb3JsZE1hbmFnZXIodGhpcyk7XG4gICAgICAgIGlmICghdGhpcy5oZWFkbGVzcykgdGhpcy5jb25zb2xlID0gbmV3IENvbnNvbGUodGhpcyk7XG4gICAgICAgIHRoaXMuY29tbWFuZE1hbmFnZXIgPSBuZXcgQ29tbWFuZE1hbmFnZXIodGhpcyk7XG4gICAgICAgIHRoaXMucXVlcnlNYW5hZ2VyID0gbmV3IFF1ZXJ5TWFuYWdlcih0aGlzKTtcbiAgICAgICAgdGhpcy5jaGF0TWFuYWdlciA9IG5ldyBDaGF0TWFuYWdlcih0aGlzKTtcbiAgICAgICAgdGhpcy5wZXJtaXNzaW9uTWFuYWdlciA9IG5ldyBQZXJtaXNzaW9uTWFuYWdlcih0aGlzKTtcbiAgICAgICAgdGhpcy5iYW5NYW5hZ2VyID0gbmV3IEJhbk1hbmFnZXIodGhpcyk7XG4gICAgfVxuXG4gICAgLyoqXG4gICAgICogRW5hYmxlcyB0aGUgc2VydmVyLlxuICAgICAqIEByZXR1cm5zIHtQcm9taXNlPHZvaWQ+fSBBIHByb21pc2UgdGhhdCByZXNvbHZlcyB3aGVuIHRoZSBzZXJ2ZXIgaXMgZW5hYmxlZC5cbiAgICAgKiBAaW50ZXJuYWxcbiAgICAgKi9cbiAgICBwcml2YXRlIGFzeW5jIGVuYWJsZSgpOiBQcm9taXNlPHZvaWQ+IHtcbiAgICAgICAgYXdhaXQgQmxvY2tNYXBwaW5ncy5pbml0TWFwcGluZ3ModGhpcyk7XG5cbiAgICAgICAgYXdhaXQgdGhpcy5jb25maWcuZW5hYmxlKCk7XG4gICAgICAgIGF3YWl0IHRoaXMuY29uc29sZT8uZW5hYmxlKCk7XG4gICAgICAgIGF3YWl0IHRoaXMubG9nZ2VyLmVuYWJsZSgpO1xuICAgICAgICBhd2FpdCB0aGlzLnBlcm1pc3Npb25NYW5hZ2VyLmVuYWJsZSgpO1xuICAgICAgICBhd2FpdCB0aGlzLnBhY2tldFJlZ2lzdHJ5LmVuYWJsZSgpO1xuICAgICAgICBhd2FpdCB0aGlzLml0ZW1NYW5hZ2VyLmVuYWJsZSgpO1xuICAgICAgICBhd2FpdCB0aGlzLmJsb2NrTWFuYWdlci5lbmFibGUoKTtcbiAgICAgICAgYXdhaXQgdGhpcy5iYW5NYW5hZ2VyLmVuYWJsZSgpO1xuICAgICAgICBhd2FpdCB0aGlzLmNvbW1hbmRNYW5hZ2VyLmVuYWJsZSgpO1xuICAgICAgICBhd2FpdCB0aGlzLndvcmxkTWFuYWdlci5lbmFibGUoKTtcblxuICAgICAgICB0aGlzLmxvZ2dlci5zZXRDb25zb2xlKHRoaXMuY29uc29sZSk7XG4gICAgfVxuXG4gICAgLyoqXG4gICAgICogRGlzYWJsZXMgdGhlIHNlcnZlci5cbiAgICAgKiBAcmV0dXJucyB7UHJvbWlzZTx2b2lkPn0gQSBwcm9taXNlIHRoYXQgcmVzb2x2ZXMgd2hlbiB0aGUgc2VydmVyIGlzIGRpc2FibGVkLlxuICAgICAqIEBpbnRlcm5hbFxuICAgICAqL1xuICAgIHByaXZhdGUgYXN5bmMgZGlzYWJsZSgpOiBQcm9taXNlPHZvaWQ+IHtcbiAgICAgICAgYXdhaXQgdGhpcy53b3JsZE1hbmFnZXIuZGlzYWJsZSgpO1xuICAgICAgICBhd2FpdCB0aGlzLmNvbW1hbmRNYW5hZ2VyLmRpc2FibGUoKTtcbiAgICAgICAgYXdhaXQgdGhpcy5iYW5NYW5hZ2VyLmRpc2FibGUoKTtcbiAgICAgICAgYXdhaXQgdGhpcy5ibG9ja01hbmFnZXIuZGlzYWJsZSgpO1xuICAgICAgICBhd2FpdCB0aGlzLml0ZW1NYW5hZ2VyLmRpc2FibGUoKTtcbiAgICAgICAgYXdhaXQgdGhpcy5wZXJtaXNzaW9uTWFuYWdlci5kaXNhYmxlKCk7XG4gICAgICAgIGF3YWl0IHRoaXMucGFja2V0UmVnaXN0cnkuZGlzYWJsZSgpO1xuICAgICAgICBhd2FpdCB0aGlzLmNvbmZpZy5kaXNhYmxlKCk7XG4gICAgICAgIGF3YWl0IHRoaXMubG9nZ2VyLmRpc2FibGUoKTtcblxuICAgICAgICBCbG9ja01hcHBpbmdzLnJlc2V0KCk7XG4gICAgfVxuXG4gICAgcHVibGljIGdldE1ldGFkYXRhKCkge1xuICAgICAgICBpZiAoIXRoaXMucmFrbmV0KSB0aHJvdyBuZXcgRXJyb3IoJ1NlcnZlciBpcyBub3Qgc3RhcnRlZCcpO1xuICAgICAgICByZXR1cm4gdGhpcy5yYWtuZXQuc2VydmVyTmFtZTtcbiAgICB9XG5cbiAgICAvKipcbiAgICAgKiBSZWxvYWRzIHRoZSBzZXJ2ZXIuXG4gICAgICogQHJldHVybnMge1Byb21pc2U8dm9pZD59IEEgcHJvbWlzZSB0aGF0IHJlc29sdmVzIHdoZW4gdGhlIHNlcnZlciBpcyByZWxvYWRlZC5cbiAgICAgKiBAcmVtYXJrcyBUaGlzIG1ldGhvZCBpcyBlcXVpdmFsZW50IHRvIGNhbGxpbmcge0BsaW5rIFNlcnZlciNkaXNhYmxlfSBhbmQge0BsaW5rIFNlcnZlciNlbmFibGV9LlxuICAgICAqIEByZW1hcmtzIFRoaXMgbWV0aG9kIGFuZCBmdW5jdGlvbmFsaXR5IGlzIHVuc3VwcG9ydGVkIGFuZCBzaG91bGQgaWRlYWxseSBiZSBjb21wbGV0ZWx5IGF2b2lkZWQuXG4gICAgICovXG4gICAgcHVibGljIGFzeW5jIHJlbG9hZCgpOiBQcm9taXNlPHZvaWQ+IHtcbiAgICAgICAgYXdhaXQgdGhpcy5kaXNhYmxlKCk7XG4gICAgICAgIGF3YWl0IHRoaXMuZW5hYmxlKCk7XG4gICAgfVxuXG4gICAgLyoqXG4gICAgICogU3RhcnRzIHRoZSBzZXJ2ZXIuXG4gICAgICogQHBhcmFtIHtzdHJpbmd9IFtzZXJ2ZXJJcD0nMC4wLjAuMCddIC0gVGhlIHNlcnZlciBJUC5cbiAgICAgKiBAcGFyYW0ge251bWJlcn0gW3BvcnQ9MTkxMzJdIC0gVGhlIHNlcnZlciBwb3J0LlxuICAgICAqIEByZXR1cm5zIHtQcm9taXNlPHZvaWQ+fSBBIHByb21pc2UgdGhhdCByZXNvbHZlcyB3aGVuIHRoZSBzZXJ2ZXIgaXMgc3RhcnRlZC5cbiAgICAgKi9cbiAgICBwdWJsaWMgYXN5bmMgYm9vdHN0cmFwKHNlcnZlcklwID0gJzAuMC4wLjAnLCBwb3J0ID0gMTkxMzIpOiBQcm9taXNlPHZvaWQ+IHtcbiAgICAgICAgYXdhaXQgdGhpcy5lbmFibGUoKTtcblxuICAgICAgICB0aGlzLnJha25ldCA9IG5ldyBSYWtOZXRMaXN0ZW5lcihcbiAgICAgICAgICAgIHRoaXMuZ2V0Q29uZmlnKCkuZ2V0TWF4UGxheWVycygpLFxuICAgICAgICAgICAgdGhpcy5nZXRDb25maWcoKS5nZXRPbmxpbmVNb2RlKCksXG4gICAgICAgICAgICBuZXcgU2VydmVyTmFtZSh0aGlzKSxcbiAgICAgICAgICAgIHRoaXMuZ2V0TG9nZ2VyKClcbiAgICAgICAgKTtcbiAgICAgICAgdGhpcy5yYWtuZXQuc3RhcnQoc2VydmVySXAsIHBvcnQpO1xuXG4gICAgICAgIHRoaXMucmFrbmV0Lm9uKCdvcGVuQ29ubmVjdGlvbicsIGFzeW5jIChzZXNzaW9uOiBSYWtOZXRTZXNzaW9uKSA9PiB7XG4gICAgICAgICAgICBjb25zdCBldmVudCA9IG5ldyBSYWtuZXRDb25uZWN0RXZlbnQoc2Vzc2lvbik7XG4gICAgICAgICAgICBhd2FpdCB0aGlzLmVtaXQoJ3Jha25ldENvbm5lY3QnLCBldmVudCk7XG5cbiAgICAgICAgICAgIGlmIChldmVudC5pc0NhbmNlbGxlZCgpKSB7XG4gICAgICAgICAgICAgICAgc2Vzc2lvbi5kaXNjb25uZWN0KCk7XG4gICAgICAgICAgICAgICAgcmV0dXJuO1xuICAgICAgICAgICAgfVxuXG4gICAgICAgICAgICBjb25zdCB0b2tlbiA9IHNlc3Npb24uZ2V0QWRkcmVzcygpLnRvVG9rZW4oKTtcbiAgICAgICAgICAgIGlmICh0aGlzLnNlc3Npb25NYW5hZ2VyLmhhcyh0b2tlbikpIHtcbiAgICAgICAgICAgICAgICB0aGlzLmxvZ2dlci5lcnJvcihgQW5vdGhlciBjbGllbnQgd2l0aCB0b2tlbiAoJHt0b2tlbn0pIGlzIGFscmVhZHkgY29ubmVjdGVkIWApO1xuICAgICAgICAgICAgICAgIHNlc3Npb24uZGlzY29ubmVjdCgnQWxyZWFkeSBjb25uZWN0ZWQgZnJvbSBhbm90aGVyIGxvY2F0aW9uJyk7XG4gICAgICAgICAgICAgICAgcmV0dXJuO1xuICAgICAgICAgICAgfVxuXG4gICAgICAgICAgICBjb25zdCB0aW1lciA9IG5ldyBUaW1lcigpO1xuICAgICAgICAgICAgdGhpcy5sb2dnZXIuZGVidWcoYCR7dG9rZW59IGlzIGF0dGVtcHRpbmcgdG8gY29ubmVjdGApO1xuICAgICAgICAgICAgdGhpcy5zZXNzaW9uTWFuYWdlci5hZGQodG9rZW4sIG5ldyBDbGllbnRDb25uZWN0aW9uKHNlc3Npb24sIHRoaXMubG9nZ2VyKSk7XG4gICAgICAgICAgICB0aGlzLmxvZ2dlci52ZXJib3NlKGBOZXcgY29ubmVjdGlvbiBoYW5kbGluZyB0b29rIMKnZSR7dGltZXIuc3RvcCgpfSBtc8KncmApO1xuICAgICAgICB9KTtcblxuICAgICAgICB0aGlzLnJha25ldC5vbignY2xvc2VDb25uZWN0aW9uJywgYXN5bmMgKGluZXRBZGRyOiBJbmV0QWRkcmVzcywgcmVhc29uOiBzdHJpbmcpID0+IHtcbiAgICAgICAgICAgIGNvbnN0IGV2ZW50ID0gbmV3IFJha25ldERpc2Nvbm5lY3RFdmVudChpbmV0QWRkciwgcmVhc29uKTtcbiAgICAgICAgICAgIGF3YWl0IHRoaXMuZW1pdCgncmFrbmV0RGlzY29ubmVjdCcsIGV2ZW50KTtcblxuICAgICAgICAgICAgY29uc3QgdGltZSA9IERhdGUubm93KCk7XG4gICAgICAgICAgICBjb25zdCB0b2tlbiA9IGluZXRBZGRyLnRvVG9rZW4oKTtcblxuICAgICAgICAgICAgY29uc3Qgc2Vzc2lvbiA9IHRoaXMuc2Vzc2lvbk1hbmFnZXIuZ2V0KHRva2VuKTtcbiAgICAgICAgICAgIGlmICghc2Vzc2lvbikge1xuICAgICAgICAgICAgICAgIHRoaXMubG9nZ2VyLmRlYnVnKGBDYW5ub3QgcmVtb3ZlIGNvbm5lY3Rpb24gZnJvbSBub24tZXhpc3RpbmcgcGxheWVyICgke3Rva2VufSlgKTtcbiAgICAgICAgICAgICAgICByZXR1cm47XG4gICAgICAgICAgICB9XG5cbiAgICAgICAgICAgIGF3YWl0IHNlc3Npb24uY2xvc2VQbGF5ZXJTZXNzaW9uKCk7XG5cbiAgICAgICAgICAgIHRoaXMuc2Vzc2lvbk1hbmFnZXIucmVtb3ZlKHRva2VuKTtcbiAgICAgICAgICAgIHRoaXMubG9nZ2VyLmRlYnVnKGAke3Rva2VufSBkaXNjb25uZWN0ZWQgZHVlIHRvICR7cmVhc29ufWApO1xuICAgICAgICAgICAgdGhpcy5sb2dnZXIuZGVidWcoYFBsYXllciBkZXN0cnVjdGlvbiB0b29rIGFib3V0ICR7RGF0ZS5ub3coKSAtIHRpbWV9IG1zYCk7XG4gICAgICAgIH0pO1xuXG4gICAgICAgIHRoaXMucmFrbmV0Lm9uKCdlbmNhcHN1bGF0ZWQnLCBhc3luYyAocGFja2V0OiBhbnksIGluZXRBZGRyOiBJbmV0QWRkcmVzcykgPT4ge1xuICAgICAgICAgICAgY29uc3QgZXZlbnQgPSBuZXcgUmFrbmV0RW5jYXBzdWxhdGVkUGFja2V0RXZlbnQoaW5ldEFkZHIsIHBhY2tldCk7XG4gICAgICAgICAgICBhd2FpdCB0aGlzLmVtaXQoJ3Jha25ldEVuY2Fwc3VsYXRlZFBhY2tldCcsIGV2ZW50KTtcblxuICAgICAgICAgICAgbGV0IGNvbm5lY3Rpb246IENsaWVudENvbm5lY3Rpb24gfCBudWxsO1xuICAgICAgICAgICAgaWYgKChjb25uZWN0aW9uID0gdGhpcy5zZXNzaW9uTWFuYWdlci5nZXQoaW5ldEFkZHIudG9Ub2tlbigpKSA/PyBudWxsKSA9PT0gbnVsbCkge1xuICAgICAgICAgICAgICAgIHRoaXMubG9nZ2VyLmVycm9yKGBHb3QgYSBwYWNrZXQgZnJvbSBhIGNsb3NlZCBjb25uZWN0aW9uICgke2luZXRBZGRyLnRvVG9rZW4oKX0pYCk7XG4gICAgICAgICAgICAgICAgcmV0dXJuO1xuICAgICAgICAgICAgfVxuXG4gICAgICAgICAgICB0cnkge1xuICAgICAgICAgICAgICAgIC8vIFJlYWQgYmF0Y2ggY29udGVudCBhbmQgaGFuZGxlIHRoZW1cbiAgICAgICAgICAgICAgICBjb25zdCBiYXRjaGVkID0gbmV3IEJhdGNoUGFja2V0KHBhY2tldC5jb250ZW50KTtcbiAgICAgICAgICAgICAgICBiYXRjaGVkLmNvbXByZXNzZWQgPSBjb25uZWN0aW9uLmhhc0NvbXByZXNzaW9uO1xuXG4gICAgICAgICAgICAgICAgLy8gUmVhZCBhbGwgcGFja2V0cyBpbnNpZGUgYmF0Y2ggYW5kIGhhbmRsZSB0aGVtXG4gICAgICAgICAgICAgICAgZm9yIChjb25zdCBidWYgb2YgYXdhaXQgYmF0Y2hlZC5hc3luY0RlY29kZSgpKSB7XG4gICAgICAgICAgICAgICAgICAgIGNvbnN0IHBpZCA9IGJ1ZlswXSE7XG5cbiAgICAgICAgICAgICAgICAgICAgaWYgKCF0aGlzLnBhY2tldFJlZ2lzdHJ5LmdldFBhY2tldHMoKS5oYXMocGlkKSkge1xuICAgICAgICAgICAgICAgICAgICAgICAgdGhpcy5sb2dnZXIud2FybihgUGFja2V0IDB4JHtwaWQudG9TdHJpbmcoMTYpfSBpc24ndCBpbXBsZW1lbnRlZGApO1xuICAgICAgICAgICAgICAgICAgICAgICAgY29udGludWU7XG4gICAgICAgICAgICAgICAgICAgIH1cblxuICAgICAgICAgICAgICAgICAgICAvLyBHZXQgcGFja2V0IGZyb20gcmVnaXN0cnlcbiAgICAgICAgICAgICAgICAgICAgY29uc3QgcGFja2V0ID0gbmV3ICh0aGlzLnBhY2tldFJlZ2lzdHJ5LmdldFBhY2tldHMoKS5nZXQocGlkKSEpKGJ1Zik7XG5cbiAgICAgICAgICAgICAgICAgICAgdHJ5IHtcbiAgICAgICAgICAgICAgICAgICAgICAgIHBhY2tldC5kZWNvZGUoKTtcbiAgICAgICAgICAgICAgICAgICAgfSBjYXRjaCAoZXJyb3I6IHVua25vd24pIHtcbiAgICAgICAgICAgICAgICAgICAgICAgIHRoaXMubG9nZ2VyLmVycm9yKGVycm9yKTtcbiAgICAgICAgICAgICAgICAgICAgICAgIHRoaXMubG9nZ2VyLmVycm9yKGBFcnJvciB3aGlsZSBkZWNvZGluZyBwYWNrZXQ6ICR7cGFja2V0LmNvbnN0cnVjdG9yLm5hbWV9OiAke2Vycm9yfWApO1xuICAgICAgICAgICAgICAgICAgICAgICAgY29udGludWU7XG4gICAgICAgICAgICAgICAgICAgIH1cblxuICAgICAgICAgICAgICAgICAgICB0cnkge1xuICAgICAgICAgICAgICAgICAgICAgICAgY29uc3QgaGFuZGxlciA9IHRoaXMucGFja2V0UmVnaXN0cnkuZ2V0SGFuZGxlcihwaWQpO1xuICAgICAgICAgICAgICAgICAgICAgICAgdGhpcy5sb2dnZXIuc2lsbHkoYFJlY2VpdmVkIMKnYiR7cGFja2V0LmNvbnN0cnVjdG9yLm5hbWV9wqdyIHBhY2tldGApO1xuICAgICAgICAgICAgICAgICAgICAgICAgYXdhaXQgKGhhbmRsZXIgYXMgYW55KS5oYW5kbGUocGFja2V0LCB0aGlzLCBjb25uZWN0aW9uLmdldFBsYXllclNlc3Npb24oKSA/PyBjb25uZWN0aW9uKTtcbiAgICAgICAgICAgICAgICAgICAgfSBjYXRjaCAoZXJyb3I6IHVua25vd24pIHtcbiAgICAgICAgICAgICAgICAgICAgICAgIHRoaXMubG9nZ2VyLmVycm9yKGBIYW5kbGVyIGVycm9yICR7cGFja2V0LmNvbnN0cnVjdG9yLm5hbWV9LWhhbmRsZXI6ICgke2Vycm9yfSlgKTtcbiAgICAgICAgICAgICAgICAgICAgICAgIHRoaXMubG9nZ2VyLmVycm9yKGVycm9yKTtcbiAgICAgICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgIH0gY2F0Y2ggKGVycm9yOiB1bmtub3duKSB7XG4gICAgICAgICAgICAgICAgdGhpcy5sb2dnZXIuZXJyb3IoZXJyb3IpO1xuICAgICAgICAgICAgfVxuICAgICAgICB9KTtcblxuICAgICAgICB0aGlzLnJha25ldC5vbigncmF3JywgYXN5bmMgKGJ1ZmZlcjogQnVmZmVyLCBpbmV0QWRkcjogSW5ldEFkZHJlc3MpID0+IHtcbiAgICAgICAgICAgIGlmICghdGhpcy5jb25maWcuZ2V0RW5hYmxlUXVlcnkoKSkgcmV0dXJuO1xuXG4gICAgICAgICAgICB0cnkge1xuICAgICAgICAgICAgICAgIGF3YWl0IHRoaXMucXVlcnlNYW5hZ2VyLm9uUmF3KGJ1ZmZlciwgaW5ldEFkZHIpO1xuICAgICAgICAgICAgfSBjYXRjaCAoZXJyb3I6IHVua25vd24pIHtcbiAgICAgICAgICAgICAgICB0aGlzLmxvZ2dlci52ZXJib3NlKGBRdWVyeU1hbmFnZXIgZW5jb3VudGVyZWQgYW4gZXJyb3JgKTtcbiAgICAgICAgICAgICAgICB0aGlzLmxvZ2dlci5lcnJvcihlcnJvcik7XG4gICAgICAgICAgICB9XG4gICAgICAgIH0pO1xuXG4gICAgICAgIGlmICh0aGlzLmNvbmZpZy5nZXRFbmFibGVUaWNraW5nKCkpIHtcbiAgICAgICAgICAgIGxldCBzdGFydFRpbWUgPSBEYXRlLm5vdygpO1xuICAgICAgICAgICAgbGV0IHRwc1N0YXJ0VGltZSA9IERhdGUubm93KCk7XG4gICAgICAgICAgICBsZXQgbGFzdFRpY2tUaW1lID0gRGF0ZS5ub3coKTtcbiAgICAgICAgICAgIGxldCB0cHNTdGFydFRpY2sgPSB0aGlzLmdldFRpY2soKTtcbiAgICAgICAgICAgIGNvbnN0IHRpY2sgPSBhc3luYyAoKSA9PiB7XG4gICAgICAgICAgICAgICAgaWYgKHRoaXMuc3RvcHBpbmcpIHJldHVybjtcblxuICAgICAgICAgICAgICAgIGNvbnN0IGV2ZW50ID0gbmV3IFRpY2tFdmVudCh0aGlzLmdldFRpY2soKSk7XG4gICAgICAgICAgICAgICAgdm9pZCB0aGlzLmVtaXQoJ3RpY2snLCBldmVudCk7XG5cbiAgICAgICAgICAgICAgICBjb25zdCB0aWNrc1BlclNlY29uZCA9IDEwMDAgLyBTZXJ2ZXIuTUlORUNSQUZUX1RJQ0tfVElNRV9NUztcblxuICAgICAgICAgICAgICAgIC8vIFVwZGF0ZSBhbGwgd29ybGRzLlxuICAgICAgICAgICAgICAgIGF3YWl0IFByb21pc2UuYWxsKHRoaXMud29ybGRNYW5hZ2VyLmdldFdvcmxkcygpLm1hcCgod29ybGQpID0+IHdvcmxkLnVwZGF0ZShldmVudC5nZXRUaWNrKCkpKSk7XG5cbiAgICAgICAgICAgICAgICBpZiAodGhpcy5jb25maWcuZ2V0RW5hYmxlUHJvY2Vzc1RpdGxlKCkgJiYgdGhpcy5nZXRUaWNrKCkgJSB0aWNrc1BlclNlY29uZCA9PT0gMCAmJiAhdGhpcy5oZWFkbGVzcykge1xuICAgICAgICAgICAgICAgICAgICAvLyBVcGRhdGUgdGhlIHByb2Nlc3MgdGl0bGUgd2l0aCBUUFMgYW5kIHRpY2suXG4gICAgICAgICAgICAgICAgICAgIHByb2Nlc3MudGl0bGUgPSBgVFBTOiAke3RoaXMuZ2V0VFBTKCkudG9GaXhlZCgyKX0gfCBUaWNrOiAke3RoaXMuZ2V0VGljaygpfSB8ICR7cHJvY2Vzcy50aXRsZS5zcGxpdCgnfCAnKS5hdCgtMSkhfWA7XG4gICAgICAgICAgICAgICAgfVxuXG4gICAgICAgICAgICAgICAgdGhpcy5jdXJyZW50VGljaysrO1xuICAgICAgICAgICAgICAgIGNvbnN0IGVuZFRpbWUgPSBEYXRlLm5vdygpO1xuICAgICAgICAgICAgICAgIGNvbnN0IGVsYXBzZWRUaW1lID0gZW5kVGltZSAtIHN0YXJ0VGltZTtcbiAgICAgICAgICAgICAgICBjb25zdCBleHBlY3RlZEVsYXBzZWRUaW1lID0gdGhpcy5nZXRUaWNrKCkgKiBTZXJ2ZXIuTUlORUNSQUZUX1RJQ0tfVElNRV9NUztcbiAgICAgICAgICAgICAgICBjb25zdCBleGVjdXRpb25UaW1lID0gZW5kVGltZSAtIGxhc3RUaWNrVGltZTtcblxuICAgICAgICAgICAgICAgIC8vIEFkanVzdCBzbGVlcFRpbWUgYmFzZWQgb24gZXhlY3V0aW9uIHNwZWVkLlxuICAgICAgICAgICAgICAgIGxldCBzbGVlcFRpbWUgPSBTZXJ2ZXIuTUlORUNSQUZUX1RJQ0tfVElNRV9NUyAtIGV4ZWN1dGlvblRpbWU7XG4gICAgICAgICAgICAgICAgaWYgKGVsYXBzZWRUaW1lIDwgZXhwZWN0ZWRFbGFwc2VkVGltZSkge1xuICAgICAgICAgICAgICAgICAgICAvLyBJZiB3ZSdyZSBydW5uaW5nIGZhc3RlciB0aGFuIGV4cGVjdGVkLCBpbmNyZWFzZSBzbGVlcFRpbWUuXG4gICAgICAgICAgICAgICAgICAgIHNsZWVwVGltZSArPSBleHBlY3RlZEVsYXBzZWRUaW1lIC0gZWxhcHNlZFRpbWU7XG4gICAgICAgICAgICAgICAgfSBlbHNlIGlmIChlbGFwc2VkVGltZSA+IGV4cGVjdGVkRWxhcHNlZFRpbWUpIHtcbiAgICAgICAgICAgICAgICAgICAgLy8gSWYgd2UncmUgcnVubmluZyBzbG93ZXIgdGhhbiBleHBlY3RlZCwgZGVjcmVhc2Ugc2xlZXBUaW1lIGJ1dCBkb24ndCBsZXQgaXQgZ28gYmVsb3cgMC5cbiAgICAgICAgICAgICAgICAgICAgc2xlZXBUaW1lID0gTWF0aC5tYXgoMCwgc2xlZXBUaW1lIC0gKGVsYXBzZWRUaW1lIC0gZXhwZWN0ZWRFbGFwc2VkVGltZSkpO1xuICAgICAgICAgICAgICAgIH1cblxuICAgICAgICAgICAgICAgIC8vIENhbGN1bGF0ZSB0cHMgYmFzZWQgb24gdGhlIGFjdHVhbCBlbGFwc2VkIHRpbWUgc2luY2UgdGhlIHN0YXJ0IG9mIHRoZSB0aWNrLlxuICAgICAgICAgICAgICAgIGlmICh0cHNTdGFydFRpbWUgIT09IGVuZFRpbWUpIHtcbiAgICAgICAgICAgICAgICAgICAgdGhpcy50cHMgPSAoKHRoaXMuZ2V0VGljaygpIC0gdHBzU3RhcnRUaWNrKSAqIDEwMDApIC8gKGVuZFRpbWUgLSB0cHNTdGFydFRpbWUpO1xuICAgICAgICAgICAgICAgIH1cblxuICAgICAgICAgICAgICAgIGlmIChlbmRUaW1lIC0gdHBzU3RhcnRUaW1lID49IDEwMDApIHtcbiAgICAgICAgICAgICAgICAgICAgdHBzU3RhcnRUaWNrID0gdGhpcy5nZXRUaWNrKCk7XG4gICAgICAgICAgICAgICAgICAgIHRwc1N0YXJ0VGltZSA9IGVuZFRpbWU7XG4gICAgICAgICAgICAgICAgfVxuXG4gICAgICAgICAgICAgICAgdGhpcy50cHMgPSBNYXRoLm1pbih0aGlzLnRwcywgMjApOyAvLyBFbnN1cmUgdHBzIGRvZXMgbm90IGV4Y2VlZCAyMFxuXG4gICAgICAgICAgICAgICAgbGFzdFRpY2tUaW1lID0gZW5kVGltZTtcbiAgICAgICAgICAgICAgICB0aGlzLnRpY2tlclRpbWVyID0gc2V0VGltZW91dCh0aWNrLCBzbGVlcFRpbWUpO1xuICAgICAgICAgICAgICAgIHRoaXMudGlja2VyVGltZXIudW5yZWYoKTtcbiAgICAgICAgICAgIH07XG5cbiAgICAgICAgICAgIC8vIFN0YXJ0IHRpY2tpbmdcbiAgICAgICAgICAgIHZvaWQgdGljaygpO1xuICAgICAgICB9XG5cbiAgICAgICAgdGhpcy5sb2dnZXIuaW5mbyhgSlNQcmlzbWFyaW5lIGlzIG5vdyBsaXN0ZW5pbmcgb24gcG9ydCDCp2Ike3BvcnR9YCk7XG4gICAgfVxuXG4gICAgLyoqXG4gICAgICogS2lsbHMgdGhlIHNlcnZlciBhc3luY2hyb25vdXNseS5cbiAgICAgKiBAcGFyYW0ge29iamVjdH0gW29wdGlvbnNdIC0gVGhlIG9wdGlvbnMuXG4gICAgICogQHBhcmFtIHtib29sZWFufSBbb3B0aW9ucy5jcmFzaF0gLSBJZiB0aGUgc2VydmVyIHNob3VsZCBjcmFzaC5cbiAgICAgKiBAcGFyYW0ge2Jvb2xlYW59IFtvcHRpb25zLnN0YXlBbGl2ZV0gLSBJZiB3ZSBzaG91bGQgbGV0IHRoZSBwcm9jZXNzIHN0YXkgYWxpdmUuXG4gICAgICogQHJldHVybnMge1Byb21pc2U8dm9pZD59IEEgcHJvbWlzZSB0aGF0IHJlc29sdmVzIHdoZW4gdGhlIHNlcnZlciBpcyBraWxsZWQuXG4gICAgICovXG4gICAgcHVibGljIGFzeW5jIHNodXRkb3duKG9wdGlvbnM/OiB7IGNyYXNoPzogYm9vbGVhbjsgc3RheUFsaXZlPzogYm9vbGVhbiB9KTogUHJvbWlzZTx2b2lkPiB7XG4gICAgICAgIGlmICh0aGlzLnN0b3BwaW5nKSByZXR1cm47XG4gICAgICAgIHRoaXMuc3RvcHBpbmcgPSB0cnVlO1xuXG4gICAgICAgIHRoaXMubG9nZ2VyLmluZm8oJ1N0b3BwaW5nIHNlcnZlcicsICdTZXJ2ZXIva2lsbCcpO1xuICAgICAgICBhd2FpdCB0aGlzLmNvbnNvbGU/LmRpc2FibGUoKTtcblxuICAgICAgICBjbGVhckludGVydmFsKHRoaXMudGlja2VyVGltZXIpO1xuXG4gICAgICAgIHRyeSB7XG4gICAgICAgICAgICAvLyBLaWNrIGFsbCBvbmxpbmUgcGxheWVycy5cbiAgICAgICAgICAgIGF3YWl0IHRoaXMuc2Vzc2lvbk1hbmFnZXIua2lja0FsbFBsYXllcnMoJ1NlcnZlciBjbG9zZWQuJyk7XG5cbiAgICAgICAgICAgIC8vIERpc2FibGUgYWxsIG1hbmFnZXJzLlxuICAgICAgICAgICAgYXdhaXQgdGhpcy5kaXNhYmxlKCk7XG5cbiAgICAgICAgICAgIC8vIGB0aGlzLnJha25ldGAgbWlnaHQgYmUgdW5kZWZpbmVkIGlmIHdlIGtpbGwgdGhlIHNlcnZlciByZWFsbHkgZWFybHkuXG4gICAgICAgICAgICB0aGlzLnJha25ldD8ua2lsbCgpO1xuXG4gICAgICAgICAgICAvLyBGaW5hbGx5LCByZW1vdmUgYWxsIGxpc3RlbmVycy5cbiAgICAgICAgICAgIHRoaXMucmVtb3ZlQWxsTGlzdGVuZXJzKCk7XG5cbiAgICAgICAgICAgIC8vIExvZ2dlciBpcyBubyBsb25nZXIgYXZhaWxhYmxlLlxuICAgICAgICAgICAgY29uc29sZS5kZWJ1ZygnU2VydmVyIHN0b3BwZWQsIEdvb2RieWUhXFxuJyk7XG5cbiAgICAgICAgICAgIGlmICghb3B0aW9ucz8uc3RheUFsaXZlKSBwcm9jZXNzLmV4aXQob3B0aW9ucz8uY3Jhc2ggPyAxIDogMCk7XG4gICAgICAgIH0gY2F0Y2ggKGVycm9yOiB1bmtub3duKSB7XG4gICAgICAgICAgICBjb25zb2xlLmVycm9yKGVycm9yKTtcbiAgICAgICAgICAgIGlmICghb3B0aW9ucz8uc3RheUFsaXZlKSBwcm9jZXNzLmV4aXQoMSk7XG4gICAgICAgIH1cbiAgICB9XG5cbiAgICBwdWJsaWMgYXN5bmMgYnJvYWRjYXN0UGFja2V0PFQgZXh0ZW5kcyBEYXRhUGFja2V0PihkYXRhUGFja2V0OiBUKTogUHJvbWlzZTx2b2lkPiB7XG4gICAgICAgIC8vIE1heWJlIGkgY2FuIGltcHJvdmUgdGhpcyBieSB1c2luZyB0aGUgVURQIGJyb2FkY2FzdCwgYWxsIHVuY29ubmVjdGVkIGNsaWVudHNcbiAgICAgICAgLy8gd2lsbCBpZ25vcmUgdGhlIGNvbm5lY3RlZCBwYWNrZXQgcHJvYmFibHksIGJ1dCBtYXkgY2F1c2UgaXNzdWVzLlxuICAgICAgICBmb3IgKGNvbnN0IG9ubGluZVBsYXllciBvZiB0aGlzLnNlc3Npb25NYW5hZ2VyLmdldEFsbFBsYXllcnMoKSkge1xuICAgICAgICAgICAgYXdhaXQgb25saW5lUGxheWVyLmdldE5ldHdvcmtTZXNzaW9uKCkuZ2V0Q29ubmVjdGlvbigpLnNlbmREYXRhUGFja2V0KGRhdGFQYWNrZXQpO1xuICAgICAgICB9XG4gICAgfVxuXG4gICAgLyoqXG4gICAgICogUmV0dXJucyB0aGUgc2VydmVyIHZlcnNpb24uXG4gICAgICogQHJldHVybnMge3N0cmluZ30gVGhlIHNlcnZlciB2ZXJzaW9uLlxuICAgICAqIEBleGFtcGxlXG4gICAgICogYGBgdHlwZXNjcmlwdFxuICAgICAqIGNvbnNvbGUubG9nKHNlcnZlci5nZXRWZXJzaW9uKCkpO1xuICAgICAqIGBgYFxuICAgICAqL1xuICAgIHB1YmxpYyBnZXRWZXJzaW9uKCk6IHN0cmluZyB7XG4gICAgICAgIHJldHVybiB2ZXJzaW9uO1xuICAgIH1cblxuICAgIC8qKlxuICAgICAqIFJldHVybnMgdGhlIGlkZW50aWZpZXJzLlxuICAgICAqIEByZXR1cm5zIHtJZGVudGlmaWVyc30gVGhlIGlkZW50aWZpZXJzLlxuICAgICAqL1xuICAgIHB1YmxpYyBnZXRJZGVudGlmaWVycygpOiB0eXBlb2YgSWRlbnRpZmllcnMge1xuICAgICAgICByZXR1cm4gSWRlbnRpZmllcnM7XG4gICAgfVxuXG4gICAgLyoqXG4gICAgICogUmV0dXJucyB0aGUgcXVlcnkgbWFuYWdlci5cbiAgICAgKiBAcmV0dXJucyB7UXVlcnlNYW5hZ2VyfSBUaGUgcXVlcnkgbWFuYWdlci5cbiAgICAgKi9cbiAgICBwdWJsaWMgZ2V0UXVlcnlNYW5hZ2VyKCk6IFF1ZXJ5TWFuYWdlciB7XG4gICAgICAgIHJldHVybiB0aGlzLnF1ZXJ5TWFuYWdlcjtcbiAgICB9XG5cbiAgICAvKipcbiAgICAgKiBSZXR1cm5zIHRoZSBjb21tYW5kIG1hbmFnZXIuXG4gICAgICogQHJldHVybnMge0NvbW1hbmRNYW5hZ2VyfSBUaGUgY29tbWFuZCBtYW5hZ2VyLlxuICAgICAqL1xuICAgIHB1YmxpYyBnZXRDb21tYW5kTWFuYWdlcigpOiBDb21tYW5kTWFuYWdlciB7XG4gICAgICAgIHJldHVybiB0aGlzLmNvbW1hbmRNYW5hZ2VyO1xuICAgIH1cblxuICAgIC8qKlxuICAgICAqIFJldHVybnMgdGhlIHBsYXllciBtYW5hZ2VyLlxuICAgICAqIEByZXR1cm5zIHtTZXNzaW9uTWFuYWdlcn0gVGhlIHBsYXllciBtYW5hZ2VyLlxuICAgICAqL1xuICAgIHB1YmxpYyBnZXRTZXNzaW9uTWFuYWdlcigpOiBTZXNzaW9uTWFuYWdlciB7XG4gICAgICAgIHJldHVybiB0aGlzLnNlc3Npb25NYW5hZ2VyO1xuICAgIH1cblxuICAgIC8qKlxuICAgICAqIFJldHVybnMgdGhlIHdvcmxkIG1hbmFnZXIuXG4gICAgICogQHJldHVybnMge1dvcmxkTWFuYWdlcn0gVGhlIHdvcmxkIG1hbmFnZXIuXG4gICAgICovXG4gICAgcHVibGljIGdldFdvcmxkTWFuYWdlcigpOiBXb3JsZE1hbmFnZXIge1xuICAgICAgICByZXR1cm4gdGhpcy53b3JsZE1hbmFnZXI7XG4gICAgfVxuXG4gICAgLyoqXG4gICAgICogUmV0dXJucyB0aGUgaXRlbSBtYW5hZ2VyLlxuICAgICAqIEByZXR1cm5zIHtJdGVtTWFuYWdlcn0gVGhlIGl0ZW0gbWFuYWdlci5cbiAgICAgKi9cbiAgICBwdWJsaWMgZ2V0SXRlbU1hbmFnZXIoKTogSXRlbU1hbmFnZXIge1xuICAgICAgICByZXR1cm4gdGhpcy5pdGVtTWFuYWdlcjtcbiAgICB9XG5cbiAgICAvKipcbiAgICAgKiBSZXR1cm5zIHRoZSBibG9jayBtYW5hZ2VyLlxuICAgICAqIEByZXR1cm5zIHtCbG9ja01hbmFnZXJ9IFRoZSBibG9jayBtYW5hZ2VyLlxuICAgICAqL1xuICAgIHB1YmxpYyBnZXRCbG9ja01hbmFnZXIoKTogQmxvY2tNYW5hZ2VyIHtcbiAgICAgICAgcmV0dXJuIHRoaXMuYmxvY2tNYW5hZ2VyO1xuICAgIH1cblxuICAgIC8qKlxuICAgICAqIFJldHVybnMgdGhlIGxvZ2dlci5cbiAgICAgKiBAcmV0dXJucyB7TG9nZ2VyQnVpbGRlcn0gVGhlIGxvZ2dlci5cbiAgICAgKiBAZXhhbXBsZVxuICAgICAqIGBgYHR5cGVzY3JpcHRcbiAgICAgKiAvLyBOb3JtYWwgbG9nOlxuICAgICAqIHNlcnZlci5nZXRMb2dnZXIoKS5pbmZvKCdIZWxsbywgd29ybGQhJyk7XG4gICAgICogLy8gRGVidWcgbG9nOlxuICAgICAqIHNlcnZlci5nZXRMb2dnZXIoKS5kZWJ1ZygnSGVsbG8sIHdvcmxkIScpO1xuICAgICAqIC8vIEVycm9yIGxvZzpcbiAgICAgKiBzZXJ2ZXIuZ2V0TG9nZ2VyKCkuZXJyb3IobmV3IEVycm9yKCdIZWxsbyBXb3JsZCcpKTtcbiAgICAgKiBgYGBcbiAgICAgKi9cbiAgICBwdWJsaWMgZ2V0TG9nZ2VyKCk6IExvZ2dlciB7XG4gICAgICAgIHJldHVybiB0aGlzLmxvZ2dlcjtcbiAgICB9XG5cbiAgICAvKipcbiAgICAgKiBSZXR1cm5zIHRoZSBwYWNrZXQgcmVnaXN0cnkuXG4gICAgICogQHJldHVybnMge1BhY2tldFJlZ2lzdHJ5fSBUaGUgcGFja2V0IHJlZ2lzdHJ5LlxuICAgICAqL1xuICAgIHB1YmxpYyBnZXRQYWNrZXRSZWdpc3RyeSgpOiBQYWNrZXRSZWdpc3RyeSB7XG4gICAgICAgIHJldHVybiB0aGlzLnBhY2tldFJlZ2lzdHJ5O1xuICAgIH1cblxuICAgIC8qKlxuICAgICAqIFJldHVybnMgdGhlIHJha25ldCBpbnN0YW5jZS5cbiAgICAgKiBAcmV0dXJucyB7UmFrTmV0TGlzdGVuZXIgfCB1bmRlZmluZWR9IFRoZSByYWtuZXQgaW5zdGFuY2UuXG4gICAgICovXG4gICAgcHVibGljIGdldFJha25ldCgpOiBSYWtOZXRMaXN0ZW5lciB8IHVuZGVmaW5lZCB7XG4gICAgICAgIHJldHVybiB0aGlzLnJha25ldDtcbiAgICB9XG5cbiAgICAvKipcbiAgICAgKiBSZXR1cm5zIHRoZSBjaGF0IG1hbmFnZXIuXG4gICAgICogQHJldHVybnMge0NoYXRNYW5hZ2VyfSBUaGUgY2hhdCBtYW5hZ2VyLlxuICAgICAqL1xuICAgIHB1YmxpYyBnZXRDaGF0TWFuYWdlcigpOiBDaGF0TWFuYWdlciB7XG4gICAgICAgIHJldHVybiB0aGlzLmNoYXRNYW5hZ2VyO1xuICAgIH1cblxuICAgIC8qKlxuICAgICAqIFJldHVybnMgdGhlIGNvbmZpZy5cbiAgICAgKiBAcmV0dXJucyB7Q29uZmlnfSBUaGUgY29uZmlnLlxuICAgICAqIEBleGFtcGxlXG4gICAgICogYGBgdHlwZXNjcmlwdFxuICAgICAqIGNvbnNvbGUubG9nKHNlcnZlci5nZXRDb25maWcoKS5nZXRNYXhQbGF5ZXJzKCkpOyAvLyAyMFxuICAgICAqIGBgYFxuICAgICAqL1xuICAgIHB1YmxpYyBnZXRDb25maWcoKTogQ29uZmlnIHtcbiAgICAgICAgcmV0dXJuIHRoaXMuY29uZmlnO1xuICAgIH1cblxuICAgIC8qKlxuICAgICAqIFJldHVybnMgdGhlIGNvbnNvbGUgaW5zdGFuY2UuXG4gICAgICogQHJldHVybnMge0NvbnNvbGUgfCB1bmRlZmluZWR9IFRoZSBjb25zb2xlIGluc3RhbmNlLlxuICAgICAqL1xuICAgIHB1YmxpYyBnZXRDb25zb2xlKCkge1xuICAgICAgICByZXR1cm4gdGhpcy5jb25zb2xlO1xuICAgIH1cblxuICAgIC8qKlxuICAgICAqIFJldHVybnMgdGhlIHBlcm1pc3Npb24gbWFuYWdlci5cbiAgICAgKiBAcmV0dXJucyB7UGVybWlzc2lvbk1hbmFnZXJ9IFRoZSBwZXJtaXNzaW9uIG1hbmFnZXIuXG4gICAgICovXG4gICAgcHVibGljIGdldFBlcm1pc3Npb25NYW5hZ2VyKCk6IFBlcm1pc3Npb25NYW5hZ2VyIHtcbiAgICAgICAgcmV0dXJuIHRoaXMucGVybWlzc2lvbk1hbmFnZXI7XG4gICAgfVxuXG4gICAgLyoqXG4gICAgICogUmV0dXJucyB0aGUgYmFuIG1hbmFnZXIuXG4gICAgICogQHJldHVybnMge0Jhbk1hbmFnZXJ9IFRoZSBiYW4gbWFuYWdlci5cbiAgICAgKi9cbiAgICBwdWJsaWMgZ2V0QmFuTWFuYWdlcigpOiBCYW5NYW5hZ2VyIHtcbiAgICAgICAgcmV0dXJuIHRoaXMuYmFuTWFuYWdlcjtcbiAgICB9XG5cbiAgICAvKipcbiAgICAgKiBSZXR1cm5zIHRoaXMgUHJpc21hcmluZSBpbnN0YW5jZS5cbiAgICAgKiBAcmV0dXJucyB7U2VydmVyfSBUaGUgUHJpc21hcmluZSBpbnN0YW5jZS5cbiAgICAgKi9cbiAgICBwdWJsaWMgZ2V0U2VydmVyKCk6IFNlcnZlciB7XG4gICAgICAgIHJldHVybiB0aGlzO1xuICAgIH1cblxuICAgIC8qKlxuICAgICAqIFJldHVybnMgdGhlIGN1cnJlbnQgVGljay5cbiAgICAgKiBAcmV0dXJucyB7bnVtYmVyfSBUaGUgY3VycmVudCBUaWNrLlxuICAgICAqL1xuICAgIHB1YmxpYyBnZXRUaWNrKCk6IG51bWJlciB7XG4gICAgICAgIHJldHVybiBOdW1iZXIodGhpcy5jdXJyZW50VGljayk7XG4gICAgfVxuXG4gICAgLyoqXG4gICAgICogUmV0dXJucyB0aGUgY3VycmVudCBUUFMuXG4gICAgICogQHJldHVybnMge251bWJlcn0gVGhlIGN1cnJlbnQgVFBTLlxuICAgICAqL1xuICAgIHB1YmxpYyBnZXRUUFMoKTogbnVtYmVyIHtcbiAgICAgICAgcmV0dXJuIE51bWJlci5wYXJzZUZsb2F0KHRoaXMudHBzLnRvRml4ZWQoMikpO1xuICAgIH1cbn1cbiJdLCJuYW1lcyI6WyJwYWNrZXQiXSwibWFwcGluZ3MiOiI7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7QUFrQ0EsTUFBcUIsZUFBZSxZQUFhLENBQUE7QUFBQSxFQUNyQyxNQUFBO0FBQUEsRUFDUyxNQUFBO0FBQUEsRUFDQSxNQUFBO0FBQUEsRUFDQSxPQUFBO0FBQUEsRUFDQSxjQUFBO0FBQUEsRUFDQSxjQUFBLEdBQWlCLElBQUksY0FBZSxFQUFBO0FBQUEsRUFDcEMsY0FBQTtBQUFBLEVBQ0EsWUFBQTtBQUFBLEVBQ0EsV0FBQTtBQUFBLEVBQ0EsWUFBQTtBQUFBLEVBQ0EsWUFBQTtBQUFBLEVBQ0EsV0FBQTtBQUFBLEVBQ0EsaUJBQUE7QUFBQSxFQUNBLFVBQUE7QUFBQTtBQUFBO0FBQUE7QUFBQTtBQUFBLEVBTVQsUUFBVyxHQUFBLEtBQUE7QUFBQTtBQUFBO0FBQUE7QUFBQTtBQUFBLEVBTVgsV0FBQTtBQUFBO0FBQUE7QUFBQTtBQUFBO0FBQUEsRUFNQSxHQUFNLEdBQUEsRUFBQTtBQUFBO0FBQUE7QUFBQTtBQUFBO0FBQUEsRUFNTixXQUFjLEdBQUEsRUFBQTtBQUFBO0FBQUE7QUFBQTtBQUFBO0FBQUEsRUFNTCxRQUFBO0FBQUE7QUFBQSxFQUdqQixPQUF3Qix5QkFBeUIsR0FBTyxHQUFBLEVB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