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