@jsprismarine/prismarine
Version:
Dedicated Minecraft Bedrock Edition server written in TypeScript
463 lines (461 loc) • 57 kB
JavaScript
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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