@jsprismarine/prismarine
Version:
Dedicated Minecraft Bedrock Edition server written in TypeScript
408 lines (407 loc) • 52.4 kB
JavaScript
import { withCwd } from "../utils/cwd.es.js";
import Timer from "../utils/Timer.es.js";
import { BlockMappings } from "../block/BlockMappings.es.js";
import { Item } from "../item/Item.es.js";
import Chunk from "./chunk/Chunk.es.js";
import GameruleManager, { GameRules } from "./GameruleManager.es.js";
import UUID from "../utils/UUID.es.js";
import LevelSoundEventPacket from "../network/packet/LevelSoundEventPacket.es.js";
import UpdateBlockPacket from "../network/packet/UpdateBlockPacket.es.js";
import WorldEventPacket from "../network/packet/WorldEventPacket.es.js";
import { Entities_exports } from "../entity/Entities.es.js";
import fs from "node:fs";
import { parseJSON5 } from "confbox";
import { Vector3 } from "@jsprismarine/math";
import { getGametypeName } from "@jsprismarine/minecraft";
//#region src/world/World.ts
var LEVEL_DATA_FILE_NAME = "level.json";
var WORLDS_FOLDER_NAME = "worlds";
var World = class {
uuid = UUID.randomString();
name;
entities = /* @__PURE__ */ new Map();
chunks = /* @__PURE__ */ new Map();
gameruleManager;
currentTick = 0;
provider;
server;
seed;
generator;
config;
spawn = null;
constructor({ name, server, provider, seed, generator, config }) {
this.name = name;
this.server = server;
this.provider = provider;
this.gameruleManager = new GameruleManager(server);
this.seed = seed;
this.generator = generator;
this.config = config ?? {};
this.gameruleManager.setGamerule(GameRules.ShowCoordinates, true, true);
try {
const path = withCwd(WORLDS_FOLDER_NAME, this.name, "playerdata");
if (!fs.existsSync(path)) fs.mkdirSync(path, { recursive: true });
} catch (error) {
this.server.getLogger().error(`Failed to create world folders for ${this.name}`);
this.server.getLogger().error(error);
}
}
/**
* On enable hook.
* @group Lifecycle
*/
async enable() {
this.server.on("tick", async (evt) => this.update(evt.getTick()));
const level = await this.getLevelData();
if (level.spawn) this.setSpawnPosition(Vector3.fromObject(level.spawn));
if (level.gameRules) level.gameRules.forEach(([name, [value, editable]]) => this.gameruleManager.setGamerule(name, value, editable));
if (level.entities) for (const entityData of level.entities) {
const Entity = Array.from(Object.values(Entities_exports)).find((e) => e.MOB_ID === entityData.type);
if (!Entity) {
this.server.getLogger().warn(`Entity type ${entityData.type} not found`);
continue;
}
await this.addEntity(new Entity({
world: this,
uuid: entityData.uuid,
...entityData.position,
server: this.server
}));
}
this.provider.setWorld(this);
await this.provider.enable();
this.server.getLogger().info(`Preparing start region for dimension ${this.getFormattedName()}`);
const chunksToLoad = [];
const timer = new Timer();
const size = this.server.getConfig().getViewDistance() * 5;
for (let x = 0; x < size; x++) for (let z = 0; z < size; z++) chunksToLoad.push(this.loadChunk(x, z, true));
await Promise.all(chunksToLoad);
this.server.getLogger().verbose(`(took §e${timer.stop()} ms§r)`);
}
/**
* On disable hook.
* @group Lifecycle
*/
async disable() {
await this.save();
await this.provider.disable();
}
getGenerator() {
return this.generator;
}
/**
* Called every tick.
*
* @param tick
*/
async update(tick) {
this.currentTick++;
if (this.currentTick / 20 === 120) await this.save();
await Promise.all(this.getEntities().map((entity) => entity.update(tick)));
await this.sendTime();
}
/**
* Returns a block instance in the given world position.
* @param {number} x - block x
* @param {number} y - block y
* @param {number} z - block z
* @param {number} [layer=0] - block storage layer (0 for blocks, 1 for liquids)
*/
async getBlock(x, y, z, layer = 0) {
const blockId = (await this.getChunkAt(x, z)).getBlock(x, y, z, layer);
const block = this.server.getBlockManager().getBlockByIdAndMeta(blockId.id, blockId.meta);
if (!block) return this.server.getBlockManager().getBlock("minecraft:air");
return block;
}
/**
* Returns the chunk in the specifies x and z, if the chunk doesn't exists
* it is generated.
*/
async getChunk(cx, cz) {
const index = Chunk.packXZ(cx, cz);
if (!this.chunks.has(index)) return this.loadChunk(cx, cz);
return this.chunks.get(index);
}
/**
* Loads a chunk in a given x and z and returns its.
* @param {number} x - x coordinate.
* @param {number} z - z coordinate.
*/
async loadChunk(x, z, _ignoreWarn) {
const index = Chunk.packXZ(x, z);
const chunk = await this.provider.readChunk(x, z, this.seed, this.generator, this.config);
this.chunks.set(index, chunk);
return chunk;
}
/**
* Sends a world event packet to all the viewers in the position chunk.
* @param {Vector3} position - world position.
* @param {number} event - event identifier.
* @param {number} data - event data.
*/
async sendWorldEvent(position, event, data) {
const worldEventPacket = new WorldEventPacket();
worldEventPacket.eventId = event;
worldEventPacket.data = data;
await Promise.all(this.getPlayers().map((player) => player.getNetworkSession().send(worldEventPacket)));
}
async getChunkAt(x, z = 0) {
if (x instanceof Vector3) return this.getChunkAt(x.getX(), x.getZ());
return this.getChunk(x >> 4, z >> 4);
}
/**
* Returns the world default spawn position.
*/
async getSpawnPosition() {
if (this.spawn) return this.spawn;
const x = 0;
const z = 0;
return new Vector3(z, (await this.getChunkAt(x, z)).getHighestBlockAt(x, z) + 1 + 2, z);
}
/**
* Set the world's spawn position.
* @param {Vector3} pos - The position.
*/
setSpawnPosition(pos) {
this.spawn = pos;
}
async useItemOn(itemInHand, blockPosition, face, clickPosition, player) {
if (itemInHand instanceof Item) return;
const block = itemInHand;
const blockId = (await this.getChunkAt(blockPosition)).getBlock(blockPosition);
const clickedBlock = this.server.getBlockManager().getBlockByIdAndMeta(blockId.id, blockId.meta);
if (!block || !clickedBlock) return;
if (clickedBlock.getName() === "minecraft:air" || !block.canBePlaced()) return;
const placedPosition = new Vector3(blockPosition.getX(), blockPosition.getY(), blockPosition.getZ());
if (!clickedBlock.canBeReplaced()) switch (face) {
case 0:
placedPosition.setY(placedPosition.getY() - 1);
break;
case 1:
placedPosition.setY(placedPosition.getY() + 1);
break;
case 2:
placedPosition.setZ(placedPosition.getZ() - 1);
break;
case 3:
placedPosition.setZ(placedPosition.getZ() + 1);
break;
case 4:
placedPosition.setX(placedPosition.getX() - 1);
break;
case 5:
placedPosition.setX(placedPosition.getX() + 1);
break;
default: throw new Error("Invalid Face");
}
if (blockPosition.getY() < 0 || blockPosition.getY() > 255) return;
if (!await new Promise(async (resolve) => {
try {
(await this.getChunkAt(placedPosition.getX(), placedPosition.getZ())).setBlock(placedPosition.getX(), placedPosition.getY(), placedPosition.getZ(), block);
resolve(true);
} catch (error) {
player.getServer().getLogger().warn(`${player.getName()} failed to place block due to ${error}`);
await player.sendMessage(error?.message);
resolve(false);
}
})) {
if (placedPosition.getY() < 0) return;
const blockUpdate = new UpdateBlockPacket();
blockUpdate.x = placedPosition.getX();
blockUpdate.y = placedPosition.getY();
blockUpdate.z = placedPosition.getZ();
blockUpdate.blockRuntimeId = BlockMappings.getRuntimeId(clickedBlock.getName());
return;
}
const runtimeId = BlockMappings.getRuntimeId(block.getName());
const blockUpdate = new UpdateBlockPacket();
blockUpdate.x = placedPosition.getX();
blockUpdate.y = placedPosition.getY();
blockUpdate.z = placedPosition.getZ();
blockUpdate.blockRuntimeId = runtimeId;
await Promise.all(this.server.getSessionManager().getAllPlayers().map(async (onlinePlayer) => onlinePlayer.getNetworkSession().getConnection().sendDataPacket(blockUpdate)));
const pk = new LevelSoundEventPacket();
pk.sound = 6;
pk.positionX = placedPosition.getX();
pk.positionY = placedPosition.getY();
pk.positionZ = placedPosition.getZ();
pk.extraData = runtimeId;
pk.disableRelativeVolume = false;
await Promise.all(player.getWorld().getPlayers().map((target) => target.getNetworkSession().send(pk)));
}
/**
* Sends the current time to all players in the world.
*/
async sendTime() {
await Promise.all(this.getPlayers().map((player) => player.getNetworkSession().sendTime(this.getTicks())));
}
/**
* Adds an entity to the level.
* @param {Entity} entity - The entity to add.
*/
async addEntity(entity) {
this.entities.set(entity.getRuntimeId(), entity);
if (!entity.isPlayer()) await entity.sendSpawn();
else await Promise.all(this.getEntities().map((e) => e.sendSpawn(entity)));
}
/**
* Removes an entity from the level.
* @param {Entity} entity - The entity to remove.
*/
async removeEntity(entity) {
if (!entity.isPlayer()) await entity.sendDespawn();
else await Promise.all(this.getEntities().map((e) => e.sendDespawn(entity)));
this.entities.delete(entity.getRuntimeId());
}
/**
* Get all entities in this world.
* @returns {Entity[]} the entities.
*/
getEntities() {
return Array.from(this.entities.values());
}
/**
* Get all players in this world.
* @returns {Player[]} the players.
*/
getPlayers() {
return this.getEntities().filter((e) => e.isPlayer()).filter((p) => p.isOnline());
}
/**
* Saves changed chunks into disk.
*/
async saveChunks() {
const timer = new Timer();
this.server.getLogger().info(`Saving chunks for level ${this.getFormattedName()}`);
await Promise.all(Array.from(this.chunks.values()).filter((c) => c.getHasChanged()).map(async (chunk) => this.provider.writeChunk(chunk)));
this.server.getLogger().verbose(`(took §e${timer.stop()} ms§r)!`);
}
async save() {
this.getPlayers().forEach(async (player) => {
await this.savePlayerData(player);
});
await this.saveChunks();
await this.saveLevelData();
}
getGameruleManager() {
return this.gameruleManager;
}
getTicks() {
return this.currentTick;
}
setTicks(tick) {
this.currentTick = tick;
}
getProvider() {
return this.provider;
}
getUUID() {
return this.uuid;
}
getName() {
return this.name;
}
getFormattedName() {
return `§b'${this.name}'/${this.generator.constructor.name}§r`;
}
getSeed() {
return this.seed;
}
async getLevelData() {
const path = withCwd(WORLDS_FOLDER_NAME, this.name, LEVEL_DATA_FILE_NAME);
if (!fs.existsSync(path)) return {};
try {
return parseJSON5((await fs.promises.readFile(path, "utf-8")).toString());
} catch (error) {
this.server.getLogger().error(error);
}
return {};
}
async saveLevelData() {
const data = {
spawn: await this.getSpawnPosition(),
gamerules: Array.from(this.getGameruleManager().getGamerules()),
entities: this.getEntities().filter((entity) => !entity.isPlayer() && !entity.isConsole()).map((entity) => ({
uuid: entity.getUUID(),
type: entity.getType(),
position: {
x: entity.getX(),
y: entity.getY(),
z: entity.getZ(),
pitch: entity.pitch,
yaw: entity.yaw,
headYaw: entity.headYaw
}
}))
};
try {
await fs.promises.writeFile(withCwd(WORLDS_FOLDER_NAME, this.name, LEVEL_DATA_FILE_NAME), JSON.stringify(data, null, 4));
} catch (error) {
this.server.getLogger().error(`Failed to save level data`);
this.server.getLogger().error(error);
}
}
/**
* Get the player data for a player.
* @param {Player} player - The player to get the data for.
* @returns {Promise<WorldPlayerData>} The player data.
*/
async getPlayerData(player) {
try {
if (!player.getXUID()) throw new Error("Player has no XUID");
return parseJSON5((await fs.promises.readFile(withCwd(WORLDS_FOLDER_NAME, this.name, "playerdata", `${player.getXUID() || player.getName()}.json`), {
flag: "r",
encoding: "utf-8"
})).toString());
} catch (error) {
this.server.getLogger().debug(`PlayerData is missing for player ${player.getXUID()}`);
this.server.getLogger().error(error);
const spawn = await this.getSpawnPosition();
return {
gamemode: this.server.getConfig().getGamemode(),
position: {
x: spawn.getX(),
y: spawn.getY(),
z: spawn.getZ(),
pitch: 0,
yaw: 0,
headYaw: 0
}
};
}
}
async savePlayerData(player) {
const data = {
uuid: player.getUUID(),
username: player.getName(),
gamemode: getGametypeName(player.gamemode),
position: {
x: player.getX(),
y: player.getY(),
z: player.getZ(),
pitch: player.pitch,
yaw: player.yaw,
headYaw: player.headYaw
}
};
try {
await fs.promises.writeFile(withCwd(WORLDS_FOLDER_NAME, this.name, "playerdata", `${player.getXUID() || player.getName()}.json`), JSON.stringify(data, null, 4), {
flag: "w+",
encoding: "utf-8",
flush: true
});
} catch (error) {
this.server.getLogger().error(`Failed to save player data`);
this.server.getLogger().error(error);
}
}
/**
* @returns {Server} The server instance.
*/
getServer() {
return this.server;
}
};
//#endregion
export { World };
//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiV29ybGQuZXMuanMiLCJuYW1lcyI6W10sInNvdXJjZXMiOlsiLi4vLi4vc3JjL3dvcmxkL1dvcmxkLnRzIl0sInNvdXJjZXNDb250ZW50IjpbImltcG9ydCBHYW1lcnVsZU1hbmFnZXIsIHsgR2FtZVJ1bGVzIH0gZnJvbSAnLi9HYW1lcnVsZU1hbmFnZXInO1xuXG5pbXBvcnQgZnMgZnJvbSAnbm9kZTpmcyc7XG5cbmltcG9ydCB7IHBhcnNlSlNPTjUgfSBmcm9tICdjb25mYm94JztcblxuaW1wb3J0IHsgVmVjdG9yMyB9IGZyb20gJ0Bqc3ByaXNtYXJpbmUvbWF0aCc7XG5pbXBvcnQgeyBnZXRHYW1ldHlwZU5hbWUgfSBmcm9tICdAanNwcmlzbWFyaW5lL21pbmVjcmFmdCc7XG5pbXBvcnQgdHlwZSB7IEJsb2NrLCBQbGF5ZXIsIFNlcnZlciwgU2VydmljZSB9IGZyb20gJy4uLyc7XG5pbXBvcnQgeyBUaW1lciwgVVVJRCB9IGZyb20gJy4uLyc7XG5pbXBvcnQgeyBCbG9ja01hcHBpbmdzIH0gZnJvbSAnLi4vYmxvY2svQmxvY2tNYXBwaW5ncyc7XG5pbXBvcnQgKiBhcyBFbnRpdGllcyBmcm9tICcuLi9lbnRpdHkvRW50aXRpZXMnO1xuaW1wb3J0IHR5cGUgeyBFbnRpdHkgfSBmcm9tICcuLi9lbnRpdHkvRW50aXR5JztcbmltcG9ydCB7IEl0ZW0gfSBmcm9tICcuLi9pdGVtL0l0ZW0nO1xuaW1wb3J0IExldmVsU291bmRFdmVudFBhY2tldCBmcm9tICcuLi9uZXR3b3JrL3BhY2tldC9MZXZlbFNvdW5kRXZlbnRQYWNrZXQnO1xuaW1wb3J0IFVwZGF0ZUJsb2NrUGFja2V0IGZyb20gJy4uL25ldHdvcmsvcGFja2V0L1VwZGF0ZUJsb2NrUGFja2V0JztcbmltcG9ydCB0eXBlIHsgV29ybGRFdmVudCB9IGZyb20gJy4uL25ldHdvcmsvcGFja2V0L1dvcmxkRXZlbnRQYWNrZXQnO1xuaW1wb3J0IFdvcmxkRXZlbnRQYWNrZXQgZnJvbSAnLi4vbmV0d29yay9wYWNrZXQvV29ybGRFdmVudFBhY2tldCc7XG5pbXBvcnQgeyB3aXRoQ3dkIH0gZnJvbSAnLi4vdXRpbHMvY3dkJztcbmltcG9ydCB0eXBlIHsgR2VuZXJhdG9yIH0gZnJvbSAnLi9HZW5lcmF0b3InO1xuaW1wb3J0IENodW5rIGZyb20gJy4vY2h1bmsvQ2h1bmsnO1xuaW1wb3J0IHR5cGUgQmFzZVByb3ZpZGVyIGZyb20gJy4vcHJvdmlkZXJzL0Jhc2VQcm92aWRlcic7XG5cbmNvbnN0IExFVkVMX0RBVEFfRklMRV9OQU1FID0gJ2xldmVsLmpzb24nO1xuY29uc3QgV09STERTX0ZPTERFUl9OQU1FID0gJ3dvcmxkcyc7XG5cbmV4cG9ydCBpbnRlcmZhY2UgV29ybGREYXRhIHtcbiAgICBuYW1lOiBzdHJpbmc7XG4gICAgcGF0aDogc3RyaW5nO1xuICAgIHNlcnZlcjogU2VydmVyO1xuICAgIHByb3ZpZGVyOiBCYXNlUHJvdmlkZXI7XG4gICAgc2VlZDogbnVtYmVyO1xuICAgIGdlbmVyYXRvcjogR2VuZXJhdG9yO1xuICAgIGNvbmZpZz86IGFueTtcbn1cblxuZXhwb3J0IGludGVyZmFjZSBMZXZlbERhdGEge1xuICAgIHNwYXduOiB7IHg6IG51bWJlcjsgeTogbnVtYmVyOyB6OiBudW1iZXIgfSB8IHVuZGVmaW5lZDtcbiAgICBnYW1lUnVsZXM6IEFycmF5PFtzdHJpbmcsIGFueV0+O1xuICAgIGVudGl0aWVzOiBBcnJheTx7XG4gICAgICAgIHV1aWQ6IHN0cmluZztcbiAgICAgICAgdHlwZTogc3RyaW5nO1xuICAgICAgICBwb3NpdGlvbjoge1xuICAgICAgICAgICAgeDogbnVtYmVyO1xuICAgICAgICAgICAgeTogbnVtYmVyO1xuICAgICAgICAgICAgejogbnVtYmVyO1xuICAgICAgICB9O1xuICAgIH0+O1xufVxuZXhwb3J0IGludGVyZmFjZSBXb3JsZFBsYXllckRhdGEge1xuICAgIGdhbWVtb2RlOiBzdHJpbmc7XG4gICAgcG9zaXRpb246IHtcbiAgICAgICAgeDogbnVtYmVyO1xuICAgICAgICB5OiBudW1iZXI7XG4gICAgICAgIHo6IG51bWJlcjtcbiAgICAgICAgcGl0Y2g6IG51bWJlcjtcbiAgICAgICAgeWF3OiBudW1iZXI7XG4gICAgICAgIGhlYWRZYXc6IG51bWJlcjtcbiAgICB9O1xufVxuXG5leHBvcnQgY2xhc3MgV29ybGQgaW1wbGVtZW50cyBTZXJ2aWNlIHtcbiAgICBwcml2YXRlIHJlYWRvbmx5IHV1aWQ6IHN0cmluZyA9IFVVSUQucmFuZG9tU3RyaW5nKCk7XG4gICAgcHJpdmF0ZSBuYW1lOiBzdHJpbmc7XG5cbiAgICBwcml2YXRlIHJlYWRvbmx5IGVudGl0aWVzOiBNYXA8YmlnaW50LCBFbnRpdHk+ID0gbmV3IE1hcCgpO1xuICAgIHByaXZhdGUgcmVhZG9ubHkgY2h1bmtzOiBNYXA8YmlnaW50LCBDaHVuaz4gPSBuZXcgTWFwKCk7XG4gICAgcHJpdmF0ZSByZWFkb25seSBnYW1lcnVsZU1hbmFnZXI6IEdhbWVydWxlTWFuYWdlcjtcbiAgICBwcml2YXRlIGN1cnJlbnRUaWNrID0gMDtcbiAgICBwcml2YXRlIHJlYWRvbmx5IHByb3ZpZGVyOiBCYXNlUHJvdmlkZXI7XG4gICAgcHJpdmF0ZSByZWFkb25seSBzZXJ2ZXI6IFNlcnZlcjtcbiAgICBwcml2YXRlIHJlYWRvbmx5IHNlZWQ6IG51bWJlcjtcbiAgICBwcml2YXRlIHJlYWRvbmx5IGdlbmVyYXRvcjogR2VuZXJhdG9yO1xuICAgIHByaXZhdGUgcmVhZG9ubHkgY29uZmlnOiBPYmplY3Q7XG4gICAgcHJpdmF0ZSBzcGF3bjogVmVjdG9yMyB8IG51bGwgPSBudWxsO1xuXG4gICAgcHVibGljIGNvbnN0cnVjdG9yKHsgbmFtZSwgc2VydmVyLCBwcm92aWRlciwgc2VlZCwgZ2VuZXJhdG9yLCBjb25maWcgfTogV29ybGREYXRhKSB7XG4gICAgICAgIHRoaXMubmFtZSA9IG5hbWU7XG4gICAgICAgIHRoaXMuc2VydmVyID0gc2VydmVyO1xuICAgICAgICB0aGlzLnByb3ZpZGVyID0gcHJvdmlkZXI7XG4gICAgICAgIHRoaXMuZ2FtZXJ1bGVNYW5hZ2VyID0gbmV3IEdhbWVydWxlTWFuYWdlcihzZXJ2ZXIpO1xuICAgICAgICB0aGlzLnNlZWQgPSBzZWVkO1xuICAgICAgICB0aGlzLmdlbmVyYXRvciA9IGdlbmVyYXRvcjtcbiAgICAgICAgdGhpcy5jb25maWcgPSBjb25maWcgPz8ge307XG5cbiAgICAgICAgdGhpcy5nYW1lcnVsZU1hbmFnZXIuc2V0R2FtZXJ1bGUoR2FtZVJ1bGVzLlNob3dDb29yZGluYXRlcywgdHJ1ZSwgdHJ1ZSk7XG5cbiAgICAgICAgdHJ5IHtcbiAgICAgICAgICAgIC8vIENyZWF0ZSBmb2xkZXJzIGlmIHRoZXkgZG9uJ3QgZXhpc3QuXG4gICAgICAgICAgICBjb25zdCBwYXRoID0gd2l0aEN3ZChXT1JMRFNfRk9MREVSX05BTUUsIHRoaXMubmFtZSwgJ3BsYXllcmRhdGEnKTtcbiAgICAgICAgICAgIGlmICghZnMuZXhpc3RzU3luYyhwYXRoKSkgZnMubWtkaXJTeW5jKHBhdGgsIHsgcmVjdXJzaXZlOiB0cnVlIH0pO1xuICAgICAgICB9IGNhdGNoIChlcnJvcjogdW5rbm93bikge1xuICAgICAgICAgICAgdGhpcy5zZXJ2ZXIuZ2V0TG9nZ2VyKCkuZXJyb3IoYEZhaWxlZCB0byBjcmVhdGUgd29ybGQgZm9sZGVycyBmb3IgJHt0aGlzLm5hbWV9YCk7XG4gICAgICAgICAgICB0aGlzLnNlcnZlci5nZXRMb2dnZXIoKS5lcnJvcihlcnJvcik7XG4gICAgICAgIH1cbiAgICB9XG5cbiAgICAvKipcbiAgICAgKiBPbiBlbmFibGUgaG9vay5cbiAgICAgKiBAZ3JvdXAgTGlmZWN5Y2xlXG4gICAgICovXG4gICAgcHVibGljIGFzeW5jIGVuYWJsZSgpOiBQcm9taXNlPHZvaWQ+IHtcbiAgICAgICAgdGhpcy5zZXJ2ZXIub24oJ3RpY2snLCBhc3luYyAoZXZ0KSA9PiB0aGlzLnVwZGF0ZShldnQuZ2V0VGljaygpKSk7XG5cbiAgICAgICAgY29uc3QgbGV2ZWwgPSBhd2FpdCB0aGlzLmdldExldmVsRGF0YSgpO1xuICAgICAgICBpZiAobGV2ZWwuc3Bhd24pIHRoaXMuc2V0U3Bhd25Qb3NpdGlvbihWZWN0b3IzLmZyb21PYmplY3QobGV2ZWwuc3Bhd24pKTtcbiAgICAgICAgaWYgKGxldmVsLmdhbWVSdWxlcykge1xuICAgICAgICAgICAgbGV2ZWwuZ2FtZVJ1bGVzLmZvckVhY2goKFtuYW1lLCBbdmFsdWUsIGVkaXRhYmxlXV0pID0+XG4gICAgICAgICAgICAgICAgdGhpcy5nYW1lcnVsZU1hbmFnZXIuc2V0R2FtZXJ1bGUobmFtZSwgdmFsdWUsIGVkaXRhYmxlKVxuICAgICAgICAgICAgKTtcbiAgICAgICAgfVxuICAgICAgICBpZiAobGV2ZWwuZW50aXRpZXMpIHtcbiAgICAgICAgICAgIGZvciAoY29uc3QgZW50aXR5RGF0YSBvZiBsZXZlbC5lbnRpdGllcykge1xuICAgICAgICAgICAgICAgIGNvbnN0IEVudGl0eSA9IEFycmF5LmZyb20oT2JqZWN0LnZhbHVlcyhFbnRpdGllcykpLmZpbmQoKGUpID0+IGUuTU9CX0lEID09PSBlbnRpdHlEYXRhLnR5cGUpO1xuICAgICAgICAgICAgICAgIGlmICghRW50aXR5KSB7XG4gICAgICAgICAgICAgICAgICAgIHRoaXMuc2VydmVyLmdldExvZ2dlcigpLndhcm4oYEVudGl0eSB0eXBlICR7ZW50aXR5RGF0YS50eXBlfSBub3QgZm91bmRgKTtcbiAgICAgICAgICAgICAgICAgICAgY29udGludWU7XG4gICAgICAgICAgICAgICAgfVxuXG4gICAgICAgICAgICAgICAgYXdhaXQgdGhpcy5hZGRFbnRpdHkoXG4gICAgICAgICAgICAgICAgICAgIG5ldyBFbnRpdHkoe1xuICAgICAgICAgICAgICAgICAgICAgICAgd29ybGQ6IHRoaXMsXG4gICAgICAgICAgICAgICAgICAgICAgICB1dWlkOiBlbnRpdHlEYXRhLnV1aWQsXG4gICAgICAgICAgICAgICAgICAgICAgICAuLi5lbnRpdHlEYXRhLnBvc2l0aW9uLFxuICAgICAgICAgICAgICAgICAgICAgICAgc2VydmVyOiB0aGlzLnNlcnZlclxuICAgICAgICAgICAgICAgICAgICB9KVxuICAgICAgICAgICAgICAgICk7XG4gICAgICAgICAgICB9XG4gICAgICAgIH1cblxuICAgICAgICB0aGlzLnByb3ZpZGVyLnNldFdvcmxkKHRoaXMpO1xuICAgICAgICBhd2FpdCB0aGlzLnByb3ZpZGVyLmVuYWJsZSgpO1xuXG4gICAgICAgIHRoaXMuc2VydmVyLmdldExvZ2dlcigpLmluZm8oYFByZXBhcmluZyBzdGFydCByZWdpb24gZm9yIGRpbWVuc2lvbiAke3RoaXMuZ2V0Rm9ybWF0dGVkTmFtZSgpfWApO1xuICAgICAgICBjb25zdCBjaHVua3NUb0xvYWQ6IEFycmF5PFByb21pc2U8Q2h1bms+PiA9IFtdO1xuICAgICAgICBjb25zdCB0aW1lciA9IG5ldyBUaW1lcigpO1xuXG4gICAgICAgIGNvbnN0IHNpemUgPSB0aGlzLnNlcnZlci5nZXRDb25maWcoKS5nZXRWaWV3RGlzdGFuY2UoKSAqIDU7XG4gICAgICAgIGZvciAobGV0IHggPSAwOyB4IDwgc2l6ZTsgeCsrKSB7XG4gICAgICAgICAgICBmb3IgKGxldCB6ID0gMDsgeiA8IHNpemU7IHorKykge1xuICAgICAgICAgICAgICAgIGNodW5rc1RvTG9hZC5wdXNoKHRoaXMubG9hZENodW5rKHgsIHosIHRydWUpKTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgfVxuXG4gICAgICAgIGF3YWl0IFByb21pc2UuYWxsKGNodW5rc1RvTG9hZCk7XG4gICAgICAgIHRoaXMuc2VydmVyLmdldExvZ2dlcigpLnZlcmJvc2UoYCh0b29rIMKnZSR7dGltZXIuc3RvcCgpfSBtc8KncilgKTtcbiAgICB9XG5cbiAgICAvKipcbiAgICAgKiBPbiBkaXNhYmxlIGhvb2suXG4gICAgICogQGdyb3VwIExpZmVjeWNsZVxuICAgICAqL1xuICAgIHB1YmxpYyBhc3luYyBkaXNhYmxlKCk6IFByb21pc2U8dm9pZD4ge1xuICAgICAgICBhd2FpdCB0aGlzLnNhdmUoKTtcbiAgICAgICAgYXdhaXQgdGhpcy5wcm92aWRlci5kaXNhYmxlKCk7XG4gICAgfVxuXG4gICAgcHVibGljIGdldEdlbmVyYXRvcigpOiBHZW5lcmF0b3Ige1xuICAgICAgICByZXR1cm4gdGhpcy5nZW5lcmF0b3I7XG4gICAgfVxuXG4gICAgLyoqXG4gICAgICogQ2FsbGVkIGV2ZXJ5IHRpY2suXG4gICAgICpcbiAgICAgKiBAcGFyYW0gdGlja1xuICAgICAqL1xuICAgIHB1YmxpYyBhc3luYyB1cGRhdGUodGljazogbnVtYmVyKTogUHJvbWlzZTx2b2lkPiB7XG4gICAgICAgIC8vIFRPRE86IHRpY2sgY2h1bmtzXG5cbiAgICAgICAgLy8gQ29udGludWUgd29ybGQgdGltZSB0aWNrc1xuICAgICAgICB0aGlzLmN1cnJlbnRUaWNrKys7XG5cbiAgICAgICAgLy8gQXV0byBzYXZlIGV2ZXJ5IDIgbWludXRlc1xuICAgICAgICBpZiAodGhpcy5jdXJyZW50VGljayAvIDIwID09PSAxMjApIHtcbiAgICAgICAgICAgIGF3YWl0IHRoaXMuc2F2ZSgpO1xuICAgICAgICB9XG5cbiAgICAgICAgYXdhaXQgUHJvbWlzZS5hbGwodGhpcy5nZXRFbnRpdGllcygpLm1hcCgoZW50aXR5KSA9PiBlbnRpdHkudXBkYXRlKHRpY2spKSk7XG4gICAgICAgIGF3YWl0IHRoaXMuc2VuZFRpbWUoKTtcbiAgICB9XG5cbiAgICAvKipcbiAgICAgKiBSZXR1cm5zIGEgYmxvY2sgaW5zdGFuY2UgaW4gdGhlIGdpdmVuIHdvcmxkIHBvc2l0aW9uLlxuICAgICAqIEBwYXJhbSB7bnVtYmVyfSB4IC0gYmxvY2sgeFxuICAgICAqIEBwYXJhbSB7bnVtYmVyfSB5IC0gYmxvY2sgeVxuICAgICAqIEBwYXJhbSB7bnVtYmVyfSB6IC0gYmxvY2sgelxuICAgICAqIEBwYXJhbSB7bnVtYmVyfSBbbGF5ZXI9MF0gLSBibG9jayBzdG9yYWdlIGxheWVyICgwIGZvciBibG9ja3MsIDEgZm9yIGxpcXVpZHMpXG4gICAgICovXG4gICAgcHVibGljIGFzeW5jIGdldEJsb2NrKHg6IG51bWJlciwgeTogbnVtYmVyLCB6OiBudW1iZXIsIGxheWVyID0gMCk6IFByb21pc2U8QmxvY2s+IHtcbiAgICAgICAgY29uc3QgYmxvY2tJZCA9IChhd2FpdCB0aGlzLmdldENodW5rQXQoeCwgeikpLmdldEJsb2NrKHgsIHksIHosIGxheWVyKTtcbiAgICAgICAgY29uc3QgYmxvY2sgPSB0aGlzLnNlcnZlci5nZXRCbG9ja01hbmFnZXIoKS5nZXRCbG9ja0J5SWRBbmRNZXRhKGJsb2NrSWQuaWQsIGJsb2NrSWQubWV0YSk7XG5cbiAgICAgICAgaWYgKCFibG9jaykgcmV0dXJuIHRoaXMuc2VydmVyLmdldEJsb2NrTWFuYWdlcigpLmdldEJsb2NrKCdtaW5lY3JhZnQ6YWlyJyk7XG4gICAgICAgIHJldHVybiBibG9jaztcbiAgICB9XG5cbiAgICAvKipcbiAgICAgKiBSZXR1cm5zIHRoZSBjaHVuayBpbiB0aGUgc3BlY2lmaWVzIHggYW5kIHosIGlmIHRoZSBjaHVuayBkb2Vzbid0IGV4aXN0c1xuICAgICAqIGl0IGlzIGdlbmVyYXRlZC5cbiAgICAgKi9cbiAgICBwdWJsaWMgYXN5bmMgZ2V0Q2h1bmsoY3g6IG51bWJlciwgY3o6IG51bWJlcik6IFByb21pc2U8Q2h1bms+IHtcbiAgICAgICAgY29uc3QgaW5kZXggPSBDaHVuay5wYWNrWFooY3gsIGN6KTtcbiAgICAgICAgaWYgKCF0aGlzLmNodW5rcy5oYXMoaW5kZXgpKSByZXR1cm4gdGhpcy5sb2FkQ2h1bmsoY3gsIGN6KTtcblxuICAgICAgICByZXR1cm4gdGhpcy5jaHVua3MuZ2V0KGluZGV4KSE7XG4gICAgfVxuXG4gICAgLyoqXG4gICAgICogTG9hZHMgYSBjaHVuayBpbiBhIGdpdmVuIHggYW5kIHogYW5kIHJldHVybnMgaXRzLlxuICAgICAqIEBwYXJhbSB7bnVtYmVyfSB4IC0geCBjb29yZGluYXRlLlxuICAgICAqIEBwYXJhbSB7bnVtYmVyfSB6IC0geiBjb29yZGluYXRlLlxuICAgICAqL1xuICAgIHB1YmxpYyBhc3luYyBsb2FkQ2h1bmsoeDogbnVtYmVyLCB6OiBudW1iZXIsIF9pZ25vcmVXYXJuPzogYm9vbGVhbik6IFByb21pc2U8Q2h1bms+IHtcbiAgICAgICAgY29uc3QgaW5kZXggPSBDaHVuay5wYWNrWFooeCwgeik7XG4gICAgICAgIC8vIFRyeSAtIGNhdGNoIGZvciBwcm92aWRlciBlcnJvcnNcbiAgICAgICAgY29uc3QgY2h1bmsgPSBhd2FpdCB0aGlzLnByb3ZpZGVyLnJlYWRDaHVuayh4LCB6LCB0aGlzLnNlZWQsIHRoaXMuZ2VuZXJhdG9yLCB0aGlzLmNvbmZpZyk7XG4gICAgICAgIHRoaXMuY2h1bmtzLnNldChpbmRleCwgY2h1bmspO1xuXG4gICAgICAgIC8vIFRPRE86IGV2ZW50IGhlcmUsIGVnIG9uQ2h1bmtMb2FkXG4gICAgICAgIHJldHVybiBjaHVuaztcbiAgICB9XG5cbiAgICAvKipcbiAgICAgKiBTZW5kcyBhIHdvcmxkIGV2ZW50IHBhY2tldCB0byBhbGwgdGhlIHZpZXdlcnMgaW4gdGhlIHBvc2l0aW9uIGNodW5rLlxuICAgICAqIEBwYXJhbSB7VmVjdG9yM30gcG9zaXRpb24gLSB3b3JsZCBwb3NpdGlvbi5cbiAgICAgKiBAcGFyYW0ge251bWJlcn0gZXZlbnQgLSBldmVudCBpZGVudGlmaWVyLlxuICAgICAqIEBwYXJhbSB7bnVtYmVyfSBkYXRhIC0gZXZlbnQgZGF0YS5cbiAgICAgKi9cbiAgICBwdWJsaWMgYXN5bmMgc2VuZFdvcmxkRXZlbnQocG9zaXRpb246IFZlY3RvcjMgfCBudWxsLCBldmVudDogV29ybGRFdmVudCwgZGF0YTogbnVtYmVyKTogUHJvbWlzZTx2b2lkPiB7XG4gICAgICAgIGNvbnN0IHdvcmxkRXZlbnRQYWNrZXQgPSBuZXcgV29ybGRFdmVudFBhY2tldCgpO1xuICAgICAgICB3b3JsZEV2ZW50UGFja2V0LmV2ZW50SWQgPSBldmVudDtcbiAgICAgICAgLy93b3JsZEV2ZW50UGFja2V0LnBvc2l0aW9uID0gcG9zaXRpb247XG4gICAgICAgIHdvcmxkRXZlbnRQYWNrZXQuZGF0YSA9IGRhdGE7XG5cbiAgICAgICAgLy8gVE9ETzogTGltaXQgZGlzdGFuY2UuXG4gICAgICAgIGF3YWl0IFByb21pc2UuYWxsKHRoaXMuZ2V0UGxheWVycygpLm1hcCgocGxheWVyKSA9PiBwbGF5ZXIuZ2V0TmV0d29ya1Nlc3Npb24oKS5zZW5kKHdvcmxkRXZlbnRQYWNrZXQpKSk7XG4gICAgfVxuXG4gICAgLyoqXG4gICAgICogUmV0dXJucyBhIGNodW5rIGZyb20gYSBibG9jayBwb3NpdGlvbidzIHggYW5kIHogY29vcmRpbmF0ZXMuXG4gICAgICovXG4gICAgcHVibGljIGFzeW5jIGdldENodW5rQXQoeDogVmVjdG9yMyk6IFByb21pc2U8Q2h1bms+O1xuICAgIHB1YmxpYyBhc3luYyBnZXRDaHVua0F0KHg6IG51bWJlciwgejogbnVtYmVyKTogUHJvbWlzZTxDaHVuaz47XG4gICAgcHVibGljIGFzeW5jIGdldENodW5rQXQoeDogVmVjdG9yMyB8IG51bWJlciwgejogbnVtYmVyID0gMCk6IFByb21pc2U8Q2h1bms+IHtcbiAgICAgICAgaWYgKHggaW5zdGFuY2VvZiBWZWN0b3IzKSB7XG4gICAgICAgICAgICByZXR1cm4gdGhpcy5nZXRDaHVua0F0KHguZ2V0WCgpLCB4LmdldFooKSk7XG4gICAgICAgIH1cblxuICAgICAgICByZXR1cm4gdGhpcy5nZXRDaHVuayh4ID4+IDQsIHogPj4gNCk7XG4gICAgfVxuXG4gICAgLyoqXG4gICAgICogUmV0dXJucyB0aGUgd29ybGQgZGVmYXVsdCBzcGF3biBwb3NpdGlvbi5cbiAgICAgKi9cbiAgICBwdWJsaWMgYXN5bmMgZ2V0U3Bhd25Qb3NpdGlvbigpOiBQcm9taXNlPFZlY3RvcjM+IHtcbiAgICAgICAgaWYgKHRoaXMuc3Bhd24pIHJldHVybiB0aGlzLnNwYXduO1xuXG4gICAgICAgIGNvbnN0IHggPSAwO1xuICAgICAgICBjb25zdCB6ID0gMDsgLy8gVE9ETzogcmVwbGFjZSB3aXRoIGFjdHVhbCBkYXRhXG4gICAgICAgIGNvbnN0IGNodW5rID0gYXdhaXQgdGhpcy5nZXRDaHVua0F0KHgsIHopO1xuICAgICAgICBjb25zdCB5ID0gY2h1bmsuZ2V0SGlnaGVzdEJsb2NrQXQoeCwgeikgKyAxO1xuICAgICAgICByZXR1cm4gbmV3IFZlY3RvcjMoeiwgeSArIDIsIHopO1xuICAgIH1cblxuICAgIC8qKlxuICAgICAqIFNldCB0aGUgd29ybGQncyBzcGF3biBwb3NpdGlvbi5cbiAgICAgKiBAcGFyYW0ge1ZlY3RvcjN9IHBvcyAtIFRoZSBwb3NpdGlvbi5cbiAgICAgKi9cbiAgICBwdWJsaWMgc2V0U3Bhd25Qb3NpdGlvbihwb3M6IFZlY3RvcjMpIHtcbiAgICAgICAgdGhpcy5zcGF3biA9IHBvcztcbiAgICB9XG5cbiAgICAvLyBUT0RPOiBtb3ZlIHRoaXM/XG4gICAgcHVibGljIGFzeW5jIHVzZUl0ZW1PbihcbiAgICAgICAgaXRlbUluSGFuZDogSXRlbSB8IEJsb2NrIHwgbnVsbCxcbiAgICAgICAgYmxvY2tQb3NpdGlvbjogVmVjdG9yMyxcbiAgICAgICAgZmFjZTogbnVtYmVyLFxuICAgICAgICBjbGlja1Bvc2l0aW9uOiBWZWN0b3IzLFxuICAgICAgICBwbGF5ZXI6IFBsYXllclxuICAgICk6IFByb21pc2U8dm9pZD4ge1xuICAgICAgICBpZiAoaXRlbUluSGFuZCBpbnN0YW5jZW9mIEl0ZW0pIHJldHVybjsgLy8gVE9ET1xuXG4gICAgICAgIC8vIFRPRE86IGNoZWNrc1xuICAgICAgICAvLyBUT0RPOiBjYW5JbnRlcmFjdFxuXG4gICAgICAgIGNvbnN0IGJsb2NrID0gaXRlbUluSGFuZDsgLy8gVE9ETzogZ2V0IGJsb2NrIGZyb20gaXRlbUluSGFuZFxuICAgICAgICBjb25zdCBibG9ja0lkID0gKGF3YWl0IHRoaXMuZ2V0Q2h1bmtBdChibG9ja1Bvc2l0aW9uKSkuZ2V0QmxvY2soYmxvY2tQb3NpdGlvbik7XG5cbiAgICAgICAgY29uc3QgY2xpY2tlZEJsb2NrID0gdGhpcy5zZXJ2ZXIuZ2V0QmxvY2tNYW5hZ2VyKCkuZ2V0QmxvY2tCeUlkQW5kTWV0YShibG9ja0lkLmlkLCBibG9ja0lkLm1ldGEpO1xuXG4gICAgICAgIGlmICghYmxvY2sgfHwgIWNsaWNrZWRCbG9jaykgcmV0dXJuO1xuICAgICAgICBpZiAoY2xpY2tlZEJsb2NrLmdldE5hbWUoKSA9PT0gJ21pbmVjcmFmdDphaXInIHx8ICFibG9jay5jYW5CZVBsYWNlZCgpKSByZXR1cm47XG5cbiAgICAgICAgY29uc3QgcGxhY2VkUG9zaXRpb24gPSBuZXcgVmVjdG9yMyhibG9ja1Bvc2l0aW9uLmdldFgoKSwgYmxvY2tQb3NpdGlvbi5nZXRZKCksIGJsb2NrUG9zaXRpb24uZ2V0WigpKTtcblxuICAgICAgICAvLyBPbmx5IHNldCBjb3JyZWN0IGZhY2UgaWYgdGhlIGJsb2NrIGNhbid0IGJlIHJlcGxhY2VkXG4gICAgICAgIGlmICghY2xpY2tlZEJsb2NrLmNhbkJlUmVwbGFjZWQoKSlcbiAgICAgICAgICAgIHN3aXRjaCAoZmFjZSkge1xuICAgICAgICAgICAgICAgIGNhc2UgMDogLy8gQm90dG9tXG4gICAgICAgICAgICAgICAgICAgIHBsYWNlZFBvc2l0aW9uLnNldFkocGxhY2VkUG9zaXRpb24uZ2V0WSgpIC0gMSk7XG4gICAgICAgICAgICAgICAgICAgIGJyZWFrO1xuICAgICAgICAgICAgICAgIGNhc2UgMTogLy8gVG9wXG4gICAgICAgICAgICAgICAgICAgIHBsYWNlZFBvc2l0aW9uLnNldFkocGxhY2VkUG9zaXRpb24uZ2V0WSgpICsgMSk7XG4gICAgICAgICAgICAgICAgICAgIGJyZWFrO1xuICAgICAgICAgICAgICAgIGNhc2UgMjogLy8gRnJvbnRcbiAgICAgICAgICAgICAgICAgICAgcGxhY2VkUG9zaXRpb24uc2V0WihwbGFjZWRQb3NpdGlvbi5nZXRaKCkgLSAxKTtcbiAgICAgICAgICAgICAgICAgICAgYnJlYWs7XG4gICAgICAgICAgICAgICAgY2FzZSAzOiAvLyBCYWNrXG4gICAgICAgICAgICAgICAgICAgIHBsYWNlZFBvc2l0aW9uLnNldFoocGxhY2VkUG9zaXRpb24uZ2V0WigpICsgMSk7XG4gICAgICAgICAgICAgICAgICAgIGJyZWFrO1xuICAgICAgICAgICAgICAgIGNhc2UgNDogLy8gUmlnaHRcbiAgICAgICAgICAgICAgICAgICAgcGxhY2VkUG9zaXRpb24uc2V0WChwbGFjZWRQb3NpdGlvbi5nZXRYKCkgLSAxKTtcbiAgICAgICAgICAgICAgICAgICAgYnJlYWs7XG4gICAgICAgICAgICAgICAgY2FzZSA1OiAvLyBMZWZ0XG4gICAgICAgICAgICAgICAgICAgIHBsYWNlZFBvc2l0aW9uLnNldFgocGxhY2VkUG9zaXRpb24uZ2V0WCgpICsgMSk7XG4gICAgICAgICAgICAgICAgICAgIGJyZWFrO1xuICAgICAgICAgICAgICAgIGRlZmF1bHQ6XG4gICAgICAgICAgICAgICAgICAgIHRocm93IG5ldyBFcnJvcignSW52YWxpZCBGYWNlJyk7XG4gICAgICAgICAgICB9XG5cbiAgICAgICAgaWYgKGJsb2NrUG9zaXRpb24uZ2V0WSgpIDwgMCB8fCBibG9ja1Bvc2l0aW9uLmdldFkoKSA+IDI1NSkgcmV0dXJuO1xuXG4gICAgICAgIGNvbnN0IHN1Y2Nlc3M6IGJvb2xlYW4gPSBhd2FpdCBuZXcgUHJvbWlzZShhc3luYyAocmVzb2x2ZSkgPT4ge1xuICAgICAgICAgICAgdHJ5IHtcbiAgICAgICAgICAgICAgICBjb25zdCBjaHVuayA9IGF3YWl0IHRoaXMuZ2V0Q2h1bmtBdChwbGFjZWRQb3NpdGlvbi5nZXRYKCksIHBsYWNlZFBvc2l0aW9uLmdldFooKSk7XG5cbiAgICAgICAgICAgICAgICBjaHVuay5zZXRCbG9jayhwbGFjZWRQb3NpdGlvbi5nZXRYKCksIHBsYWNlZFBvc2l0aW9uLmdldFkoKSwgcGxhY2VkUG9zaXRpb24uZ2V0WigpLCBibG9jayk7XG4gICAgICAgICAgICAgICAgcmVzb2x2ZSh0cnVlKTtcbiAgICAgICAgICAgIH0gY2F0Y2ggKGVycm9yOiB1bmtub3duKSB7XG4gICAgICAgICAgICAgICAgcGxheWVyLmdldFNlcnZlcigpLmdldExvZ2dlcigpLndhcm4oYCR7cGxheWVyLmdldE5hbWUoKX0gZmFpbGVkIHRvIHBsYWNlIGJsb2NrIGR1ZSB0byAke2Vycm9yfWApO1xuICAgICAgICAgICAgICAgIGF3YWl0IHBsYXllci5zZW5kTWVzc2FnZSgoZXJyb3IgYXMgYW55KT8ubWVzc2FnZSk7XG5cbiAgICAgICAgICAgICAgICByZXNvbHZlKGZhbHNlKTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgfSk7XG5cbiAgICAgICAgaWYgKCFzdWNjZXNzKSB7XG4gICAgICAgICAgICBpZiAocGxhY2VkUG9zaXRpb24uZ2V0WSgpIDwgMCkgcmV0dXJuO1xuXG4gICAgICAgICAgICBjb25zdCBibG9ja1VwZGF0ZSA9IG5ldyBVcGRhdGVCbG9ja1BhY2tldCgpO1xuICAgICAgICAgICAgYmxvY2tVcGRhdGUueCA9IHBsYWNlZFBvc2l0aW9uLmdldFgoKTtcbiAgICAgICAgICAgIGJsb2NrVXBkYXRlLnkgPSBwbGFjZWRQb3NpdGlvbi5nZXRZKCk7XG4gICAgICAgICAgICBibG9ja1VwZGF0ZS56ID0gcGxhY2VkUG9zaXRpb24uZ2V0WigpO1xuICAgICAgICAgICAgYmxvY2tVcGRhdGUuYmxvY2tSdW50aW1lSWQgPSBCbG9ja01hcHBpbmdzLmdldFJ1bnRpbWVJZChjbGlja2VkQmxvY2suZ2V0TmFtZSgpKTtcbiAgICAgICAgICAgIHJldHVybjtcbiAgICAgICAgfVxuXG4gICAgICAgIGNvbnN0IHJ1bnRpbWVJZCA9IEJsb2NrTWFwcGluZ3MuZ2V0UnVudGltZUlkKGJsb2NrLmdldE5hbWUoKSk7XG5cbiAgICAgICAgY29uc3QgYmxvY2tVcGRhdGUgPSBuZXcgVXBkYXRlQmxvY2tQYWNrZXQoKTtcbiAgICAgICAgYmxvY2tVcGRhdGUueCA9IHBsYWNlZFBvc2l0aW9uLmdldFgoKTtcbiAgICAgICAgYmxvY2tVcGRhdGUueSA9IHBsYWNlZFBvc2l0aW9uLmdldFkoKTtcbiAgICAgICAgYmxvY2tVcGRhdGUueiA9IHBsYWNlZFBvc2l0aW9uLmdldFooKTtcbiAgICAgICAgYmxvY2tVcGRhdGUuYmxvY2tSdW50aW1lSWQgPSBydW50aW1lSWQ7XG5cbiAgICAgICAgYXdhaXQgUHJvbWlzZS5hbGwoXG4gICAgICAgICAgICB0aGlzLnNlcnZlclxuICAgICAgICAgICAgICAgIC5nZXRTZXNzaW9uTWFuYWdlcigpXG4gICAgICAgICAgICAgICAgLmdldEFsbFBsYXllcnMoKVxuICAgICAgICAgICAgICAgIC5tYXAoYXN5bmMgKG9ubGluZVBsYXllcikgPT5cbiAgICAgICAgICAgICAgICAgICAgb25saW5lUGxheWVyLmdldE5ldHdvcmtTZXNzaW9uKCkuZ2V0Q29ubmVjdGlvbigpLnNlbmREYXRhUGFja2V0KGJsb2NrVXBkYXRlKVxuICAgICAgICAgICAgICAgIClcbiAgICAgICAgKTtcblxuICAgICAgICBjb25zdCBwayA9IG5ldyBMZXZlbFNvdW5kRXZlbnRQYWNrZXQoKTtcbiAgICAgICAgcGsuc291bmQgPSA2OyAvLyBUT0RPOiBlbnVtXG5cbiAgICAgICAgcGsucG9zaXRpb25YID0gcGxhY2VkUG9zaXRpb24uZ2V0WCgpO1xuICAgICAgICBway5wb3NpdGlvblkgPSBwbGFjZWRQb3NpdGlvbi5nZXRZKCk7XG4gICAgICAgIHBrLnBvc2l0aW9uWiA9IHBsYWNlZFBvc2l0aW9uLmdldFooKTtcblxuICAgICAgICBway5leHRyYURhdGEgPSBydW50aW1lSWQ7IC8vIEluIHRoaXMgY2FzZSByZWZlcnMgdG8gYmxvY2sgcnVudGltZSBJZFxuICAgICAgICBway5kaXNhYmxlUmVsYXRpdmVWb2x1bWUgPSBmYWxzZTtcblxuICAgICAgICBhd2FpdCBQcm9taXNlLmFsbChcbiAgICAgICAgICAgIHBsYXllclxuICAgICAgICAgICAgICAgIC5nZXRXb3JsZCgpXG4gICAgICAgICAgICAgICAgLmdldFBsYXllcnMoKVxuICAgICAgICAgICAgICAgIC5tYXAoKHRhcmdldCkgPT4gdGFyZ2V0LmdldE5ldHdvcmtTZXNzaW9uKCkuc2VuZChwaykpXG4gICAgICAgICk7XG4gICAgfVxuXG4gICAgLyoqXG4gICAgICogU2VuZHMgdGhlIGN1cnJlbnQgdGltZSB0byBhbGwgcGxheWVycyBpbiB0aGUgd29ybGQuXG4gICAgICovXG4gICAgcHVibGljIGFzeW5jIHNlbmRUaW1lKCk6IFByb21pc2U8dm9pZD4ge1xuICAgICAgICAvLyBUcnkgdG8gc2VuZCBpdCBhdCB0aGUgc2FtZSB0aW1lIHRvIGFsbFxuICAgICAgICBhd2FpdCBQcm9taXNlLmFsbCh0aGlzLmdldFBsYXllcnMoKS5tYXAoKHBsYXllcikgPT4gcGxheWVyLmdldE5ldHdvcmtTZXNzaW9uKCkuc2VuZFRpbWUodGhpcy5nZXRUaWNrcygpKSkpO1xuICAgIH1cblxuICAgIC8qKlxuICAgICAqIEFkZHMgYW4gZW50aXR5IHRvIHRoZSBsZXZlbC5cbiAgICAgKiBAcGFyYW0ge0VudGl0eX0gZW50aXR5IC0gVGhlIGVudGl0eSB0byBhZGQuXG4gICAgICovXG4gICAgcHVibGljIGFzeW5jIGFkZEVudGl0eShlbnRpdHk6IEVudGl0eSk6IFByb21pc2U8dm9pZD4ge1xuICAgICAgICB0aGlzLmVudGl0aWVzLnNldChlbnRpdHkuZ2V0UnVudGltZUlkKCksIGVudGl0eSk7XG5cbiAgICAgICAgaWYgKCFlbnRpdHkuaXNQbGF5ZXIoKSkgYXdhaXQgZW50aXR5LnNlbmRTcGF3bigpO1xuICAgICAgICBlbHNlIGF3YWl0IFByb21pc2UuYWxsKHRoaXMuZ2V0RW50aXRpZXMoKS5tYXAoKGUpID0+IGUuc2VuZFNwYXduKGVudGl0eSBhcyBQbGF5ZXIpKSk7XG4gICAgfVxuXG4gICAgLyoqXG4gICAgICogUmVtb3ZlcyBhbiBlbnRpdHkgZnJvbSB0aGUgbGV2ZWwuXG4gICAgICogQHBhcmFtIHtFbnRpdHl9IGVudGl0eSAtIFRoZSBlbnRpdHkgdG8gcmVtb3ZlLlxuICAgICAqL1xuICAgIHB1YmxpYyBhc3luYyByZW1vdmVFbnRpdHkoZW50aXR5OiBFbnRpdHkpOiBQcm9taXNlPHZvaWQ+IHtcbiAgICAgICAgaWYgKCFlbnRpdHkuaXNQbGF5ZXIoKSkgYXdhaXQgZW50aXR5LnNlbmREZXNwYXduKCk7XG4gICAgICAgIGVsc2UgYXdhaXQgUHJvbWlzZS5hbGwodGhpcy5nZXRFbnRpdGllcygpLm1hcCgoZSkgPT4gZS5zZW5kRGVzcGF3bihlbnRpdHkgYXMgUGxheWVyKSkpO1xuXG4gICAgICAgIHRoaXMuZW50aXRpZXMuZGVsZXRlKGVudGl0eS5nZXRSdW50aW1lSWQoKSk7XG4gICAgfVxuXG4gICAgLyoqXG4gICAgICogR2V0IGFsbCBlbnRpdGllcyBpbiB0aGlzIHdvcmxkLlxuICAgICAqIEByZXR1cm5zIHtFbnRpdHlbXX0gdGhlIGVudGl0aWVzLlxuICAgICAqL1xuICAgIHB1YmxpYyBnZXRFbnRpdGllcygpOiBFbnRpdHlbXSB7XG4gICAgICAgIHJldHVybiBBcnJheS5mcm9tKHRoaXMuZW50aXRpZXMudmFsdWVzKCkpO1xuICAgIH1cbiAgICAvKipcbiAgICAgKiBHZXQgYWxsIHBsYXllcnMgaW4gdGhpcyB3b3JsZC5cbiAgICAgKiBAcmV0dXJucyB7UGxheWVyW119IHRoZSBwbGF5ZXJzLlxuICAgICAqL1xuICAgIHB1YmxpYyBnZXRQbGF5ZXJzKCk6IFBsYXllcltdIHtcbiAgICAgICAgcmV0dXJuICh0aGlzLmdldEVudGl0aWVzKCkuZmlsdGVyKChlKSA9PiBlLmlzUGxheWVyKCkpIGFzIFBsYXllcltdKS5maWx0ZXIoKHApID0+IHAuaXNPbmxpbmUoKSk7XG4gICAgfVxuXG4gICAgLyoqXG4gICAgICogU2F2ZXMgY2hhbmdlZCBjaHVua3MgaW50byBkaXNrLlxuICAgICAqL1xuICAgIHB1YmxpYyBhc3luYyBzYXZlQ2h1bmtzKCk6IFByb21pc2U8dm9pZD4ge1xuICAgICAgICBjb25zdCB0aW1lciA9IG5ldyBUaW1lcigpO1xuICAgICAgICB0aGlzLnNlcnZlci5nZXRMb2dnZXIoKS5pbmZvKGBTYXZpbmcgY2h1bmtzIGZvciBsZXZlbCAke3RoaXMuZ2V0Rm9ybWF0dGVkTmFtZSgpfWApO1xuXG4gICAgICAgIGF3YWl0IFByb21pc2UuYWxsKFxuICAgICAgICAgICAgQXJyYXkuZnJvbSh0aGlzLmNodW5rcy52YWx1ZXMoKSlcbiAgICAgICAgICAgICAgICAuZmlsdGVyKChjKSA9PiBjLmdldEhhc0NoYW5nZWQoKSlcbiAgICAgICAgICAgICAgICAubWFwKGFzeW5jIChjaHVuaykgPT4gdGhpcy5wcm92aWRlci53cml0ZUNodW5rKGNodW5rKSlcbiAgICAgICAgKTtcbiAgICAgICAgdGhpcy5zZXJ2ZXIuZ2V0TG9nZ2VyKCkudmVyYm9zZShgKHRvb2sgwqdlJHt0aW1lci5zdG9wKCl9IG1zwqdyKSFgKTtcbiAgICB9XG5cbiAgICBwdWJsaWMgYXN5bmMgc2F2ZSgpOiBQcm9taXNlPHZvaWQ+IHtcbiAgICAgICAgLy8gU2F2ZSBjaHVua3NcbiAgICAgICAgdGhpcy5nZXRQbGF5ZXJzKCkuZm9yRWFjaChhc3luYyAocGxheWVyKSA9PiB7XG4gICAgICAgICAgICBhd2FpdCB0aGlzLnNhdmVQbGF5ZXJEYXRhKHBsYXllcik7XG4gICAgICAgIH0pO1xuICAgICAgICBhd2FpdCB0aGlzLnNhdmVDaHVua3MoKTtcbiAgICAgICAgYXdhaXQgdGhpcy5zYXZlTGV2ZWxEYXRhKCk7XG4gICAgfVxuXG4gICAgcHVibGljIGdldEdhbWVydWxlTWFuYWdlcigpOiBHYW1lcnVsZU1hbmFnZXIge1xuICAgICAgICByZXR1cm4gdGhpcy5nYW1lcnVsZU1hbmFnZXI7XG4gICAgfVxuXG4gICAgcHVibGljIGdldFRpY2tzKCk6IG51bWJlciB7XG4gICAgICAgIHJldHVybiB0aGlzLmN1cnJlbnRUaWNrO1xuICAgIH1cblxuICAgIHB1YmxpYyBzZXRUaWNrcyh0aWNrOiBudW1iZXIpOiB2b2lkIHtcbiAgICAgICAgdGhpcy5jdXJyZW50VGljayA9IHRpY2s7XG4gICAgfVxuXG4gICAgcHVibGljIGdldFByb3ZpZGVyKCk6IGFueSB7XG4gICAgICAgIHJldHVybiB0aGlzLnByb3ZpZGVyO1xuICAgIH1cblxuICAgIC8vIFRoaXMgaXMgdXNlZCBmb3IgZXhhbXBsZSBpbiBzdGFydCBnYW1lIHBhY2tldFxuICAgIHB1YmxpYyBnZXRVVUlEKCk6IHN0cmluZyB7XG4gICAgICAgIHJldHVybiB0aGlzLnV1aWQ7XG4gICAgfVxuXG4gICAgcHVibGljIGdldE5hbWUoKTogc3RyaW5nIHtcbiAgICAgICAgcmV0dXJuIHRoaXMubmFtZTtcbiAgICB9XG4gICAgcHVibGljIGdldEZvcm1hdHRlZE5hbWUoKTogc3RyaW5nIHtcbiAgICAgICAgcmV0dXJuIGDCp2InJHt0aGlzLm5hbWV9Jy8ke3RoaXMuZ2VuZXJhdG9yLmNvbnN0cnVjdG9yLm5hbWV9wqdyYDtcbiAgICB9XG5cbiAgICBwdWJsaWMgZ2V0U2VlZCgpOiBudW1iZXIge1xuICAgICAgICByZXR1cm4gdGhpcy5zZWVkO1xuICAgIH1cblxuICAgIHByaXZhdGUgYXN5bmMgZ2V0TGV2ZWxEYXRhKCkge1xuICAgICAgICBjb25zdCBwYXRoID0gd2l0aEN3ZChXT1JMRFNfRk9MREVSX05BTUUsIHRoaXMubmFtZSwgTEVWRUxfREFUQV9GSUxFX05BTUUpO1xuICAgICAgICBpZiAoIWZzLmV4aXN0c1N5bmMocGF0aCkpIHJldHVybiB7fTtcblxuICAgICAgICB0cnkge1xuICAgICAgICAgICAgY29uc3QgcmF3ID0gYXdhaXQgZnMucHJvbWlzZXMucmVhZEZpbGUocGF0aCwgJ3V0Zi04Jyk7XG4gICAgICAgICAgICByZXR1cm4gcGFyc2VKU09ONShyYXcudG9TdHJpbmcoKSkgYXMgUGFydGlhbDxMZXZlbERhdGE+O1xuICAgICAgICB9IGNhdGNoIChlcnJvcjogYW55KSB7XG4gICAgICAgICAgICAvLyBTb21ldGhpbmcgd2VudCB3cm9uZyB3aGlsZSByZWFkaW5nIG9yIHBhcnNpbmcgdGhlIGxldmVsIGRhdGEuXG4gICAgICAgICAgICB0aGlzLnNlcnZlci5nZXRMb2dnZXIoKS5lcnJvcihlcnJvcik7XG4gICAgICAgIH1cblxuICAgICAgICByZXR1cm4ge307XG4gICAgfVxuICAgIHB1YmxpYyBhc3luYyBzYXZlTGV2ZWxEYXRhKCk6IFByb21pc2U8dm9pZD4ge1xuICAgICAgICBjb25zdCBkYXRhID0ge1xuICAgICAgICAgICAgc3Bhd246IGF3YWl0IHRoaXMuZ2V0U3Bhd25Qb3NpdGlvbigpLFxuICAgICAgICAgICAgZ2FtZXJ1bGVzOiBBcnJheS5mcm9tKHRoaXMuZ2V0R2FtZXJ1bGVNYW5hZ2VyKCkuZ2V0R2FtZXJ1bGVzKCkpLFxuICAgICAgICAgICAgZW50aXRpZXM6IHRoaXMuZ2V0RW50aXRpZXMoKVxuICAgICAgICAgICAgICAgIC5maWx0ZXIoKGVudGl0eSkgPT4gIWVudGl0eS5pc1BsYXllcigpICYmICFlbnRpdHkuaXNDb25zb2xlKCkpXG4gICAgICAgICAgICAgICAgLm1hcCgoZW50aXR5KSA9PiAoe1xuICAgICAgICAgICAgICAgICAgICB1dWlkOiBlbnRpdHkuZ2V0VVVJRCgpLFxuICAgICAgICAgICAgICAgICAgICB0eXBlOiBlbnRpdHkuZ2V0VHlwZSgpLFxuICAgICAgICAgICAgICAgICAgICBwb3NpdGlvbjoge1xuICAgICAgICAgICAgICAgICAgICAgICAgeDogZW50aXR5LmdldFgoKSxcbiAgICAgICAgICAgICAgICAgICAgICAgIHk6IGVudGl0eS5nZXRZKCksXG4gICAgICAgICAgICAgICAgICAgICAgICB6OiBlbnRpdHkuZ2V0WigpLFxuICAgICAgICAgICAgICAgICAgICAgICAgcGl0Y2g6IGVudGl0eS5waXRjaCxcbiAgICAgICAgICAgICAgICAgICAgICAgIHlhdzogZW50aXR5LnlhdyxcbiAgICAgICAgICAgICAgICAgICAgICAgIGhlYWRZYXc6IGVudGl0eS5oZWFkWWF3XG4gICAgICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICB9KSlcbiAgICAgICAgfTtcblxuICAgICAgICB0cnkge1xuICAgICAgICAgICAgYXdhaXQgZnMucHJvbWlzZXMud3JpdGVGaWxlKFxuICAgICAgICAgICAgICAgIC8vIEZJWE1FOiBUaGlzIG92ZXJ3cml0ZXMgY29tbWVudHMgaW4gdGhlIGZpbGUuXG4gICAgICAgICAgICAgICAgd2l0aEN3ZChXT1JMRFNfRk9MREVSX05BTUUsIHRoaXMubmFtZSwgTEVWRUxfREFUQV9GSUxFX05BTUUpLFxuICAgICAgICAgICAgICAgIEpTT04uc3RyaW5naWZ5KGRhdGEsIG51bGwsIDQpXG4gICAgICAgICAgICApO1xuICAgICAgICB9IGNhdGNoIChlcnJvcjogdW5rbm93bikge1xuICAgICAgICAgICAgdGhpcy5zZXJ2ZXIuZ2V0TG9nZ2VyKCkuZXJyb3IoYEZhaWxlZCB0byBzYXZlIGxldmVsIGRhdGFgKTtcbiAgICAgICAgICAgIHRoaXMuc2VydmVyLmdldExvZ2dlcigpLmVycm9yKGVycm9yKTtcbiAgICAgICAgfVxuICAgIH1cblxuICAgIC8qKlxuICAgICAqIEdldCB0aGUgcGxheWVyIGRhdGEgZm9yIGEgcGxheWVyLlxuICAgICAqIEBwYXJhbSB7UGxheWVyfSBwbGF5ZXIgLSBUaGUgcGxheWVyIHRvIGdldCB0aGUgZGF0YSBmb3IuXG4gICAgICogQHJldHVybnMge1Byb21pc2U8V29ybGRQbGF5ZXJEYXRhPn0gVGhlIHBsYXllciBkYXRhLlxuICAgICAqL1xuICAgIHB1YmxpYyBhc3luYyBnZXRQbGF5ZXJEYXRhKHBsYXllcjogUGxheWVyKTogUHJvbWlzZTxQYXJ0aWFsPFdvcmxkUGxheWVyRGF0YT4+IHtcbiAgICAgICAgdHJ5IHtcbiAgICAgICAgICAgIGNvbnN0IGZpbGVOYW1lID0gcGxheWVyLmdldFhVSUQoKTtcbiAgICAgICAgICAgIGlmICghZmlsZU5hbWUpIHtcbiAgICAgICAgICAgICAgICB0aHJvdyBuZXcgRXJyb3IoJ1BsYXllciBoYXMgbm8gWFVJRCcpO1xuICAgICAgICAgICAgfVxuXG4gICAgICAgICAgICBjb25zdCByYXcgPSBhd2FpdCBmcy5wcm9taXNlcy5yZWFkRmlsZShcbiAgICAgICAgICAgICAgICB3aXRoQ3dkKFdPUkxEU19GT0xERVJfTkFNRSwgdGhpcy5uYW1lLCAncGxheWVyZGF0YScsIGAke3BsYXllci5nZXRYVUlEKCkgfHwgcGxheWVyLmdldE5hbWUoKX0uanNvbmApLFxuICAgICAgICAgICAgICAgIHsgZmxhZzogJ3InLCBlbmNvZGluZzogJ3V0Zi04JyB9XG4gICAgICAgICAgICApO1xuICAgICAgICAgICAgcmV0dXJuIHBhcnNlSlNPTjUocmF3LnRvU3RyaW5nKCkpIGFzIFBhcnRpYWw8V29ybGRQbGF5ZXJEYXRhPjtcbiAgICAgICAgfSBjYXRjaCAoZXJyb3I6IHVua25vd24pIHtcbiAgICAgICAgICAgIHRoaXMuc2VydmVyLmdldExvZ2dlcigpLmRlYnVnKGBQbGF5ZXJEYXRhIGlzIG1pc3NpbmcgZm9yIHBsYXllciAke3BsYXllci5nZXRYVUlEKCl9YCk7XG4gICAgICAgICAgICB0aGlzLnNlcnZlci5nZXRMb2dnZXIoKS5lcnJvcihlcnJvcik7XG5cbiAgICAgICAgICAgIGNvbnN0IHNwYXduID0gYXdhaXQgdGhpcy5nZXRTcGF3blBvc2l0aW9uKCk7XG4gICAgICAgICAgICByZXR1cm4ge1xuICAgICAgICAgICAgICAgIGdhbWVtb2RlOiB0aGlzLnNlcnZlci5nZXRDb25maWcoKS5nZXRHYW1lbW9kZSgpLFxuICAgICAgICAgICAgICAgIHBvc2l0aW9uOiB7XG4gICAgICAgICAgICAgICAgICAgIHg6IHNwYXduLmdldFgoKSxcbiAgICAgICAgICAgICAgICAgICAgeTogc3Bhd24uZ2V0WSgpLFxuICAgICAgICAgICAgICAgICAgICB6OiBzcGF3bi5nZXRaKCksXG4gICAgICAgICAgICAgICAgICAgIHBpdGNoOiAwLFxuICAgICAgICAgICAgICAgICAgICB5YXc6IDAsXG4gICAgICAgICAgICAgICAgICAgIGhlYWRZYXc6IDBcbiAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICB9O1xuICAgICAgICB9XG4gICAgfVxuICAgIHB1YmxpYyBhc3luYyBzYXZlUGxheWVyRGF0YShwbGF5ZXI6IFBsYXllcik6IFByb21pc2U8dm9pZD4ge1xuICAgICAgICBjb25zdCBkYXRhID0ge1xuICAgICAgICAgICAgdXVpZDogcGxheWVyLmdldFVVSUQoKSxcbiAgICAgICAgICAgIHVzZXJuYW1lOiBwbGF5ZXIuZ2V0TmFtZSgpLFxuICAgICAgICAgICAgZ2FtZW1vZGU6IGdldEdhbWV0eXBlTmFtZShwbGF5ZXIuZ2FtZW1vZGUpLFxuICAgICAgICAgICAgcG9zaXRpb246IHtcbiAgICAgICAgICAgICAgICB4OiBwbGF5ZXIuZ2V0WCgpLFxuICAgICAgICAgICAgICAgIHk6IHBsYXllci5nZXRZKCksXG4gICAgICAgICAgICAgICAgejogcGxheWVyLmdldFooKSxcbiAgICAgICAgICAgICAgICBwaXRjaDogcGxheWVyLnBpdGNoLFxuICAgICAgICAgICAgICAgIHlhdzogcGxheWVyLnlhdyxcbiAgICAgICAgICAgICAgICBoZWFkWWF3OiBwbGF5ZXIuaGVhZFlhd1xuICAgICAgICAgICAgfVxuICAgICAgICB9IGFzIFdvcmxkUGxheWVyRGF0YTtcblxuICAgICAgICB0cnkge1xuICAgICAgICAgICAgYXdhaXQgZnMucHJvbWlzZXMud3JpdGVGaWxlKFxuICAgICAgICAgICAgICAgIC8vIEZJWE1FOiBUaGlzIG92ZXJ3cml0ZXMgY29tbWVudHMgaW4gdGhlIGZpbGUuXG4gICAgICAgICAgICAgICAgd2l0aEN3ZChXT1JMRFNfRk9MREVSX05BTUUsIHRoaXMubmFtZSwgJ3BsYXllcmRhdGEnLCBgJHtwbGF5ZXIuZ2V0WFVJRCgpIHx8IHBsYXllci5nZXROYW1lKCl9Lmpzb25gKSxcbiAgICAgICAgICAgICAgICBKU09OLnN0cmluZ2lmeShkYXRhLCBudWxsLCA0KSxcbiAgICAgICAgICAgICAgICB7IGZsYWc6ICd3KycsIGVuY29kaW5nOiAndXRmLTgnLCBmbHVzaDogdHJ1ZSB9XG4gICAgICAgICAgICApO1xuICAgICAgICB9IGNhdGNoIChlcnJvcjogdW5rbm93bikge1xuICAgICAgICAgICAgdGhpcy5zZXJ2ZXIuZ2V0TG9nZ2VyKCkuZXJyb3IoYEZhaWxlZCB0byBzYXZlIHBsYXllciBkYXRhYCk7XG4gICAgICAgICAgICB0aGlzLnNlcnZlci5nZXRMb2dnZXIoKS5lcnJvcihlcnJvcik7XG4gICAgICAgIH1cbiAgICB9XG5cbiAgICAvKipcbiAgICAgKiBAcmV0dXJucyB7U2VydmVyfSBUaGUgc2VydmVyIGluc3RhbmNlLlxuICAgICAqL1xuICAgIHB1YmxpYyBnZXRTZXJ2ZXIoKTogU2VydmVyIHtcbiAgICAgICAgcmV0dXJuIHRoaXMuc2VydmVyO1xuICAgIH1cbn1cbiJdLCJtYXBwaW5ncyI6Ijs7Ozs7Ozs7Ozs7Ozs7OztBQXVCQSxJQUFNLHVCQUF1QjtBQUM3QixJQUFNLHFCQUFxQjtBQXFDM0IsSUFBYSxRQUFiLE1BQXNDO0NBQ2xDLE9BQWdDLEtBQUssYUFBYTtDQUNsRDtDQUVBLDJCQUFpRCxJQUFJLElBQUk7Q0FDekQseUJBQThDLElBQUksSUFBSTtDQUN0RDtDQUNBLGNBQXNCO0NBQ3RCO0NBQ0E7Q0FDQTtDQUNBO0NBQ0E7Q0FDQSxRQUFnQztDQUVoQyxZQUFtQixFQUFFLE1BQU0sUUFBUSxVQUFVLE1BQU0sV0FBVyxVQUFxQjtFQUMvRSxLQUFLLE9BQU87RUFDWixLQUFLLFNBQVM7RUFDZCxLQUFLLFdBQVc7RUFDaEIsS0FBSyxrQkFBa0IsSUFBSSxnQkFBZ0IsTUFBTTtFQUNqRCxLQUFLLE9BQU87RUFDWixLQUFLLFlBQVk7RUFDakIsS0FBSyxTQUFTLFVBQVUsQ0FBQztFQUV6QixLQUFLLGdCQUFnQixZQUFZLFVBQVUsaUJBQWlCLE1BQU0sSUFBSTtFQUV0RSxJQUFJO0dBRUEsTUFBTSxPQUFPLFFBQVEsb0JBQW9CLEtBQUssTUFBTSxZQUFZO0dBQ2hFLElBQUksQ0FBQyxHQUFHLFdBQVcsSUFBSSxHQUFHLEdBQUcsVUFBVSxNQUFNLEVBQUUsV0FBVyxLQUFLLENBQUM7RUFDcEUsU0FBUyxPQUFnQjtHQUNyQixLQUFLLE9BQU8sVUFBVSxFQUFFLE1BQU0sc0NBQXNDLEtBQUssTUFBTTtHQUMvRSxLQUFLLE9BQU8sVUFBVSxFQUFFLE1BQU0sS0FBSztFQUN2QztD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