UNPKG

@jsprismarine/prismarine

Version:

Dedicated Minecraft Bedrock Edition server written in TypeScript

456 lines (455 loc) 50.9 kB
"use strict"; Object.defineProperties(exports, { __esModule: { value: true }, [Symbol.toStringTag]: { value: "Module" } }); require("./_virtual/_rolldown/runtime.cjs.cjs"); const require_chat_ChatManager = require("./chat/ChatManager.cjs.cjs"); const require_permission_PermissionManager = require("./permission/PermissionManager.cjs.cjs"); const require_query_QueryManager = require("./query/QueryManager.cjs.cjs"); const require_SessionManager = require("./SessionManager.cjs.cjs"); const require_ban_BanManager = require("./ban/BanManager.cjs.cjs"); const require_utils_Timer = require("./utils/Timer.cjs.cjs"); const require_block_BlockManager = require("./block/BlockManager.cjs.cjs"); const require_block_BlockMappings = require("./block/BlockMappings.cjs.cjs"); const require_item_ItemManager = require("./item/ItemManager.cjs.cjs"); const require_network_Identifiers = require("./network/Identifiers.cjs.cjs"); const require_network_packet_BatchPacket = require("./network/packet/BatchPacket.cjs.cjs"); const require_world_WorldManager = require("./world/WorldManager.cjs.cjs"); const require_command_CommandManager = require("./command/CommandManager.cjs.cjs"); const require_events_raknet_RaknetConnectEvent = require("./events/raknet/RaknetConnectEvent.cjs.cjs"); const require_events_raknet_RaknetDisconnectEvent = require("./events/raknet/RaknetDisconnectEvent.cjs.cjs"); const require_events_raknet_RaknetEncapsulatedPacketEvent = require("./events/raknet/RaknetEncapsulatedPacketEvent.cjs.cjs"); const require_events_other_TickEvent = require("./events/other/TickEvent.cjs.cjs"); const require_Console = require("./Console.cjs.cjs"); const require_events_EventEmitter = require("./events/EventEmitter.cjs.cjs"); const require_network_ClientConnection = require("./network/ClientConnection.cjs.cjs"); const require_network_PacketRegistry = require("./network/PacketRegistry.cjs.cjs"); const require_package = require("./package.json.cjs.cjs"); let _jsprismarine_raknet = require("@jsprismarine/raknet"); //#region src/Server.ts /** * JSPrismarine's main server class. * @public */ var Server = class Server extends require_events_EventEmitter.EventEmitter { raknet; logger; config; console; packetRegistry; sessionManager = new require_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; 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${require_package.version}§r for Minecraft: Bedrock Edition ${require_network_Identifiers.default.MinecraftVersions.at(-1)} (protocol version §e${require_network_Identifiers.default.Protocol}§r)`); this.logger = logger; this.config = config; this.packetRegistry = new require_network_PacketRegistry.default(this); this.itemManager = new require_item_ItemManager.default(this); this.blockManager = new require_block_BlockManager.default(this); this.worldManager = new require_world_WorldManager.default(this); if (!this.headless) this.console = new require_Console.default(this); this.commandManager = new require_command_CommandManager.CommandManager(this); this.queryManager = new require_query_QueryManager.QueryManager(this); this.chatManager = new require_chat_ChatManager.ChatManager(this); this.permissionManager = new require_permission_PermissionManager.PermissionManager(this); this.banManager = new require_ban_BanManager.default(this); } /** * Enables the server. * @returns {Promise<void>} A promise that resolves when the server is enabled. * @internal */ async enable() { await require_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(); require_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 _jsprismarine_raknet.RakNetListener(this.getConfig().getMaxPlayers(), this.getConfig().getOnlineMode(), new _jsprismarine_raknet.ServerName(this), this.getLogger()); this.raknet.start(serverIp, port); this.raknet.on("openConnection", async (session) => { const event = new require_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 require_utils_Timer.default(); this.logger.debug(`${token} is attempting to connect`); this.sessionManager.add(token, new require_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 require_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 require_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 require_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 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 require_events_other_TickEvent.default(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 require_package.version; } /** * Returns the identifiers. * @returns {Identifiers} The identifiers. */ getIdentifiers() { return require_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)); } }; //#endregion exports.default = Server; //# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiU2VydmVyLmNqcy5janMiLCJuYW1lcyI6W10sInNvdXJjZXMiOlsiLi4vc3JjL1NlcnZlci50cyJdLCJzb3VyY2VzQ29udGVudCI6WyJpbXBvcnQgeyBSYWtOZXRMaXN0ZW5lciwgU2VydmVyTmFtZSB9IGZyb20gJ0Bqc3ByaXNtYXJpbmUvcmFrbmV0JztcbmltcG9ydCBDb25zb2xlIGZyb20gJy4vQ29uc29sZSc7XG5pbXBvcnQgU2Vzc2lvbk1hbmFnZXIgZnJvbSAnLi9TZXNzaW9uTWFuYWdlcic7XG5pbXBvcnQgQmFuTWFuYWdlciBmcm9tICcuL2Jhbi9CYW5NYW5hZ2VyJztcbmltcG9ydCBCbG9ja01hbmFnZXIgZnJvbSAnLi9ibG9jay9CbG9ja01hbmFnZXInO1xuaW1wb3J0IHsgQmxvY2tNYXBwaW5ncyB9IGZyb20gJy4vYmxvY2svQmxvY2tNYXBwaW5ncyc7XG5pbXBvcnQgeyBDaGF0TWFuYWdlciB9IGZyb20gJy4vY2hhdC9DaGF0TWFuYWdlcic7XG5pbXBvcnQgeyBDb21tYW5kTWFuYWdlciB9IGZyb20gJy4vY29tbWFuZC9Db21tYW5kTWFuYWdlcic7XG5pbXBvcnQgeyBFdmVudEVtaXR0ZXIgfSBmcm9tICcuL2V2ZW50cy9FdmVudEVtaXR0ZXInO1xuaW1wb3J0IHsgVGlja0V2ZW50IH0gZnJvbSAnLi9ldmVudHMvRXZlbnRzJztcbmltcG9ydCBSYWtuZXRDb25uZWN0RXZlbnQgZnJvbSAnLi9ldmVudHMvcmFrbmV0L1Jha25ldENvbm5lY3RFdmVudCc7XG5pbXBvcnQgUmFrbmV0RGlzY29ubmVjdEV2ZW50IGZyb20gJy4vZXZlbnRzL3Jha25ldC9SYWtuZXREaXNjb25uZWN0RXZlbnQnO1xuaW1wb3J0IFJha25ldEVuY2Fwc3VsYXRlZFBhY2tldEV2ZW50IGZyb20gJy4vZXZlbnRzL3Jha25ldC9SYWtuZXRFbmNhcHN1bGF0ZWRQYWNrZXRFdmVudCc7XG5pbXBvcnQgSXRlbU1hbmFnZXIgZnJvbSAnLi9pdGVtL0l0ZW1NYW5hZ2VyJztcbmltcG9ydCBDbGllbnRDb25uZWN0aW9uIGZyb20gJy4vbmV0d29yay9DbGllbnRDb25uZWN0aW9uJztcbmltcG9ydCBJZGVudGlmaWVycyBmcm9tICcuL25ldHdvcmsvSWRlbnRpZmllcnMnO1xuaW1wb3J0IFBhY2tldFJlZ2lzdHJ5IGZyb20gJy4vbmV0d29yay9QYWNrZXRSZWdpc3RyeSc7XG5pbXBvcnQgdHlwZSB7IERhdGFQYWNrZXQgfSBmcm9tICcuL25ldHdvcmsvUGFja2V0cyc7XG5pbXBvcnQgQmF0Y2hQYWNrZXQgZnJvbSAnLi9uZXR3b3JrL3BhY2tldC9CYXRjaFBhY2tldCc7XG5pbXBvcnQgeyBQZXJtaXNzaW9uTWFuYWdlciB9IGZyb20gJy4vcGVybWlzc2lvbi9QZXJtaXNzaW9uTWFuYWdlcic7XG5pbXBvcnQgeyBRdWVyeU1hbmFnZXIgfSBmcm9tICcuL3F1ZXJ5L1F1ZXJ5TWFuYWdlcic7XG5pbXBvcnQgVGltZXIgZnJvbSAnLi91dGlscy9UaW1lcic7XG5pbXBvcnQgV29ybGRNYW5hZ2VyIGZyb20gJy4vd29ybGQvV29ybGRNYW5hZ2VyJztcblxuaW1wb3J0IHR5cGUgeyBJbmV0QWRkcmVzcywgUmFrTmV0U2Vzc2lvbiB9IGZyb20gJ0Bqc3ByaXNtYXJpbmUvcmFrbmV0JztcbmltcG9ydCB0eXBlIHsgQ29uZmlnIH0gZnJvbSAnLi9jb25maWcvQ29uZmlnJztcblxuaW1wb3J0IHR5cGUgeyBMb2dnZXIgfSBmcm9tICdAanNwcmlzbWFyaW5lL2xvZ2dlcic7XG5pbXBvcnQgeyB2ZXJzaW9uIH0gZnJvbSAnLi4vcGFja2FnZS5qc29uJyB3aXRoIHsgdHlwZTogJ2pzb24nIH07XG5cbi8qKlxuICogSlNQcmlzbWFyaW5lJ3MgbWFpbiBzZXJ2ZXIgY2xhc3MuXG4gKiBAcHVibGljXG4gKi9cbmV4cG9ydCBkZWZhdWx0IGNsYXNzIFNlcnZlciBleHRlbmRzIEV2ZW50RW1pdHRlciB7XG4gICAgcHJpdmF0ZSByYWtuZXQ6IFJha05ldExpc3RlbmVyIHwgdW5kZWZpbmVkO1xuICAgIHByaXZhdGUgcmVhZG9ubHkgbG9nZ2VyOiBMb2dnZXI7XG4gICAgcHJpdmF0ZSByZWFkb25seSBjb25maWc6IENvbmZpZztcbiAgICBwcml2YXRlIHJlYWRvbmx5IGNvbnNvbGU6IENvbnNvbGUgfCB1bmRlZmluZWQ7XG4gICAgcHJpdmF0ZSByZWFkb25seSBwYWNrZXRSZWdpc3RyeTogUGFja2V0UmVnaXN0cnk7XG4gICAgcHJpdmF0ZSByZWFkb25seSBzZXNzaW9uTWFuYWdlciA9IG5ldyBTZXNzaW9uTWFuYWdlcigpO1xuICAgIHByaXZhdGUgcmVhZG9ubHkgY29tbWFuZE1hbmFnZXI6IENvbW1hbmRNYW5hZ2VyO1xuICAgIHByaXZhdGUgcmVhZG9ubHkgd29ybGRNYW5hZ2VyOiBXb3JsZE1hbmFnZXI7XG4gICAgcHJpdmF0ZSByZWFkb25seSBpdGVtTWFuYWdlcjogSXRlbU1hbmFnZXI7XG4gICAgcHJpdmF0ZSByZWFkb25seSBibG9ja01hbmFnZXI6IEJsb2NrTWFuYWdlcjtcbiAgICBwcml2YXRlIHJlYWRvbmx5IHF1ZXJ5TWFuYWdlcjogUXVlcnlNYW5hZ2VyO1xuICAgIHByaXZhdGUgcmVhZG9ubHkgY2hhdE1hbmFnZXI6IENoYXRNYW5hZ2VyO1xuICAgIHByaXZhdGUgcmVhZG9ubHkgcGVybWlzc2lvbk1hbmFnZXI6IFBlcm1pc3Npb25NYW5hZ2VyO1xuICAgIHByaXZhdGUgcmVhZG9ubHkgYmFuTWFuYWdlcjogQmFuTWFuYWdlcjtcblxuICAgIC8qKlxuICAgICAqIElmIHRoZSBzZXJ2ZXIgaXMgc3RvcHBpbmcuXG4gICAgICogQGludGVybmFsXG4gICAgICovXG4gICAgcHJpdmF0ZSBzdG9wcGluZyA9IGZhbHNlO1xuXG4gICAgLyoqXG4gICAgICogVGhlIGN1cnJlbnQgdGlja2VyIHRpbWVyLlxuICAgICAqIEBpbnRlcm5hbFxuICAgICAqL1xuICAgIHByaXZhdGUgdGlja2VyVGltZXI6IE5vZGVKUy5UaW1lb3V0IHwgdW5kZWZpbmVkO1xuXG4gICAgLyoqXG4gICAgICogVGhlIGN1cnJlbnQgVFBTLlxuICAgICAqIEBpbnRlcm5hbFxuICAgICAqL1xuICAgIHByaXZhdGUgdHBzID0gMjA7XG5cbiAgICAvKipcbiAgICAgKiBUaGUgY3VycmVudCB0aWNrLlxuICAgICAqIEBpbnRlcm5hbFxuICAgICAqL1xuICAgIHByaXZhdGUgY3VycmVudFRpY2sgPSAwbjtcblxuICAgIC8qKlxuICAgICAqIElmIHRoZSBzZXJ2ZXIgaXMgaGVhZGxlc3MuXG4gICAgICogQGludGVybmFsXG4gICAgICovXG4gICAgcHJpdmF0ZSByZWFkb25seSBoZWFkbGVzczogYm9vbGVhbjtcblxuICAgIC8vIFRPRE86IE1vdmUgdGhpcyBzb21ld2hlcmUgZWxzZS5cbiAgICBwcml2YXRlIHN0YXRpYyByZWFkb25seSBNSU5FQ1JBRlRfVElDS19USU1FX01TID0gMTAwMCAvIDIwO1xuXG4gICAgLyoqXG4gICAgICogQ3JlYXRlcyBhIG5ldyBzZXJ2ZXIgaW5zdGFuY2UuXG4gICAgICogQHBhcmFtIHtvYmplY3R9IG9wdGlvbnMgLSBUaGUgb3B0aW9ucy5cbiAgICAgKiBAcGFyYW0ge0xvZ2dlckJ1aWxkZXJ9IG9wdGlvbnMubG9nZ2VyIC0gVGhlIGxvZ2dlci5cbiAgICAgKiBAcGFyYW0ge0NvbmZpZ30gb3B0aW9ucy5jb25maWcgLSBUaGUgY29uZmlnLlxuICAgICAqIEByZXR1cm5zIHtTZXJ2ZXJ9IFRoZSBzZXJ2ZXIgaW5zdGFuY2UuXG4gICAgICovXG4gICAgcHVibGljIGNvbnN0cnVjdG9yKHsgbG9nZ2VyLCBjb25maWcsIGhlYWRsZXNzID0gZmFsc2UgfTogeyBsb2dnZXI6IExvZ2dlcjsgY29uZmlnOiBDb25maWc7IGhlYWRsZXNzPzogYm9vbGVhbiB9KSB7XG4gICAgICAgIHN1cGVyKCk7XG5cbiAgICAgICAgdGhpcy5oZWFkbGVzcyA9IGhlYWRsZXNzO1xuXG4gICAgICAgIGxvZ2dlci5pbmZvKFxuICAgICAgICAgICAgYFN0YXJ0aW5nIEpTUHJpc21hcmluZSBzZXJ2ZXIgdmVyc2lvbiDCp2V2JHt2ZXJzaW9ufcKnciBmb3IgTWluZWNyYWZ0OiBCZWRyb2NrIEVkaXRpb24gJHtJZGVudGlmaWVycy5NaW5lY3JhZnRWZXJzaW9ucy5hdCgtMSl9IChwcm90b2NvbCB2ZXJzaW9uIMKnZSR7SWRlbnRpZmllcnMuUHJvdG9jb2x9wqdyKWBcbiAgICAgICAgKTtcblxuICAgICAgICB0aGlzLmxvZ2dlciA9IGxvZ2dlcjtcbiAgICAgICAgdGhpcy5jb25maWcgPSBjb25maWc7XG4gICAgICAgIHRoaXMucGFja2V0UmVnaXN0cnkgPSBuZXcgUGFja2V0UmVnaXN0cnkodGhpcyk7XG4gICAgICAgIHRoaXMuaXRlbU1hbmFnZXIgPSBuZXcgSXRlbU1hbmFnZXIodGhpcyk7XG4gICAgICAgIHRoaXMuYmxvY2tNYW5hZ2VyID0gbmV3IEJsb2NrTWFuYWdlcih0aGlzKTtcbiAgICAgICAgdGhpcy53b3JsZE1hbmFnZXIgPSBuZXcgV29ybGRNYW5hZ2VyKHRoaXMpO1xuICAgICAgICBpZiAoIXRoaXMuaGVhZGxlc3MpIHRoaXMuY29uc29sZSA9IG5ldyBDb25zb2xlKHRoaXMpO1xuICAgICAgICB0aGlzLmNvbW1hbmRNYW5hZ2VyID0gbmV3IENvbW1hbmRNYW5hZ2VyKHRoaXMpO1xuICAgICAgICB0aGlzLnF1ZXJ5TWFuYWdlciA9IG5ldyBRdWVyeU1hbmFnZXIodGhpcyk7XG4gICAgICAgIHRoaXMuY2hhdE1hbmFnZXIgPSBuZXcgQ2hhdE1hbmFnZXIodGhpcyk7XG4gICAgICAgIHRoaXMucGVybWlzc2lvbk1hbmFnZXIgPSBuZXcgUGVybWlzc2lvbk1hbmFnZXIodGhpcyk7XG4gICAgICAgIHRoaXMuYmFuTWFuYWdlciA9IG5ldyBCYW5NYW5hZ2VyKHRoaXMpO1xuICAgIH1cblxuICAgIC8qKlxuICAgICAqIEVuYWJsZXMgdGhlIHNlcnZlci5cbiAgICAgKiBAcmV0dXJucyB7UHJvbWlzZTx2b2lkPn0gQSBwcm9taXNlIHRoYXQgcmVzb2x2ZXMgd2hlbiB0aGUgc2VydmVyIGlzIGVuYWJsZWQuXG4gICAgICogQGludGVybmFsXG4gICAgICovXG4gICAgcHJpdmF0ZSBhc3luYyBlbmFibGUoKTogUHJvbWlzZTx2b2lkPiB7XG4gICAgICAgIGF3YWl0IEJsb2NrTWFwcGluZ3MuaW5pdE1hcHBpbmdzKHRoaXMpO1xuXG4gICAgICAgIGF3YWl0IHRoaXMuY29uZmlnLmVuYWJsZSgpO1xuICAgICAgICBhd2FpdCB0aGlzLmNvbnNvbGU/LmVuYWJsZSgpO1xuICAgICAgICBhd2FpdCB0aGlzLmxvZ2dlci5lbmFibGUoKTtcbiAgICAgICAgYXdhaXQgdGhpcy5wZXJtaXNzaW9uTWFuYWdlci5lbmFibGUoKTtcbiAgICAgICAgYXdhaXQgdGhpcy5wYWNrZXRSZWdpc3RyeS5lbmFibGUoKTtcbiAgICAgICAgYXdhaXQgdGhpcy5pdGVtTWFuYWdlci5lbmFibGUoKTtcbiAgICAgICAgYXdhaXQgdGhpcy5ibG9ja01hbmFnZXIuZW5hYmxlKCk7XG4gICAgICAgIGF3YWl0IHRoaXMuYmFuTWFuYWdlci5lbmFibGUoKTtcbiAgICAgICAgYXdhaXQgdGhpcy5jb21tYW5kTWFuYWdlci5lbmFibGUoKTtcbiAgICAgICAgYXdhaXQgdGhpcy53b3JsZE1hbmFnZXIuZW5hYmxlKCk7XG5cbiAgICAgICAgdGhpcy5sb2dnZXIuc2V0Q29uc29sZSh0aGlzLmNvbnNvbGUpO1xuICAgIH1cblxuICAgIC8qKlxuICAgICAqIERpc2FibGVzIHRoZSBzZXJ2ZXIuXG4gICAgICogQHJldHVybnMge1Byb21pc2U8dm9pZD59IEEgcHJvbWlzZSB0aGF0IHJlc29sdmVzIHdoZW4gdGhlIHNlcnZlciBpcyBkaXNhYmxlZC5cbiAgICAgKiBAaW50ZXJuYWxcbiAgICAgKi9cbiAgICBwcml2YXRlIGFzeW5jIGRpc2FibGUoKTogUHJvbWlzZTx2b2lkPiB7XG4gICAgICAgIGF3YWl0IHRoaXMud29ybGRNYW5hZ2VyLmRpc2FibGUoKTtcbiAgICAgICAgYXdhaXQgdGhpcy5jb21tYW5kTWFuYWdlci5kaXNhYmxlKCk7XG4gICAgICAgIGF3YWl0IHRoaXMuYmFuTWFuYWdlci5kaXNhYmxlKCk7XG4gICAgICAgIGF3YWl0IHRoaXMuYmxvY2tNYW5hZ2VyLmRpc2FibGUoKTtcbiAgICAgICAgYXdhaXQgdGhpcy5pdGVtTWFuYWdlci5kaXNhYmxlKCk7XG4gICAgICAgIGF3YWl0IHRoaXMucGVybWlzc2lvbk1hbmFnZXIuZGlzYWJsZSgpO1xuICAgICAgICBhd2FpdCB0aGlzLnBhY2tldFJlZ2lzdHJ5LmRpc2FibGUoKTtcbiAgICAgICAgYXdhaXQgdGhpcy5jb25maWcuZGlzYWJsZSgpO1xuICAgICAgICBhd2FpdCB0aGlzLmxvZ2dlci5kaXNhYmxlKCk7XG5cbiAgICAgICAgQmxvY2tNYXBwaW5ncy5yZXNldCgpO1xuICAgIH1cblxuICAgIHB1YmxpYyBnZXRNZXRhZGF0YSgpIHtcbiAgICAgICAgaWYgKCF0aGlzLnJha25ldCkgdGhyb3cgbmV3IEVycm9yKCdTZXJ2ZXIgaXMgbm90IHN0YXJ0ZWQnKTtcbiAgICAgICAgcmV0dXJuIHRoaXMucmFrbmV0LnNlcnZlck5hbWU7XG4gICAgfVxuXG4gICAgLyoqXG4gICAgICogUmVsb2FkcyB0aGUgc2VydmVyLlxuICAgICAqIEByZXR1cm5zIHtQcm9taXNlPHZvaWQ+fSBBIHByb21pc2UgdGhhdCByZXNvbHZlcyB3aGVuIHRoZSBzZXJ2ZXIgaXMgcmVsb2FkZWQuXG4gICAgICogQHJlbWFya3MgVGhpcyBtZXRob2QgaXMgZXF1aXZhbGVudCB0byBjYWxsaW5nIHtAbGluayBTZXJ2ZXIjZGlzYWJsZX0gYW5kIHtAbGluayBTZXJ2ZXIjZW5hYmxlfS5cbiAgICAgKiBAcmVtYXJrcyBUaGlzIG1ldGhvZCBhbmQgZnVuY3Rpb25hbGl0eSBpcyB1bnN1cHBvcnRlZCBhbmQgc2hvdWxkIGlkZWFsbHkgYmUgY29tcGxldGVseSBhdm9pZGVkLlxuICAgICAqL1xuICAgIHB1YmxpYyBhc3luYyByZWxvYWQoKTogUHJvbWlzZTx2b2lkPiB7XG4gICAgICAgIGF3YWl0IHRoaXMuZGlzYWJsZSgpO1xuICAgICAgICBhd2FpdCB0aGlzLmVuYWJsZSgpO1xuICAgIH1cblxuICAgIC8qKlxuICAgICAqIFN0YXJ0cyB0aGUgc2VydmVyLlxuICAgICAqIEBwYXJhbSB7c3RyaW5nfSBbc2VydmVySXA9JzAuMC4wLjAnXSAtIFRoZSBzZXJ2ZXIgSVAuXG4gICAgICogQHBhcmFtIHtudW1iZXJ9IFtwb3J0PTE5MTMyXSAtIFRoZSBzZXJ2ZXIgcG9ydC5cbiAgICAgKiBAcmV0dXJucyB7UHJvbWlzZTx2b2lkPn0gQSBwcm9taXNlIHRoYXQgcmVzb2x2ZXMgd2hlbiB0aGUgc2VydmVyIGlzIHN0YXJ0ZWQuXG4gICAgICovXG4gICAgcHVibGljIGFzeW5jIGJvb3RzdHJhcChzZXJ2ZXJJcCA9ICcwLjAuMC4wJywgcG9ydCA9IDE5MTMyKTogUHJvbWlzZTx2b2lkPiB7XG4gICAgICAgIGF3YWl0IHRoaXMuZW5hYmxlKCk7XG5cbiAgICAgICAgdGhpcy5yYWtuZXQgPSBuZXcgUmFrTmV0TGlzdGVuZXIoXG4gICAgICAgICAgICB0aGlzLmdldENvbmZpZygpLmdldE1heFBsYXllcnMoKSxcbiAgICAgICAgICAgIHRoaXMuZ2V0Q29uZmlnKCkuZ2V0T25saW5lTW9kZSgpLFxuICAgICAgICAgICAgbmV3IFNlcnZlck5hbWUodGhpcyksXG4gICAgICAgICAgICB0aGlzLmdldExvZ2dlcigpXG4gICAgICAgICk7XG4gICAgICAgIHRoaXMucmFrbmV0LnN0YXJ0KHNlcnZlcklwLCBwb3J0KTtcblxuICAgICAgICB0aGlzLnJha25ldC5vbignb3BlbkNvbm5lY3Rpb24nLCBhc3luYyAoc2Vzc2lvbjogUmFrTmV0U2Vzc2lvbikgPT4ge1xuICAgICAgICAgICAgY29uc3QgZXZlbnQgPSBuZXcgUmFrbmV0Q29ubmVjdEV2ZW50KHNlc3Npb24pO1xuICAgICAgICAgICAgYXdhaXQgdGhpcy5lbWl0KCdyYWtuZXRDb25uZWN0JywgZXZlbnQpO1xuXG4gICAgICAgICAgICBpZiAoZXZlbnQuaXNDYW5jZWxsZWQoKSkge1xuICAgICAgICAgICAgICAgIHNlc3Npb24uZGlzY29ubmVjdCgpO1xuICAgICAgICAgICAgICAgIHJldHVybjtcbiAgICAgICAgICAgIH1cblxuICAgICAgICAgICAgY29uc3QgdG9rZW4gPSBzZXNzaW9uLmdldEFkZHJlc3MoKS50b1Rva2VuKCk7XG4gICAgICAgICAgICBpZiAodGhpcy5zZXNzaW9uTWFuYWdlci5oYXModG9rZW4pKSB7XG4gICAgICAgICAgICAgICAgdGhpcy5sb2dnZXIuZXJyb3IoYEFub3RoZXIgY2xpZW50IHdpdGggdG9rZW4gKCR7dG9rZW59KSBpcyBhbHJlYWR5IGNvbm5lY3RlZCFgKTtcbiAgICAgICAgICAgICAgICBzZXNzaW9uLmRpc2Nvbm5lY3QoJ0FscmVhZHkgY29ubmVjdGVkIGZyb20gYW5vdGhlciBsb2NhdGlvbicpO1xuICAgICAgICAgICAgICAgIHJldHVybjtcbiAgICAgICAgICAgIH1cblxuICAgICAgICAgICAgY29uc3QgdGltZXIgPSBuZXcgVGltZXIoKTtcbiAgICAgICAgICAgIHRoaXMubG9nZ2VyLmRlYnVnKGAke3Rva2VufSBpcyBhdHRlbXB0aW5nIHRvIGNvbm5lY3RgKTtcbiAgICAgICAgICAgIHRoaXMuc2Vzc2lvbk1hbmFnZXIuYWRkKHRva2VuLCBuZXcgQ2xpZW50Q29ubmVjdGlvbihzZXNzaW9uLCB0aGlzLmxvZ2dlcikpO1xuICAgICAgICAgICAgdGhpcy5sb2dnZXIudmVyYm9zZShgTmV3IGNvbm5lY3Rpb24gaGFuZGxpbmcgdG9vayDCp2Uke3RpbWVyLnN0b3AoKX0gbXPCp3JgKTtcbiAgICAgICAgfSk7XG5cbiAgICAgICAgdGhpcy5yYWtuZXQub24oJ2Nsb3NlQ29ubmVjdGlvbicsIGFzeW5jIChpbmV0QWRkcjogSW5ldEFkZHJlc3MsIHJlYXNvbjogc3RyaW5nKSA9PiB7XG4gICAgICAgICAgICBjb25zdCBldmVudCA9IG5ldyBSYWtuZXREaXNjb25uZWN0RXZlbnQoaW5ldEFkZHIsIHJlYXNvbik7XG4gICAgICAgICAgICBhd2FpdCB0aGlzLmVtaXQoJ3Jha25ldERpc2Nvbm5lY3QnLCBldmVudCk7XG5cbiAgICAgICAgICAgIGNvbnN0IHRpbWUgPSBEYXRlLm5vdygpO1xuICAgICAgICAgICAgY29uc3QgdG9rZW4gPSBpbmV0QWRkci50b1Rva2VuKCk7XG5cbiAgICAgICAgICAgIGNvbnN0IHNlc3Npb24gPSB0aGlzLnNlc3Npb25NYW5hZ2VyLmdldCh0b2tlbik7XG4gICAgICAgICAgICBpZiAoIXNlc3Npb24pIHtcbiAgICAgICAgICAgICAgICB0aGlzLmxvZ2dlci5kZWJ1ZyhgQ2Fubm90IHJlbW92ZSBjb25uZWN0aW9uIGZyb20gbm9uLWV4aXN0aW5nIHBsYXllciAoJHt0b2tlbn0pYCk7XG4gICAgICAgICAgICAgICAgcmV0dXJuO1xuICAgICAgICAgICAgfVxuXG4gICAgICAgICAgICBhd2FpdCBzZXNzaW9uLmNsb3NlUGxheWVyU2Vzc2lvbigpO1xuXG4gICAgICAgICAgICB0aGlzLnNlc3Npb25NYW5hZ2VyLnJlbW92ZSh0b2tlbik7XG4gICAgICAgICAgICB0aGlzLmxvZ2dlci5kZWJ1ZyhgJHt0b2tlbn0gZGlzY29ubmVjdGVkIGR1ZSB0byAke3JlYXNvbn1gKTtcbiAgICAgICAgICAgIHRoaXMubG9nZ2VyLmRlYnVnKGBQbGF5ZXIgZGVzdHJ1Y3Rpb24gdG9vayBhYm91dCAke0RhdGUubm93KCkgLSB0aW1lfSBtc2ApO1xuICAgICAgICB9KTtcblxuICAgICAgICB0aGlzLnJha25ldC5vbignZW5jYXBzdWxhdGVkJywgYXN5bmMgKHBhY2tldDogYW55LCBpbmV0QWRkcjogSW5ldEFkZHJlc3MpID0+IHtcbiAgICAgICAgICAgIGNvbnN0IGV2ZW50ID0gbmV3IFJha25ldEVuY2Fwc3VsYXRlZFBhY2tldEV2ZW50KGluZXRBZGRyLCBwYWNrZXQpO1xuICAgICAgICAgICAgYXdhaXQgdGhpcy5lbWl0KCdyYWtuZXRFbmNhcHN1bGF0ZWRQYWNrZXQnLCBldmVudCk7XG5cbiAgICAgICAgICAgIGxldCBjb25uZWN0aW9uOiBDbGllbnRDb25uZWN0aW9uIHwgbnVsbDtcbiAgICAgICAgICAgIGlmICgoY29ubmVjdGlvbiA9IHRoaXMuc2Vzc2lvbk1hbmFnZXIuZ2V0KGluZXRBZGRyLnRvVG9rZW4oKSkgPz8gbnVsbCkgPT09IG51bGwpIHtcbiAgICAgICAgICAgICAgICB0aGlzLmxvZ2dlci5lcnJvcihgR290IGEgcGFja2V0IGZyb20gYSBjbG9zZWQgY29ubmVjdGlvbiAoJHtpbmV0QWRkci50b1Rva2VuKCl9KWApO1xuICAgICAgICAgICAgICAgIHJldHVybjtcbiAgICAgICAgICAgIH1cblxuICAgICAgICAgICAgdHJ5IHtcbiAgICAgICAgICAgICAgICAvLyBSZWFkIGJhdGNoIGNvbnRlbnQgYW5kIGhhbmRsZSB0aGVtXG4gICAgICAgICAgICAgICAgY29uc3QgYmF0Y2hlZCA9IG5ldyBCYXRjaFBhY2tldChwYWNrZXQuY29udGVudCk7XG4gICAgICAgICAgICAgICAgYmF0Y2hlZC5jb21wcmVzc2VkID0gY29ubmVjdGlvbi5oYXNDb21wcmVzc2lvbjtcblxuICAgICAgICAgICAgICAgIC8vIFJlYWQgYWxsIHBhY2tldHMgaW5zaWRlIGJhdGNoIGFuZCBoYW5kbGUgdGhlbVxuICAgICAgICAgICAgICAgIGZvciAoY29uc3QgYnVmIG9mIGF3YWl0IGJhdGNoZWQuYXN5bmNEZWNvZGUoKSkge1xuICAgICAgICAgICAgICAgICAgICBjb25zdCBwaWQgPSBidWZbMF0hO1xuXG4gICAgICAgICAgICAgICAgICAgIGlmICghdGhpcy5wYWNrZXRSZWdpc3RyeS5nZXRQYWNrZXRzKCkuaGFzKHBpZCkpIHtcbiAgICAgICAgICAgICAgICAgICAgICAgIHRoaXMubG9nZ2VyLndhcm4oYFBhY2tldCAweCR7cGlkLnRvU3RyaW5nKDE2KX0gaXNuJ3QgaW1wbGVtZW50ZWRgKTtcbiAgICAgICAgICAgICAgICAgICAgICAgIGNvbnRpbnVlO1xuICAgICAgICAgICAgICAgICAgICB9XG5cbiAgICAgICAgICAgICAgICAgICAgLy8gR2V0IHBhY2tldCBmcm9tIHJlZ2lzdHJ5XG4gICAgICAgICAgICAgICAgICAgIGNvbnN0IHBhY2tldCA9IG5ldyAodGhpcy5wYWNrZXRSZWdpc3RyeS5nZXRQYWNrZXRzKCkuZ2V0KHBpZCkhKShidWYpO1xuXG4gICAgICAgICAgICAgICAgICAgIHRyeSB7XG4gICAgICAgICAgICAgICAgICAgICAgICBwYWNrZXQuZGVjb2RlKCk7XG4gICAgICAgICAgICAgICAgICAgIH0gY2F0Y2ggKGVycm9yOiB1bmtub3duKSB7XG4gICAgICAgICAgICAgICAgICAgICAgICB0aGlzLmxvZ2dlci5lcnJvcihlcnJvcik7XG4gICAgICAgICAgICAgICAgICAgICAgICB0aGlzLmxvZ2dlci5lcnJvcihgRXJyb3Igd2hpbGUgZGVjb2RpbmcgcGFja2V0OiAke3BhY2tldC5jb25zdHJ1Y3Rvci5uYW1lfTogJHtlcnJvcn1gKTtcbiAgICAgICAgICAgICAgICAgICAgICAgIGNvbnRpbnVlO1xuICAgICAgICAgICAgICAgICAgICB9XG5cbiAgICAgICAgICAgICAgICAgICAgdHJ5IHtcbiAgICAgICAgICAgICAgICAgICAgICAgIGNvbnN0IGhhbmRsZXIgPSB0aGlzLnBhY2tldFJlZ2lzdHJ5LmdldEhhbmRsZXIocGlkKTtcbiAgICAgICAgICAgICAgICAgICAgICAgIHRoaXMubG9nZ2VyLnNpbGx5KGBSZWNlaXZlZCDCp2Ike3BhY2tldC5jb25zdHJ1Y3Rvci5uYW1lfcKnciBwYWNrZXRgKTtcbiAgICAgICAgICAgICAgICAgICAgICAgIGF3YWl0IChoYW5kbGVyIGFzIGFueSkuaGFuZGxlKHBhY2tldCwgdGhpcywgY29ubmVjdGlvbi5nZXRQbGF5ZXJTZXNzaW9uKCkgPz8gY29ubmVjdGlvbik7XG4gICAgICAgICAgICAgICAgICAgIH0gY2F0Y2ggKGVycm9yOiB1bmtub3duKSB7XG4gICAgICAgICAgICAgICAgICAgICAgICB0aGlzLmxvZ2dlci5lcnJvcihgSGFuZGxlciBlcnJvciAke3BhY2tldC5jb25zdHJ1Y3Rvci5uYW1lfS1oYW5kbGVyOiAoJHtlcnJvcn0pYCk7XG4gICAgICAgICAgICAgICAgICAgICAgICB0aGlzLmxvZ2dlci5lcnJvcihlcnJvcik7XG4gICAgICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICB9IGNhdGNoIChlcnJvcjogdW5rbm93bikge1xuICAgICAgICAgICAgICAgIHRoaXMubG9nZ2VyLmVycm9yKGVycm9yKTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgfSk7XG5cbiAgICAgICAgdGhpcy5yYWtuZXQub24oJ3JhdycsIGFzeW5jIChidWZmZXI6IEJ1ZmZlciwgaW5ldEFkZHI6IEluZXRBZGRyZXNzKSA9PiB7XG4gICAgICAgICAgICBpZiAoIXRoaXMuY29uZmlnLmdldEVuYWJsZVF1ZXJ5KCkpIHJldHVybjtcblxuICAgICAgICAgICAgdHJ5IHtcbiAgICAgICAgICAgICAgICBhd2FpdCB0aGlzLnF1ZXJ5TWFuYWdlci5vblJhdyhidWZmZXIsIGluZXRBZGRyKTtcbiAgICAgICAgICAgIH0gY2F0Y2ggKGVycm9yOiB1bmtub3duKSB7XG4gICAgICAgICAgICAgICAgdGhpcy5sb2dnZXIudmVyYm9zZShgUXVlcnlNYW5hZ2VyIGVuY291bnRlcmVkIGFuIGVycm9yYCk7XG4gICAgICAgICAgICAgICAgdGhpcy5sb2dnZXIuZXJyb3IoZXJyb3IpO1xuICAgICAgICAgICAgfVxuICAgICAgICB9KTtcblxuICAgICAgICBpZiAodGhpcy5jb25maWcuZ2V0RW5hYmxlVGlja2luZygpKSB7XG4gICAgICAgICAgICBsZXQgc3RhcnRUaW1lID0gRGF0ZS5ub3coKTtcbiAgICAgICAgICAgIGxldCB0cHNTdGFydFRpbWUgPSBEYXRlLm5vdygpO1xuICAgICAgICAgICAgbGV0IGxhc3RUaWNrVGltZSA9IERhdGUubm93KCk7XG4gICAgICAgICAgICBsZXQgdHBzU3RhcnRUaWNrID0gdGhpcy5nZXRUaWNrKCk7XG4gICAgICAgICAgICBjb25zdCB0aWNrID0gYXN5bmMgKCkgPT4ge1xuICAgICAgICAgICAgICAgIGlmICh0aGlzLnN0b3BwaW5nKSByZXR1cm47XG5cbiAgICAgICAgICAgICAgICBjb25zdCBldmVudCA9IG5ldyBUaWNrRXZlbnQodGhpcy5nZXRUaWNrKCkpO1xuICAgICAgICAgICAgICAgIHZvaWQgdGhpcy5lbWl0KCd0aWNrJywgZXZlbnQpO1xuXG4gICAgICAgICAgICAgICAgY29uc3QgdGlja3NQZXJTZWNvbmQgPSAxMDAwIC8gU2VydmVyLk1JTkVDUkFGVF9USUNLX1RJTUVfTVM7XG5cbiAgICAgICAgICAgICAgICAvLyBVcGRhdGUgYWxsIHdvcmxkcy5cbiAgICAgICAgICAgICAgICBhd2FpdCBQcm9taXNlLmFsbCh0aGlzLndvcmxkTWFuYWdlci5nZXRXb3JsZHMoKS5tYXAoKHdvcmxkKSA9PiB3b3JsZC51cGRhdGUoZXZlbnQuZ2V0VGljaygpKSkpO1xuXG4gICAgICAgICAgICAgICAgaWYgKHRoaXMuY29uZmlnLmdldEVuYWJsZVByb2Nlc3NUaXRsZSgpICYmIHRoaXMuZ2V0VGljaygpICUgdGlja3NQZXJTZWNvbmQgPT09IDAgJiYgIXRoaXMuaGVhZGxlc3MpIHtcbiAgICAgICAgICAgICAgICAgICAgLy8gVXBkYXRlIHRoZSBwcm9jZXNzIHRpdGxlIHdpdGggVFBTIGFuZCB0aWNrLlxuICAgICAgICAgICAgICAgICAgICBwcm9jZXNzLnRpdGxlID0gYFRQUzogJHt0aGlzLmdldFRQUygpLnRvRml4ZWQoMil9IHwgVGljazogJHt0aGlzLmdldFRpY2soKX0gfCAke3Byb2Nlc3MudGl0bGUuc3BsaXQoJ3wgJykuYXQoLTEpIX1gO1xuICAgICAgICAgICAgICAgIH1cblxuICAgICAgICAgICAgICAgIHRoaXMuY3VycmVudFRpY2srKztcbiAgICAgICAgICAgICAgICBjb25zdCBlbmRUaW1lID0gRGF0ZS5ub3coKTtcbiAgICAgICAgICAgICAgICBjb25zdCBlbGFwc2VkVGltZSA9IGVuZFRpbWUgLSBzdGFydFRpbWU7XG4gICAgICAgICAgICAgICAgY29uc3QgZXhwZWN0ZWRFbGFwc2VkVGltZSA9IHRoaXMuZ2V0VGljaygpICogU2VydmVyLk1JTkVDUkFGVF9USUNLX1RJTUVfTVM7XG4gICAgICAgICAgICAgICAgY29uc3QgZXhlY3V0aW9uVGltZSA9IGVuZFRpbWUgLSBsYXN0VGlja1RpbWU7XG5cbiAgICAgICAgICAgICAgICAvLyBBZGp1c3Qgc2xlZXBUaW1lIGJhc2VkIG9uIGV4ZWN1dGlvbiBzcGVlZC5cbiAgICAgICAgICAgICAgICBsZXQgc2xlZXBUaW1lID0gU2VydmVyLk1JTkVDUkFGVF9USUNLX1RJTUVfTVMgLSBleGVjdXRpb25UaW1lO1xuICAgICAgICAgICAgICAgIGlmIChlbGFwc2VkVGltZSA8IGV4cGVjdGVkRWxhcHNlZFRpbWUpIHtcbiAgICAgICAgICAgICAgICAgICAgLy8gSWYgd2UncmUgcnVubmluZyBmYXN0ZXIgdGhhbiBleHBlY3RlZCwgaW5jcmVhc2Ugc2xlZXBUaW1lLlxuICAgICAgICAgICAgICAgICAgICBzbGVlcFRpbWUgKz0gZXhwZWN0ZWRFbGFwc2VkVGltZSAtIGVsYXBzZWRUaW1lO1xuICAgICAgICAgICAgICAgIH0gZWxzZSBpZiAoZWxhcHNlZFRpbWUgPiBleHBlY3RlZEVsYXBzZWRUaW1lKSB7XG4gICAgICAgICAgICAgICAgICAgIC8vIElmIHdlJ3JlIHJ1bm5pbmcgc2xvd2VyIHRoYW4gZXhwZWN0ZWQsIGRlY3JlYXNlIHNsZWVwVGltZSBidXQgZG9uJ3QgbGV0IGl0IGdvIGJlbG93IDAuXG4gICAgICAgICAgICAgICAgICAgIHNsZWVwVGltZSA9IE1hdGgubWF4KDAsIHNsZWVwVGltZSAtIChlbGFwc2VkVGltZSAtIGV4cGVjdGVkRWxhcHNlZFRpbWUpKTtcbiAgICAgICAgICAgICAgICB9XG5cbiAgICAgICAgICAgICAgICAvLyBDYWxjdWxhdGUgdHBzIGJhc2VkIG9uIHRoZSBhY3R1YWwgZWxhcHNlZCB0aW1lIHNpbmNlIHRoZSBzdGFydCBvZiB0aGUgdGljay5cbiAgICAgICAgICAgICAgICBpZiAodHBzU3RhcnRUaW1lICE9PSBlbmRUaW1lKSB7XG4gICAgICAgICAgICAgICAgICAgIHRoaXMudHBzID0gKCh0aGlzLmdldFRpY2soKSAtIHRwc1N0YXJ0VGljaykgKiAxMDAwKSAvIChlbmRUaW1lIC0gdHBzU3RhcnRUaW1lKTtcbiAgICAgICAgICAgICAgICB9XG5cbiAgICAgICAgICAgICAgICBpZiAoZW5kVGltZSAtIHRwc1N0YXJ0VGltZSA+PSAxMDAwKSB7XG4gICAgICAgICAgICAgICAgICAgIHRwc1N0YXJ0VGljayA9IHRoaXMuZ2V0VGljaygpO1xuICAgICAgICAgICAgICAgICAgICB0cHNTdGFydFRpbWUgPSBlbmRUaW1lO1xuICAgICAgICAgICAgICAgIH1cblxuICAgICAgICAgICAgICAgIHRoaXMudHBzID0gTWF0aC5taW4odGhpcy50cHMsIDIwKTsgLy8gRW5zdXJlIHRwcyBkb2VzIG5vdCBleGNlZWQgMjBcblxuICAgICAgICAgICAgICAgIGxhc3RUaWNrVGltZSA9IGVuZFRpbWU7XG4gICAgICAgICAgICAgICAgdGhpcy50aWNrZXJUaW1lciA9IHNldFRpbWVvdXQodGljaywgTWF0aC5tYXgoMCwgc2xlZXBUaW1lKSk7XG4gICAgICAgICAgICAgICAgdGhpcy50aWNrZXJUaW1lci51bnJlZigpO1xuICAgICAgICAgICAgfTtcblxuICAgICAgICAgICAgLy8gU3RhcnQgdGlja2luZ1xuICAgICAgICAgICAgdm9pZCB0aWNrKCk7XG4gICAgICAgIH1cblxuICAgICAgICB0aGlzLmxvZ2dlci5pbmZvKGBKU1ByaXNtYXJpbmUgaXMgbm93IGxpc3RlbmluZyBvbiBwb3J0IMKnYiR7cG9ydH1gKTtcbiAgICB9XG5cbiAgICAvKipcbiAgICAgKiBLaWxscyB0aGUgc2VydmVyIGFzeW5jaHJvbm91c2x5LlxuICAgICAqIEBwYXJhbSB7b2JqZWN0fSBbb3B0aW9uc10gLSBUaGUgb3B0aW9ucy5cbiAgICAgKiBAcGFyYW0ge2Jvb2xlYW59IFtvcHRpb25zLmNyYXNoXSAtIElmIHRoZSBzZXJ2ZXIgc2hvdWxkIGNyYXNoLlxuICAgICAqIEBwYXJhbSB7Ym9vbGVhbn0gW29wdGlvbnMuc3RheUFsaXZlXSAtIElmIHdlIHNob3VsZCBsZXQgdGhlIHByb2Nlc3Mgc3RheSBhbGl2ZS5cbiAgICAgKiBAcmV0dXJucyB7UHJvbWlzZTx2b2lkPn0gQSBwcm9taXNlIHRoYXQgcmVzb2x2ZXMgd2hlbiB0aGUgc2VydmVyIGlzIGtpbGxlZC5cbiAgICAgKi9cbiAgICBwdWJsaWMgYXN5bmMgc2h1dGRvd24ob3B0aW9ucz86IHsgY3Jhc2g/OiBib29sZWFuOyBzdGF5QWxpdmU/OiBib29sZWFuIH0pOiBQcm9taXNlPHZvaWQ+IHtcbiAgICAgICAgaWYgKHRoaXMuc3RvcHBpbmcpIHJldHVybjtcbiAgICAgICAgdGhpcy5zdG9wcGluZyA9IHRydWU7XG5cbiAgICAgICAgdGhpcy5sb2dnZXIuaW5mbygnU3RvcHBpbmcgc2VydmVyJywgJ1NlcnZlci9raWxsJyk7XG4gICAgICAgIGF3YWl0IHRoaXMuY29uc29sZT8uZGlzYWJsZSgpO1xuXG4gICAgICAgIGNsZWFySW50ZXJ2YWwodGhpcy50aWNrZXJUaW1lcik7XG5cbiAgICAgICAgdHJ5IHtcbiAgICAgICAgICAgIC8vIEtpY2sgYWxsIG9ubGluZSBwbGF5ZXJzLlxuICAgICAgICAgICAgYXdhaXQgdGhpcy5zZXNzaW9uTWFuYWdlci5raWNrQWxsUGxheWVycygnU2VydmVyIGNsb3NlZC4nKTtcblxuICAgICAgICAgICAgLy8gRGlzYWJsZSBhbGwgbWFuYWdlcnMuXG4gICAgICAgICAgICBhd2FpdCB0aGlzLmRpc2FibGUoKTtcblxuICAgICAgICAgICAgLy8gYHRoaXMucmFrbmV0YCBtaWdodCBiZSB1bmRlZmluZWQgaWYgd2Uga2lsbCB0aGUgc2VydmVyIHJlYWxseSBlYXJseS5cbiAgICAgICAgICAgIHRoaXMucmFrbmV0Py5raWxsKCk7XG5cbiAgICAgICAgICAgIC8vIEZpbmFsbHksIHJlbW92ZSBhbGwgbGlzdGVuZXJzLlxuICAgICAgICAgICAgdGhpcy5yZW1vdmVBbGxMaXN0ZW5lcnMoKTtcblxuICAgICAgICAgICAgLy8gTG9nZ2VyIGlzIG5vIGxvbmdlciBhdmFpbGFibGUuXG4gICAgICAgICAgICBjb25zb2xlLmRlYnVnKCdTZXJ2ZXIgc3RvcHBlZCwgR29vZGJ5ZSFcXG4nKTtcblxuICAgICAgICAgICAgaWYgKCFvcHRpb25zPy5zdGF5QWxpdmUpIHByb2Nlc3MuZXhpdChvcHRpb25zPy5jcmFzaCA/IDEgOiAwKTtcbiAgICAgICAgfSBjYXRjaCAoZXJyb3I6IHVua25vd24pIHtcbiAgICAgICAgICAgIGNvbnNvbGUuZXJyb3IoZXJyb3IpO1xuICAgICAgICAgICAgaWYgKCFvcHRpb25zPy5zdGF5QWxpdmUpIHByb2Nlc3MuZXhpdCgxKTtcbiAgICAgICAgfVxuICAgIH1cblxuICAgIHB1YmxpYyBhc3luYyBicm9hZGNhc3RQYWNrZXQ8VCBleHRlbmRzIERhdGFQYWNrZXQ+KGRhdGFQYWNrZXQ6IFQpOiBQcm9taXNlPHZvaWQ+IHtcbiAgICAgICAgLy8gTWF5YmUgaSBjYW4gaW1wcm92ZSB0aGlzIGJ5IHVzaW5nIHRoZSBVRFAgYnJvYWRjYXN0LCBhbGwgdW5jb25uZWN0ZWQgY2xpZW50c1xuICAgICAgICAvLyB3aWxsIGlnbm9yZSB0aGUgY29ubmVjdGVkIHBhY2tldCBwcm9iYWJseSwgYnV0IG1heSBjYXVzZSBpc3N1ZXMuXG4gICAgICAgIGZvciAoY29uc3Qgb25saW5lUGxheWVyIG9mIHRoaXMuc2Vzc2lvbk1hbmFnZXIuZ2V0QWxsUGxheWVycygpKSB7XG4gICAgICAgICAgICBhd2FpdCBvbmxpbmVQbGF5ZXIuZ2V0TmV0d29ya1Nlc3Npb24oKS5nZXRDb25uZWN0aW9uKCkuc2VuZERhdGFQYWNrZXQoZGF0YVBhY2tldCk7XG4gICAgICAgIH1cbiAgICB9XG5cbiAgICAvKipcbiAgICAgKiBSZXR1cm5zIHRoZSBzZXJ2ZXIgdmVyc2lvbi5cbiAgICAgKiBAcmV0dXJucyB7c3RyaW5nfSBUaGUgc2VydmVyIHZlcnNpb24uXG4gICAgICogQGV4YW1wbGVcbiAgICAgKiBgYGB0eXBlc2NyaXB0XG4gICAgICogY29uc29sZS5sb2coc2VydmVyLmdldFZlcnNpb24oKSk7XG4gICAgICogYGBgXG4gICAgICovXG4gICAgcHVibGljIGdldFZlcnNpb24oKTogc3RyaW5nIHtcbiAgICAgICAgcmV0dXJuIHZlcnNpb247XG4gICAgfVxuXG4gICAgLyoqXG4gICAgICogUmV0dXJucyB0aGUgaWRlbnRpZmllcnMuXG4gICAgICogQHJldHVybnMge0lkZW50aWZpZXJzfSBUaGUgaWRlbnRpZmllcnMuXG4gICAgICovXG4gICAgcHVibGljIGdldElkZW50aWZpZXJzKCk6IHR5cGVvZiBJZGVudGlmaWVycyB7XG4gICAgICAgIHJldHVybiBJZGVudGlmaWVycztcbiAgICB9XG5cbiAgICAvKipcbiAgICAgKiBSZXR1cm5zIHRoZSBxdWVyeSBtYW5hZ2VyLlxuICAgICAqIEByZXR1cm5zIHtRdWVyeU1hbmFnZXJ9IFRoZSBxdWVyeSBtYW5hZ2VyLlxuICAgICAqL1xuICAgIHB1YmxpYyBnZXRRdWVyeU1hbmFnZXIoKTogUXVlcnlNYW5hZ2VyIHtcbiAgICAgICAgcmV0dXJuIHRoaXMucXVlcnlNYW5hZ2VyO1xuICAgIH1cblxuICAgIC8qKlxuICAgICAqIFJldHVybnMgdGhlIGNvbW1hbmQgbWFuYWdlci5cbiAgICAgKiBAcmV0dXJucyB7Q29tbWFuZE1hbmFnZXJ9IFRoZSBjb21tYW5kIG1hbmFnZXIuXG4gICAgICovXG4gICAgcHVibGljIGdldENvbW1hbmRNYW5hZ2VyKCk6IENvbW1hbmRNYW5hZ2VyIHtcbiAgICAgICAgcmV0dXJuIHRoaXMuY29tbWFuZE1hbmFnZXI7XG4gICAgfVxuXG4gICAgLyoqXG4gICAgICogUmV0dXJucyB0aGUgcGxheWVyIG1hbmFnZXIuXG4gICAgICogQHJldHVybnMge1Nlc3Npb25NYW5hZ2VyfSBUaGUgcGxheWVyIG1hbmFnZXIuXG4gICAgICovXG4gICAgcHVibGljIGdldFNlc3Npb25NYW5hZ2VyKCk6IFNlc3Npb25NYW5hZ2VyIHtcbiAgICAgICAgcmV0dXJuIHRoaXMuc2Vzc2lvbk1hbmFnZXI7XG4gICAgfVxuXG4gICAgLyoqXG4gICAgICogUmV0dXJucyB0aGUgd29ybGQgbWFuYWdlci5cbiAgICAgKiBAcmV0dXJucyB7V29ybGRNYW5hZ2VyfSBUaGUgd29ybGQgbWFuYWdlci5cbiAgICAgKi9cbiAgICBwdWJsaWMgZ2V0V29ybGRNYW5hZ2VyKCk6IFdvcmxkTWFuYWdlciB7XG4gICAgICAgIHJldHVybiB0aGlzLndvcmxkTWFuYWdlcjtcbiAgICB9XG5cbiAgICAvKipcbiAgICAgKiBSZXR1cm5zIHRoZSBpdGVtIG1hbmFnZXIuXG4gICAgICogQHJldHVybnMge0l0ZW1NYW5hZ2VyfSBUaGUgaXRlbSBtYW5hZ2VyLlxuICAgICAqL1xuICAgIHB1YmxpYyBnZXRJdGVtTWFuYWdlcigpOiBJdGVtTWFuYWdlciB7XG4gICAgICAgIHJldHVybiB0aGlzLml0ZW1NYW5hZ2VyO1xuICAgIH1cblxuICAgIC8qKlxuICAgICAqIFJldHVybnMgdGhlIGJsb2NrIG1hbmFnZXIuXG4gICAgICogQHJldHVybnMge0Jsb2NrTWFuYWdlcn0gVGhlIGJsb2NrIG1hbmFnZXIuXG4gICAgICovXG4gICAgcHVibGljIGdldEJsb2NrTWFuYWdlcigpOiBCbG9ja01hbmFnZXIge1xuICAgICAgICByZXR1cm4gdGhpcy5ibG9ja01hbmFnZXI7XG4gICAgfVxuXG4gICAgLyoqXG4gICAgICogUmV0dXJucyB0aGUgbG9nZ2VyLlxuICAgICAqIEByZXR1cm5zIHtMb2dnZXJCdWlsZGVyfSBUaGUgbG9nZ2VyLlxuICAgICAqIEBleGFtcGxlXG4gICAgICogYGBgdHlwZXNjcmlwdFxuICAgICAqIC8vIE5vcm1hbCBsb2c6XG4gICAgICogc2VydmVyLmdldExvZ2dlcigpLmluZm8oJ0hlbGxvLCB3b3JsZCEnKTtcbiAgICAgKiAvLyBEZWJ1ZyBsb2c6XG4gICAgICogc2VydmVyLmdldExvZ2dlcigpLmRlYnVnKCdIZWxsbywgd29ybGQhJyk7XG4gICAgICogLy8gRXJyb3IgbG9nOlxuICAgICAqIHNlcnZlci5nZXRMb2dnZXIoKS5lcnJvcihuZXcgRXJyb3IoJ0hlbGxvIFdvcmxkJykpO1xuICAgICAqIGBgYFxuICAgICAqL1xuICAgIHB1YmxpYyBnZXRMb2dnZXIoKTogTG9nZ2VyIHtcbiAgICAgICAgcmV0dXJuIHRoaXMubG9nZ2VyO1xuICAgIH1cblxuICAgIC8qKlxuICAgICAqIFJldHVybnMgdGhlIHBhY2tldCByZWdpc3RyeS5cbiAgICAgKiBAcmV0dXJucyB7UGFja2V0UmVnaXN0cnl9IFRoZSBwYWNrZXQgcmVnaXN0cnkuXG4gICAgICovXG4gICAgcHVibGljIGdldFBhY2tldFJlZ2lzdHJ5KCk6IFBhY2tldFJlZ2lzdHJ5IHtcbiAgICAgICAgcmV0dXJuIHRoaXMucGFja2V0UmVnaXN0cnk7XG4gICAgfVxuXG4gICAgLyoqXG4gICAgICogUmV0dXJucyB0aGUgcmFrbmV0IGluc3RhbmNlLlxuICAgICAqIEByZXR1cm5zIHtSYWtOZXRMaXN0ZW5lciB8IHVuZGVmaW5lZH0gVGhlIHJha25ldCBpbnN0YW5jZS5cbiAgICAgKi9cbiAgICBwdWJsaWMgZ2V0UmFrbmV0KCk6IFJha05ldExpc3RlbmVyIHwgdW5kZWZpbmVkIHtcbiAgICAgICAgcmV0dXJuIHRoaXMucmFrbmV0O1xuICAgIH1cblxuICAgIC8qKlxuICAgICAqIFJldHVybnMgdGhlIGNoYXQgbWFuYWdlci5cbiAgICAgKiBAcmV0dXJucyB7Q2hhdE1hbmFnZXJ9IFRoZSBjaGF0IG1hbmFnZXIuXG4gICAgICovXG4gICAgcHVibGljIGdldENoYXRNYW5hZ2VyKCk6IENoYXRNYW5hZ2VyIHtcbiAgICAgICAgcmV0dXJuIHRoaXMuY2hhdE1hbmFnZXI7XG4gICAgfVxuXG4gICAgLyoqXG4gICAgICogUmV0dXJucyB0aGUgY29uZmlnLlxuICAgICAqIEByZXR1cm5zIHtDb25maWd9IFRoZSBjb25maWcuXG4gICAgICogQGV4YW1wbGVcbiAgICAgKiBgYGB0eXBlc2NyaXB0XG4gICAgICogY29uc29sZS5sb2coc2VydmVyLmdldENvbmZpZygpLmdldE1heFBsYXllcnMoKSk7IC8vIDIwXG4gICAgICogYGBgXG4gICAgICovXG4gICAgcHVibGljIGdldENvbmZpZygpOiBDb25maWcge1xuICAgICAgICByZXR1cm4gdGhpcy5jb25maWc7XG4gICAgfVxuXG4gICAgLyoqXG4gICAgICogUmV0dXJucyB0aGUgY29uc29sZSBpbnN0YW5jZS5cbiAgICAgKiBAcmV0dXJucyB7Q29uc29sZSB8IHVuZGVmaW5lZH0gVGhlIGNvbnNvbGUgaW5zdGFuY2UuXG4gICAgICovXG4gICAgcHVibGljIGdldENvbnNvbGUoKSB7XG4gICAgICAgIHJldHVybiB0aGlzLmNvbnNvbGU7XG4gICAgfVxuXG4gICAgLyoqXG4gICAgICogUmV0dXJucyB0aGUgcGVybWlzc2lvbiBtYW5hZ2VyLlxuICAgICAqIEByZXR1cm5zIHtQZXJtaXNzaW9uTWFuYWdlcn0gVGhlIHBlcm1pc3Npb24gbWFuYWdlci5cbiAgICAgKi9cbiAgICBwdWJsaWMgZ2V0UGVybWlzc2lvbk1hbmFnZXIoKTogUGVybWlzc2lvbk1hbmFnZXIge1xuICAgICAgICByZXR1cm4gdGhpcy5wZXJtaXNzaW9uTWFuYWdlcjtcbiAgICB9XG5cbiAgICAvKipcbiAgICAgKiBSZXR1cm5zIHRoZSBiYW4gbWFuYWdlci5cbiAgICAgKiBAcmV0dXJucyB7QmFuTWFuYWdlcn0gVGhlIGJhbiBtYW5hZ2VyLlxuICAgICAqL1xuICAgIHB1YmxpYyBnZXRCYW5NYW5hZ2VyKCk6IEJhbk1hbmFnZXIge1xuICAgICAgICByZXR1cm4gdGhpcy5iYW5NYW5hZ2VyO1xuICAgIH1cblxuICAgIC8qKlxuICAgICAqIFJldHVybnMgdGhpcyBQcmlzbWFyaW5lIGluc3RhbmNlLlxuICAgICAqIEByZXR1cm5zIHtTZXJ2ZXJ9IFRoZSBQcmlzbWFyaW5lIGluc3RhbmNlLlxuICAgICAqL1xuICAgIHB1YmxpYyBnZXRTZXJ2ZXIoKTogU2VydmVyIHtcbiAgICAgICAgcmV0dXJuIHRoaXM7XG4gICAgfVxuXG4gICAgLyoqXG4gICAgICogUmV0dXJucyB0aGUgY3VycmVudCBUaWNrLlxuICAgICAqIEByZXR1cm5zIHtudW1iZXJ9IFRoZSBjdXJyZW50IFRpY2suXG4gICAgICovXG4gICAgcHVibGljIGdldFRpY2soKTogbnVtYmVyIHtcbiAgICAgICAgcmV0dXJuIE51bWJlcih0aGlzLmN1cnJlbnRUaWNrKTtcbiAgICB9XG5cbiAgICAvKipcbiAgICAgKiBSZXR1cm5zIHRoZSBjdXJyZW50IFRQUy5cbiAgICAgKiBAcmV0dXJucyB7bnVtYmVyfSBUaGUgY3VycmVudCBUUFMuXG4gICAgICovXG4gICAgcHVibGljIGdldFRQUygpOiBudW1iZXIge1xuICAgICAgICByZXR1cm4gTnVtYmVyLnBhcnNlRmxvYXQodGhpcy50cHMudG9GaXhlZCgyKSk7XG4gICAgfVxufVxuIl0sIm1hcHBpbmdzIjoiOzs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7O0FBa0NBLElBQXFCLFNBQXJCLE1BQXFCLGVBQWUsNEJBQUEsYUFBYTtDQUM3QztDQUNBO0NBQ0E7Q0FDQTtDQUNBO0NBQ0EsaUJBQWtDLElBQUksdUJBQUEsUUFBZTtDQUNyRDtDQUNBO0NBQ0E7Q0FDQTtDQUNBO0NBQ0E7Q0FDQTtDQUNBOzs7OztDQU1BLFdBQW1COzs7OztDQU1uQjs7Ozs7Q0FNQSxNQUFjOzs7OztDQU1kLGNBQXNCOzs7OztDQU10QjtDQUdBLE9BQXdCLHlCQUF5QixNQUFPOzs7Ozs7OztDQVN4RCxZQUFtQixFQUFFLFFBQVEsUUFBUSxXQUFXLFNBQWlFO0VBQzdHLE1BQU07RUFFTixLQUFLLFdBQVc7RUFFaEIsT0FBTyxLQUNILDJDQUEyQyxnQkFBQSxRQUFRLG9DQUFvQyw0QkFBQSxRQUFZLGtCQUFrQixHQUFHLEVBQUUsRUFBRSx1QkFBdUIsNEJBQUEsUUFBWSxTQUFTLElBQzVLO0VBRUEsS0FBSyxTQUFTO0VBQ2QsS0FBSyxTQUFTO0VBQ2QsS0FBSyxpQkFBaUIsSUFBSSwrQkFBQSxRQUFlLElBQUk7RUFDN0MsS0FBSyxjQUFjLElBQUkseUJBQUEs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