@jsprismarine/prismarine
Version:
Dedicated Minecraft Bedrock Edition server written in TypeScript
412 lines (411 loc) • 53.8 kB
JavaScript
"use strict";
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
const require_runtime = require("../_virtual/_rolldown/runtime.cjs.cjs");
const require_utils_cwd = require("../utils/cwd.cjs.cjs");
const require_utils_Timer = require("../utils/Timer.cjs.cjs");
const require_block_BlockMappings = require("../block/BlockMappings.cjs.cjs");
const require_item_Item = require("../item/Item.cjs.cjs");
const require_world_chunk_Chunk = require("./chunk/Chunk.cjs.cjs");
const require_world_GameruleManager = require("./GameruleManager.cjs.cjs");
const require_utils_UUID = require("../utils/UUID.cjs.cjs");
const require_network_packet_LevelSoundEventPacket = require("../network/packet/LevelSoundEventPacket.cjs.cjs");
const require_network_packet_UpdateBlockPacket = require("../network/packet/UpdateBlockPacket.cjs.cjs");
const require_network_packet_WorldEventPacket = require("../network/packet/WorldEventPacket.cjs.cjs");
const require_entity_Entities = require("../entity/Entities.cjs.cjs");
let node_fs = require("node:fs");
node_fs = require_runtime.__toESM(node_fs, 1);
let confbox = require("confbox");
let _jsprismarine_math = require("@jsprismarine/math");
let _jsprismarine_minecraft = require("@jsprismarine/minecraft");
//#region src/world/World.ts
var LEVEL_DATA_FILE_NAME = "level.json";
var WORLDS_FOLDER_NAME = "worlds";
var World = class {
uuid = require_utils_UUID.default.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 require_world_GameruleManager.default(server);
this.seed = seed;
this.generator = generator;
this.config = config ?? {};
this.gameruleManager.setGamerule(require_world_GameruleManager.GameRules.ShowCoordinates, true, true);
try {
const path = require_utils_cwd.withCwd(WORLDS_FOLDER_NAME, this.name, "playerdata");
if (!node_fs.default.existsSync(path)) node_fs.default.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(_jsprismarine_math.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(require_entity_Entities.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 require_utils_Timer.default();
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 = require_world_chunk_Chunk.default.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 = require_world_chunk_Chunk.default.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 require_network_packet_WorldEventPacket.default();
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 _jsprismarine_math.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 _jsprismarine_math.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 require_item_Item.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 _jsprismarine_math.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 require_network_packet_UpdateBlockPacket.default();
blockUpdate.x = placedPosition.getX();
blockUpdate.y = placedPosition.getY();
blockUpdate.z = placedPosition.getZ();
blockUpdate.blockRuntimeId = require_block_BlockMappings.BlockMappings.getRuntimeId(clickedBlock.getName());
return;
}
const runtimeId = require_block_BlockMappings.BlockMappings.getRuntimeId(block.getName());
const blockUpdate = new require_network_packet_UpdateBlockPacket.default();
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 require_network_packet_LevelSoundEventPacket.default();
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 require_utils_Timer.default();
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 = require_utils_cwd.withCwd(WORLDS_FOLDER_NAME, this.name, LEVEL_DATA_FILE_NAME);
if (!node_fs.default.existsSync(path)) return {};
try {
return (0, confbox.parseJSON5)((await node_fs.default.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 node_fs.default.promises.writeFile(require_utils_cwd.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 (0, confbox.parseJSON5)((await node_fs.default.promises.readFile(require_utils_cwd.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: (0, _jsprismarine_minecraft.getGametypeName)(player.gamemode),
position: {
x: player.getX(),
y: player.getY(),
z: player.getZ(),
pitch: player.pitch,
yaw: player.yaw,
headYaw: player.headYaw
}
};
try {
await node_fs.default.promises.writeFile(require_utils_cwd.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
exports.World = World;
//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiV29ybGQuY2pzLmNqcyIsIm5hbWVzIjpbXSwic291cmNlcyI6WyIuLi8uLi9zcmMvd29ybGQvV29ybGQudHMiXSwic291cmNlc0NvbnRlbnQiOlsiaW1wb3J0IEdhbWVydWxlTWFuYWdlciwgeyBHYW1lUnVsZXMgfSBmcm9tICcuL0dhbWVydWxlTWFuYWdlcic7XG5cbmltcG9ydCBmcyBmcm9tICdub2RlOmZzJztcblxuaW1wb3J0IHsgcGFyc2VKU09ONSB9IGZyb20gJ2NvbmZib3gnO1xuXG5pbXBvcnQgeyBWZWN0b3IzIH0gZnJvbSAnQGpzcHJpc21hcmluZS9tYXRoJztcbmltcG9ydCB7IGdldEdhbWV0eXBlTmFtZSB9IGZyb20gJ0Bqc3ByaXNtYXJpbmUvbWluZWNyYWZ0JztcbmltcG9ydCB0eXBlIHsgQmxvY2ssIFBsYXllciwgU2VydmVyLCBTZXJ2aWNlIH0gZnJvbSAnLi4vJztcbmltcG9ydCB7IFRpbWVyLCBVVUlEIH0gZnJvbSAnLi4vJztcbmltcG9ydCB7IEJsb2NrTWFwcGluZ3MgfSBmcm9tICcuLi9ibG9jay9CbG9ja01hcHBpbmdzJztcbmltcG9ydCAqIGFzIEVudGl0aWVzIGZyb20gJy4uL2VudGl0eS9FbnRpdGllcyc7XG5pbXBvcnQgdHlwZSB7IEVudGl0eSB9IGZyb20gJy4uL2VudGl0eS9FbnRpdHknO1xuaW1wb3J0IHsgSXRlbSB9IGZyb20gJy4uL2l0ZW0vSXRlbSc7XG5pbXBvcnQgTGV2ZWxTb3VuZEV2ZW50UGFja2V0IGZyb20gJy4uL25ldHdvcmsvcGFja2V0L0xldmVsU291bmRFdmVudFBhY2tldCc7XG5pbXBvcnQgVXBkYXRlQmxvY2tQYWNrZXQgZnJvbSAnLi4vbmV0d29yay9wYWNrZXQvVXBkYXRlQmxvY2tQYWNrZXQnO1xuaW1wb3J0IHR5cGUgeyBXb3JsZEV2ZW50IH0gZnJvbSAnLi4vbmV0d29yay9wYWNrZXQvV29ybGRFdmVudFBhY2tldCc7XG5pbXBvcnQgV29ybGRFdmVudFBhY2tldCBmcm9tICcuLi9uZXR3b3JrL3BhY2tldC9Xb3JsZEV2ZW50UGFja2V0JztcbmltcG9ydCB7IHdpdGhDd2QgfSBmcm9tICcuLi91dGlscy9jd2QnO1xuaW1wb3J0IHR5cGUgeyBHZW5lcmF0b3IgfSBmcm9tICcuL0dlbmVyYXRvcic7XG5pbXBvcnQgQ2h1bmsgZnJvbSAnLi9jaHVuay9DaHVuayc7XG5pbXBvcnQgdHlwZSBCYXNlUHJvdmlkZXIgZnJvbSAnLi9wcm92aWRlcnMvQmFzZVByb3ZpZGVyJztcblxuY29uc3QgTEVWRUxfREFUQV9GSUxFX05BTUUgPSAnbGV2ZWwuanNvbic7XG5jb25zdCBXT1JMRFNfRk9MREVSX05BTUUgPSAnd29ybGRzJztcblxuZXhwb3J0IGludGVyZmFjZSBXb3JsZERhdGEge1xuICAgIG5hbWU6IHN0cmluZztcbiAgICBwYXRoOiBzdHJpbmc7XG4gICAgc2VydmVyOiBTZXJ2ZXI7XG4gICAgcHJvdmlkZXI6IEJhc2VQcm92aWRlcjtcbiAgICBzZWVkOiBudW1iZXI7XG4gICAgZ2VuZXJhdG9yOiBHZW5lcmF0b3I7XG4gICAgY29uZmlnPzogYW55O1xufVxuXG5leHBvcnQgaW50ZXJmYWNlIExldmVsRGF0YSB7XG4gICAgc3Bhd246IHsgeDogbnVtYmVyOyB5OiBudW1iZXI7IHo6IG51bWJlciB9IHwgdW5kZWZpbmVkO1xuICAgIGdhbWVSdWxlczogQXJyYXk8W3N0cmluZywgYW55XT47XG4gICAgZW50aXRpZXM6IEFycmF5PHtcbiAgICAgICAgdXVpZDogc3RyaW5nO1xuICAgICAgICB0eXBlOiBzdHJpbmc7XG4gICAgICAgIHBvc2l0aW9uOiB7XG4gICAgICAgICAgICB4OiBudW1iZXI7XG4gICAgICAgICAgICB5OiBudW1iZXI7XG4gICAgICAgICAgICB6OiBudW1iZXI7XG4gICAgICAgIH07XG4gICAgfT47XG59XG5leHBvcnQgaW50ZXJmYWNlIFdvcmxkUGxheWVyRGF0YSB7XG4gICAgZ2FtZW1vZGU6IHN0cmluZztcbiAgICBwb3NpdGlvbjoge1xuICAgICAgICB4OiBudW1iZXI7XG4gICAgICAgIHk6IG51bWJlcjtcbiAgICAgICAgejogbnVtYmVyO1xuICAgICAgICBwaXRjaDogbnVtYmVyO1xuICAgICAgICB5YXc6IG51bWJlcjtcbiAgICAgICAgaGVhZFlhdzogbnVtYmVyO1xuICAgIH07XG59XG5cbmV4cG9ydCBjbGFzcyBXb3JsZCBpbXBsZW1lbnRzIFNlcnZpY2Uge1xuICAgIHByaXZhdGUgcmVhZG9ubHkgdXVpZDogc3RyaW5nID0gVVVJRC5yYW5kb21TdHJpbmcoKTtcbiAgICBwcml2YXRlIG5hbWU6IHN0cmluZztcblxuICAgIHByaXZhdGUgcmVhZG9ubHkgZW50aXRpZXM6IE1hcDxiaWdpbnQsIEVudGl0eT4gPSBuZXcgTWFwKCk7XG4gICAgcHJpdmF0ZSByZWFkb25seSBjaHVua3M6IE1hcDxiaWdpbnQsIENodW5rPiA9IG5ldyBNYXAoKTtcbiAgICBwcml2YXRlIHJlYWRvbmx5IGdhbWVydWxlTWFuYWdlcjogR2FtZXJ1bGVNYW5hZ2VyO1xuICAgIHByaXZhdGUgY3VycmVudFRpY2sgPSAwO1xuICAgIHByaXZhdGUgcmVhZG9ubHkgcHJvdmlkZXI6IEJhc2VQcm92aWRlcjtcbiAgICBwcml2YXRlIHJlYWRvbmx5IHNlcnZlcjogU2VydmVyO1xuICAgIHByaXZhdGUgcmVhZG9ubHkgc2VlZDogbnVtYmVyO1xuICAgIHByaXZhdGUgcmVhZG9ubHkgZ2VuZXJhdG9yOiBHZW5lcmF0b3I7XG4gICAgcHJpdmF0ZSByZWFkb25seSBjb25maWc6IE9iamVjdDtcbiAgICBwcml2YXRlIHNwYXduOiBWZWN0b3IzIHwgbnVsbCA9IG51bGw7XG5cbiAgICBwdWJsaWMgY29uc3RydWN0b3IoeyBuYW1lLCBzZXJ2ZXIsIHByb3ZpZGVyLCBzZWVkLCBnZW5lcmF0b3IsIGNvbmZpZyB9OiBXb3JsZERhdGEpIHtcbiAgICAgICAgdGhpcy5uYW1lID0gbmFtZTtcbiAgICAgICAgdGhpcy5zZXJ2ZXIgPSBzZXJ2ZXI7XG4gICAgICAgIHRoaXMucHJvdmlkZXIgPSBwcm92aWRlcjtcbiAgICAgICAgdGhpcy5nYW1lcnVsZU1hbmFnZXIgPSBuZXcgR2FtZXJ1bGVNYW5hZ2VyKHNlcnZlcik7XG4gICAgICAgIHRoaXMuc2VlZCA9IHNlZWQ7XG4gICAgICAgIHRoaXMuZ2VuZXJhdG9yID0gZ2VuZXJhdG9yO1xuICAgICAgICB0aGlzLmNvbmZpZyA9IGNvbmZpZyA/PyB7fTtcblxuICAgICAgICB0aGlzLmdhbWVydWxlTWFuYWdlci5zZXRHYW1lcnVsZShHYW1lUnVsZXMuU2hvd0Nvb3JkaW5hdGVzLCB0cnVlLCB0cnVlKTtcblxuICAgICAgICB0cnkge1xuICAgICAgICAgICAgLy8gQ3JlYXRlIGZvbGRlcnMgaWYgdGhleSBkb24ndCBleGlzdC5cbiAgICAgICAgICAgIGNvbnN0IHBhdGggPSB3aXRoQ3dkKFdPUkxEU19GT0xERVJfTkFNRSwgdGhpcy5uYW1lLCAncGxheWVyZGF0YScpO1xuICAgICAgICAgICAgaWYgKCFmcy5leGlzdHNTeW5jKHBhdGgpKSBmcy5ta2RpclN5bmMocGF0aCwgeyByZWN1cnNpdmU6IHRydWUgfSk7XG4gICAgICAgIH0gY2F0Y2ggKGVycm9yOiB1bmtub3duKSB7XG4gICAgICAgICAgICB0aGlzLnNlcnZlci5nZXRMb2dnZXIoKS5lcnJvcihgRmFpbGVkIHRvIGNyZWF0ZSB3b3JsZCBmb2xkZXJzIGZvciAke3RoaXMubmFtZX1gKTtcbiAgICAgICAgICAgIHRoaXMuc2VydmVyLmdldExvZ2dlcigpLmVycm9yKGVycm9yKTtcbiAgICAgICAgfVxuICAgIH1cblxuICAgIC8qKlxuICAgICAqIE9uIGVuYWJsZSBob29rLlxuICAgICAqIEBncm91cCBMaWZlY3ljbGVcbiAgICAgKi9cbiAgICBwdWJsaWMgYXN5bmMgZW5hYmxlKCk6IFByb21pc2U8dm9pZD4ge1xuICAgICAgICB0aGlzLnNlcnZlci5vbigndGljaycsIGFzeW5jIChldnQpID0+IHRoaXMudXBkYXRlKGV2dC5nZXRUaWNrKCkpKTtcblxuICAgICAgICBjb25zdCBsZXZlbCA9IGF3YWl0IHRoaXMuZ2V0TGV2ZWxEYXRhKCk7XG4gICAgICAgIGlmIChsZXZlbC5zcGF3bikgdGhpcy5zZXRTcGF3blBvc2l0aW9uKFZlY3RvcjMuZnJvbU9iamVjdChsZXZlbC5zcGF3bikpO1xuICAgICAgICBpZiAobGV2ZWwuZ2FtZVJ1bGVzKSB7XG4gICAgICAgICAgICBsZXZlbC5nYW1lUnVsZXMuZm9yRWFjaCgoW25hbWUsIFt2YWx1ZSwgZWRpdGFibGVdXSkgPT5cbiAgICAgICAgICAgICAgICB0aGlzLmdhbWVydWxlTWFuYWdlci5zZXRHYW1lcnVsZShuYW1lLCB2YWx1ZSwgZWRpdGFibGUpXG4gICAgICAgICAgICApO1xuICAgICAgICB9XG4gICAgICAgIGlmIChsZXZlbC5lbnRpdGllcykge1xuICAgICAgICAgICAgZm9yIChjb25zdCBlbnRpdHlEYXRhIG9mIGxldmVsLmVudGl0aWVzKSB7XG4gICAgICAgICAgICAgICAgY29uc3QgRW50aXR5ID0gQXJyYXkuZnJvbShPYmplY3QudmFsdWVzKEVudGl0aWVzKSkuZmluZCgoZSkgPT4gZS5NT0JfSUQgPT09IGVudGl0eURhdGEudHlwZSk7XG4gICAgICAgICAgICAgICAgaWYgKCFFbnRpdHkpIHtcbiAgICAgICAgICAgICAgICAgICAgdGhpcy5zZXJ2ZXIuZ2V0TG9nZ2VyKCkud2FybihgRW50aXR5IHR5cGUgJHtlbnRpdHlEYXRhLnR5cGV9IG5vdCBmb3VuZGApO1xuICAgICAgICAgICAgICAgICAgICBjb250aW51ZTtcbiAgICAgICAgICAgICAgICB9XG5cbiAgICAgICAgICAgICAgICBhd2FpdCB0aGlzLmFkZEVudGl0eShcbiAgICAgICAgICAgICAgICAgICAgbmV3IEVudGl0eSh7XG4gICAgICAgICAgICAgICAgICAgICAgICB3b3JsZDogdGhpcyxcbiAgICAgICAgICAgICAgICAgICAgICAgIHV1aWQ6IGVudGl0eURhdGEudXVpZCxcbiAgICAgICAgICAgICAgICAgICAgICAgIC4uLmVudGl0eURhdGEucG9zaXRpb24sXG4gICAgICAgICAgICAgICAgICAgICAgICBzZXJ2ZXI6IHRoaXMuc2VydmVyXG4gICAgICAgICAgICAgICAgICAgIH0pXG4gICAgICAgICAgICAgICAgKTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgfVxuXG4gICAgICAgIHRoaXMucHJvdmlkZXIuc2V0V29ybGQodGhpcyk7XG4gICAgICAgIGF3YWl0IHRoaXMucHJvdmlkZXIuZW5hYmxlKCk7XG5cbiAgICAgICAgdGhpcy5zZXJ2ZXIuZ2V0TG9nZ2VyKCkuaW5mbyhgUHJlcGFyaW5nIHN0YXJ0IHJlZ2lvbiBmb3IgZGltZW5zaW9uICR7dGhpcy5nZXRGb3JtYXR0ZWROYW1lKCl9YCk7XG4gICAgICAgIGNvbnN0IGNodW5rc1RvTG9hZDogQXJyYXk8UHJvbWlzZTxDaHVuaz4+ID0gW107XG4gICAgICAgIGNvbnN0IHRpbWVyID0gbmV3IFRpbWVyKCk7XG5cbiAgICAgICAgY29uc3Qgc2l6ZSA9IHRoaXMuc2VydmVyLmdldENvbmZpZygpLmdldFZpZXdEaXN0YW5jZSgpICogNTtcbiAgICAgICAgZm9yIChsZXQgeCA9IDA7IHggPCBzaXplOyB4KyspIHtcbiAgICAgICAgICAgIGZvciAobGV0IHogPSAwOyB6IDwgc2l6ZTsgeisrKSB7XG4gICAgICAgICAgICAgICAgY2h1bmtzVG9Mb2FkLnB1c2godGhpcy5sb2FkQ2h1bmsoeCwgeiwgdHJ1ZSkpO1xuICAgICAgICAgICAgfVxuICAgICAgICB9XG5cbiAgICAgICAgYXdhaXQgUHJvbWlzZS5hbGwoY2h1bmtzVG9Mb2FkKTtcbiAgICAgICAgdGhpcy5zZXJ2ZXIuZ2V0TG9nZ2VyKCkudmVyYm9zZShgKHRvb2sgwqdlJHt0aW1lci5zdG9wKCl9IG1zwqdyKWApO1xuICAgIH1cblxuICAgIC8qKlxuICAgICAqIE9uIGRpc2FibGUgaG9vay5cbiAgICAgKiBAZ3JvdXAgTGlmZWN5Y2xlXG4gICAgICovXG4gICAgcHVibGljIGFzeW5jIGRpc2FibGUoKTogUHJvbWlzZTx2b2lkPiB7XG4gICAgICAgIGF3YWl0IHRoaXMuc2F2ZSgpO1xuICAgICAgICBhd2FpdCB0aGlzLnByb3ZpZGVyLmRpc2FibGUoKTtcbiAgICB9XG5cbiAgICBwdWJsaWMgZ2V0R2VuZXJhdG9yKCk6IEdlbmVyYXRvciB7XG4gICAgICAgIHJldHVybiB0aGlzLmdlbmVyYXRvcjtcbiAgICB9XG5cbiAgICAvKipcbiAgICAgKiBDYWxsZWQgZXZlcnkgdGljay5cbiAgICAgKlxuICAgICAqIEBwYXJhbSB0aWNrXG4gICAgICovXG4gICAgcHVibGljIGFzeW5jIHVwZGF0ZSh0aWNrOiBudW1iZXIpOiBQcm9taXNlPHZvaWQ+IHtcbiAgICAgICAgLy8gVE9ETzogdGljayBjaHVua3NcblxuICAgICAgICAvLyBDb250aW51ZSB3b3JsZCB0aW1lIHRpY2tzXG4gICAgICAgIHRoaXMuY3VycmVudFRpY2srKztcblxuICAgICAgICAvLyBBdXRvIHNhdmUgZXZlcnkgMiBtaW51dGVzXG4gICAgICAgIGlmICh0aGlzLmN1cnJlbnRUaWNrIC8gMjAgPT09IDEyMCkge1xuICAgICAgICAgICAgYXdhaXQgdGhpcy5zYXZlKCk7XG4gICAgICAgIH1cblxuICAgICAgICBhd2FpdCBQcm9taXNlLmFsbCh0aGlzLmdldEVudGl0aWVzKCkubWFwKChlbnRpdHkpID0+IGVudGl0eS51cGRhdGUodGljaykpKTtcbiAgICAgICAgYXdhaXQgdGhpcy5zZW5kVGltZSgpO1xuICAgIH1cblxuICAgIC8qKlxuICAgICAqIFJldHVybnMgYSBibG9jayBpbnN0YW5jZSBpbiB0aGUgZ2l2ZW4gd29ybGQgcG9zaXRpb24uXG4gICAgICogQHBhcmFtIHtudW1iZXJ9IHggLSBibG9jayB4XG4gICAgICogQHBhcmFtIHtudW1iZXJ9IHkgLSBibG9jayB5XG4gICAgICogQHBhcmFtIHtudW1iZXJ9IHogLSBibG9jayB6XG4gICAgICogQHBhcmFtIHtudW1iZXJ9IFtsYXllcj0wXSAtIGJsb2NrIHN0b3JhZ2UgbGF5ZXIgKDAgZm9yIGJsb2NrcywgMSBmb3IgbGlxdWlkcylcbiAgICAgKi9cbiAgICBwdWJsaWMgYXN5bmMgZ2V0QmxvY2soeDogbnVtYmVyLCB5OiBudW1iZXIsIHo6IG51bWJlciwgbGF5ZXIgPSAwKTogUHJvbWlzZTxCbG9jaz4ge1xuICAgICAgICBjb25zdCBibG9ja0lkID0gKGF3YWl0IHRoaXMuZ2V0Q2h1bmtBdCh4LCB6KSkuZ2V0QmxvY2soeCwgeSwgeiwgbGF5ZXIpO1xuICAgICAgICBjb25zdCBibG9jayA9IHRoaXMuc2VydmVyLmdldEJsb2NrTWFuYWdlcigpLmdldEJsb2NrQnlJZEFuZE1ldGEoYmxvY2tJZC5pZCwgYmxvY2tJZC5tZXRhKTtcblxuICAgICAgICBpZiAoIWJsb2NrKSByZXR1cm4gdGhpcy5zZXJ2ZXIuZ2V0QmxvY2tNYW5hZ2VyKCkuZ2V0QmxvY2soJ21pbmVjcmFmdDphaXInKTtcbiAgICAgICAgcmV0dXJuIGJsb2NrO1xuICAgIH1cblxuICAgIC8qKlxuICAgICAqIFJldHVybnMgdGhlIGNodW5rIGluIHRoZSBzcGVjaWZpZXMgeCBhbmQgeiwgaWYgdGhlIGNodW5rIGRvZXNuJ3QgZXhpc3RzXG4gICAgICogaXQgaXMgZ2VuZXJhdGVkLlxuICAgICAqL1xuICAgIHB1YmxpYyBhc3luYyBnZXRDaHVuayhjeDogbnVtYmVyLCBjejogbnVtYmVyKTogUHJvbWlzZTxDaHVuaz4ge1xuICAgICAgICBjb25zdCBpbmRleCA9IENodW5rLnBhY2tYWihjeCwgY3opO1xuICAgICAgICBpZiAoIXRoaXMuY2h1bmtzLmhhcyhpbmRleCkpIHJldHVybiB0aGlzLmxvYWRDaHVuayhjeCwgY3opO1xuXG4gICAgICAgIHJldHVybiB0aGlzLmNodW5rcy5nZXQoaW5kZXgpITtcbiAgICB9XG5cbiAgICAvKipcbiAgICAgKiBMb2FkcyBhIGNodW5rIGluIGEgZ2l2ZW4geCBhbmQgeiBhbmQgcmV0dXJucyBpdHMuXG4gICAgICogQHBhcmFtIHtudW1iZXJ9IHggLSB4IGNvb3JkaW5hdGUuXG4gICAgICogQHBhcmFtIHtudW1iZXJ9IHogLSB6IGNvb3JkaW5hdGUuXG4gICAgICovXG4gICAgcHVibGljIGFzeW5jIGxvYWRDaHVuayh4OiBudW1iZXIsIHo6IG51bWJlciwgX2lnbm9yZVdhcm4/OiBib29sZWFuKTogUHJvbWlzZTxDaHVuaz4ge1xuICAgICAgICBjb25zdCBpbmRleCA9IENodW5rLnBhY2tYWih4LCB6KTtcbiAgICAgICAgLy8gVHJ5IC0gY2F0Y2ggZm9yIHByb3ZpZGVyIGVycm9yc1xuICAgICAgICBjb25zdCBjaHVuayA9IGF3YWl0IHRoaXMucHJvdmlkZXIucmVhZENodW5rKHgsIHosIHRoaXMuc2VlZCwgdGhpcy5nZW5lcmF0b3IsIHRoaXMuY29uZmlnKTtcbiAgICAgICAgdGhpcy5jaHVua3Muc2V0KGluZGV4LCBjaHVuayk7XG5cbiAgICAgICAgLy8gVE9ETzogZXZlbnQgaGVyZSwgZWcgb25DaHVua0xvYWRcbiAgICAgICAgcmV0dXJuIGNodW5rO1xuICAgIH1cblxuICAgIC8qKlxuICAgICAqIFNlbmRzIGEgd29ybGQgZXZlbnQgcGFja2V0IHRvIGFsbCB0aGUgdmlld2VycyBpbiB0aGUgcG9zaXRpb24gY2h1bmsuXG4gICAgICogQHBhcmFtIHtWZWN0b3IzfSBwb3NpdGlvbiAtIHdvcmxkIHBvc2l0aW9uLlxuICAgICAqIEBwYXJhbSB7bnVtYmVyfSBldmVudCAtIGV2ZW50IGlkZW50aWZpZXIuXG4gICAgICogQHBhcmFtIHtudW1iZXJ9IGRhdGEgLSBldmVudCBkYXRhLlxuICAgICAqL1xuICAgIHB1YmxpYyBhc3luYyBzZW5kV29ybGRFdmVudChwb3NpdGlvbjogVmVjdG9yMyB8IG51bGwsIGV2ZW50OiBXb3JsZEV2ZW50LCBkYXRhOiBudW1iZXIpOiBQcm9taXNlPHZvaWQ+IHtcbiAgICAgICAgY29uc3Qgd29ybGRFdmVudFBhY2tldCA9IG5ldyBXb3JsZEV2ZW50UGFja2V0KCk7XG4gICAgICAgIHdvcmxkRXZlbnRQYWNrZXQuZXZlbnRJZCA9IGV2ZW50O1xuICAgICAgICAvL3dvcmxkRXZlbnRQYWNrZXQucG9zaXRpb24gPSBwb3NpdGlvbjtcbiAgICAgICAgd29ybGRFdmVudFBhY2tldC5kYXRhID0gZGF0YTtcblxuICAgICAgICAvLyBUT0RPOiBMaW1pdCBkaXN0YW5jZS5cbiAgICAgICAgYXdhaXQgUHJvbWlzZS5hbGwodGhpcy5nZXRQbGF5ZXJzKCkubWFwKChwbGF5ZXIpID0+IHBsYXllci5nZXROZXR3b3JrU2Vzc2lvbigpLnNlbmQod29ybGRFdmVudFBhY2tldCkpKTtcbiAgICB9XG5cbiAgICAvKipcbiAgICAgKiBSZXR1cm5zIGEgY2h1bmsgZnJvbSBhIGJsb2NrIHBvc2l0aW9uJ3MgeCBhbmQgeiBjb29yZGluYXRlcy5cbiAgICAgKi9cbiAgICBwdWJsaWMgYXN5bmMgZ2V0Q2h1bmtBdCh4OiBWZWN0b3IzKTogUHJvbWlzZTxDaHVuaz47XG4gICAgcHVibGljIGFzeW5jIGdldENodW5rQXQoeDogbnVtYmVyLCB6OiBudW1iZXIpOiBQcm9taXNlPENodW5rPjtcbiAgICBwdWJsaWMgYXN5bmMgZ2V0Q2h1bmtBdCh4OiBWZWN0b3IzIHwgbnVtYmVyLCB6OiBudW1iZXIgPSAwKTogUHJvbWlzZTxDaHVuaz4ge1xuICAgICAgICBpZiAoeCBpbnN0YW5jZW9mIFZlY3RvcjMpIHtcbiAgICAgICAgICAgIHJldHVybiB0aGlzLmdldENodW5rQXQoeC5nZXRYKCksIHguZ2V0WigpKTtcbiAgICAgICAgfVxuXG4gICAgICAgIHJldHVybiB0aGlzLmdldENodW5rKHggPj4gNCwgeiA+PiA0KTtcbiAgICB9XG5cbiAgICAvKipcbiAgICAgKiBSZXR1cm5zIHRoZSB3b3JsZCBkZWZhdWx0IHNwYXduIHBvc2l0aW9uLlxuICAgICAqL1xuICAgIHB1YmxpYyBhc3luYyBnZXRTcGF3blBvc2l0aW9uKCk6IFByb21pc2U8VmVjdG9yMz4ge1xuICAgICAgICBpZiAodGhpcy5zcGF3bikgcmV0dXJuIHRoaXMuc3Bhd247XG5cbiAgICAgICAgY29uc3QgeCA9IDA7XG4gICAgICAgIGNvbnN0IHogPSAwOyAvLyBUT0RPOiByZXBsYWNlIHdpdGggYWN0dWFsIGRhdGFcbiAgICAgICAgY29uc3QgY2h1bmsgPSBhd2FpdCB0aGlzLmdldENodW5rQXQoeCwgeik7XG4gICAgICAgIGNvbnN0IHkgPSBjaHVuay5nZXRIaWdoZXN0QmxvY2tBdCh4LCB6KSArIDE7XG4gICAgICAgIHJldHVybiBuZXcgVmVjdG9yMyh6LCB5ICsgMiwgeik7XG4gICAgfVxuXG4gICAgLyoqXG4gICAgICogU2V0IHRoZSB3b3JsZCdzIHNwYXduIHBvc2l0aW9uLlxuICAgICAqIEBwYXJhbSB7VmVjdG9yM30gcG9zIC0gVGhlIHBvc2l0aW9uLlxuICAgICAqL1xuICAgIHB1YmxpYyBzZXRTcGF3blBvc2l0aW9uKHBvczogVmVjdG9yMykge1xuICAgICAgICB0aGlzLnNwYXduID0gcG9zO1xuICAgIH1cblxuICAgIC8vIFRPRE86IG1vdmUgdGhpcz9cbiAgICBwdWJsaWMgYXN5bmMgdXNlSXRlbU9uKFxuICAgICAgICBpdGVtSW5IYW5kOiBJdGVtIHwgQmxvY2sgfCBudWxsLFxuICAgICAgICBibG9ja1Bvc2l0aW9uOiBWZWN0b3IzLFxuICAgICAgICBmYWNlOiBudW1iZXIsXG4gICAgICAgIGNsaWNrUG9zaXRpb246IFZlY3RvcjMsXG4gICAgICAgIHBsYXllcjogUGxheWVyXG4gICAgKTogUHJvbWlzZTx2b2lkPiB7XG4gICAgICAgIGlmIChpdGVtSW5IYW5kIGluc3RhbmNlb2YgSXRlbSkgcmV0dXJuOyAvLyBUT0RPXG5cbiAgICAgICAgLy8gVE9ETzogY2hlY2tzXG4gICAgICAgIC8vIFRPRE86IGNhbkludGVyYWN0XG5cbiAgICAgICAgY29uc3QgYmxvY2sgPSBpdGVtSW5IYW5kOyAvLyBUT0RPOiBnZXQgYmxvY2sgZnJvbSBpdGVtSW5IYW5kXG4gICAgICAgIGNvbnN0IGJsb2NrSWQgPSAoYXdhaXQgdGhpcy5nZXRDaHVua0F0KGJsb2NrUG9zaXRpb24pKS5nZXRCbG9jayhibG9ja1Bvc2l0aW9uKTtcblxuICAgICAgICBjb25zdCBjbGlja2VkQmxvY2sgPSB0aGlzLnNlcnZlci5nZXRCbG9ja01hbmFnZXIoKS5nZXRCbG9ja0J5SWRBbmRNZXRhKGJsb2NrSWQuaWQsIGJsb2NrSWQubWV0YSk7XG5cbiAgICAgICAgaWYgKCFibG9jayB8fCAhY2xpY2tlZEJsb2NrKSByZXR1cm47XG4gICAgICAgIGlmIChjbGlja2VkQmxvY2suZ2V0TmFtZSgpID09PSAnbWluZWNyYWZ0OmFpcicgfHwgIWJsb2NrLmNhbkJlUGxhY2VkKCkpIHJldHVybjtcblxuICAgICAgICBjb25zdCBwbGFjZWRQb3NpdGlvbiA9IG5ldyBWZWN0b3IzKGJsb2NrUG9zaXRpb24uZ2V0WCgpLCBibG9ja1Bvc2l0aW9uLmdldFkoKSwgYmxvY2tQb3NpdGlvbi5nZXRaKCkpO1xuXG4gICAgICAgIC8vIE9ubHkgc2V0IGNvcnJlY3QgZmFjZSBpZiB0aGUgYmxvY2sgY2FuJ3QgYmUgcmVwbGFjZWRcbiAgICAgICAgaWYgKCFjbGlja2VkQmxvY2suY2FuQmVSZXBsYWNlZCgpKVxuICAgICAgICAgICAgc3dpdGNoIChmYWNlKSB7XG4gICAgICAgICAgICAgICAgY2FzZSAwOiAvLyBCb3R0b21cbiAgICAgICAgICAgICAgICAgICAgcGxhY2VkUG9zaXRpb24uc2V0WShwbGFjZWRQb3NpdGlvbi5nZXRZKCkgLSAxKTtcbiAgICAgICAgICAgICAgICAgICAgYnJlYWs7XG4gICAgICAgICAgICAgICAgY2FzZSAxOiAvLyBUb3BcbiAgICAgICAgICAgICAgICAgICAgcGxhY2VkUG9zaXRpb24uc2V0WShwbGFjZWRQb3NpdGlvbi5nZXRZKCkgKyAxKTtcbiAgICAgICAgICAgICAgICAgICAgYnJlYWs7XG4gICAgICAgICAgICAgICAgY2FzZSAyOiAvLyBGcm9udFxuICAgICAgICAgICAgICAgICAgICBwbGFjZWRQb3NpdGlvbi5zZXRaKHBsYWNlZFBvc2l0aW9uLmdldFooKSAtIDEpO1xuICAgICAgICAgICAgICAgICAgICBicmVhaztcbiAgICAgICAgICAgICAgICBjYXNlIDM6IC8vIEJhY2tcbiAgICAgICAgICAgICAgICAgICAgcGxhY2VkUG9zaXRpb24uc2V0WihwbGFjZWRQb3NpdGlvbi5nZXRaKCkgKyAxKTtcbiAgICAgICAgICAgICAgICAgICAgYnJlYWs7XG4gICAgICAgICAgICAgICAgY2FzZSA0OiAvLyBSaWdodFxuICAgICAgICAgICAgICAgICAgICBwbGFjZWRQb3NpdGlvbi5zZXRYKHBsYWNlZFBvc2l0aW9uLmdldFgoKSAtIDEpO1xuICAgICAgICAgICAgICAgICAgICBicmVhaztcbiAgICAgICAgICAgICAgICBjYXNlIDU6IC8vIExlZnRcbiAgICAgICAgICAgICAgICAgICAgcGxhY2VkUG9zaXRpb24uc2V0WChwbGFjZWRQb3NpdGlvbi5nZXRYKCkgKyAxKTtcbiAgICAgICAgICAgICAgICAgICAgYnJlYWs7XG4gICAgICAgICAgICAgICAgZGVmYXVsdDpcbiAgICAgICAgICAgICAgICAgICAgdGhyb3cgbmV3IEVycm9yKCdJbnZhbGlkIEZhY2UnKTtcbiAgICAgICAgICAgIH1cblxuICAgICAgICBpZiAoYmxvY2tQb3NpdGlvbi5nZXRZKCkgPCAwIHx8IGJsb2NrUG9zaXRpb24uZ2V0WSgpID4gMjU1KSByZXR1cm47XG5cbiAgICAgICAgY29uc3Qgc3VjY2VzczogYm9vbGVhbiA9IGF3YWl0IG5ldyBQcm9taXNlKGFzeW5jIChyZXNvbHZlKSA9PiB7XG4gICAgICAgICAgICB0cnkge1xuICAgICAgICAgICAgICAgIGNvbnN0IGNodW5rID0gYXdhaXQgdGhpcy5nZXRDaHVua0F0KHBsYWNlZFBvc2l0aW9uLmdldFgoKSwgcGxhY2VkUG9zaXRpb24uZ2V0WigpKTtcblxuICAgICAgICAgICAgICAgIGNodW5rLnNldEJsb2NrKHBsYWNlZFBvc2l0aW9uLmdldFgoKSwgcGxhY2VkUG9zaXRpb24uZ2V0WSgpLCBwbGFjZWRQb3NpdGlvbi5nZXRaKCksIGJsb2NrKTtcbiAgICAgICAgICAgICAgICByZXNvbHZlKHRydWUpO1xuICAgICAgICAgICAgfSBjYXRjaCAoZXJyb3I6IHVua25vd24pIHtcbiAgICAgICAgICAgICAgICBwbGF5ZXIuZ2V0U2VydmVyKCkuZ2V0TG9nZ2VyKCkud2FybihgJHtwbGF5ZXIuZ2V0TmFtZSgpfSBmYWlsZWQgdG8gcGxhY2UgYmxvY2sgZHVlIHRvICR7ZXJyb3J9YCk7XG4gICAgICAgICAgICAgICAgYXdhaXQgcGxheWVyLnNlbmRNZXNzYWdlKChlcnJvciBhcyBhbnkpPy5tZXNzYWdlKTtcblxuICAgICAgICAgICAgICAgIHJlc29sdmUoZmFsc2UpO1xuICAgICAgICAgICAgfVxuICAgICAgICB9KTtcblxuICAgICAgICBpZiAoIXN1Y2Nlc3MpIHtcbiAgICAgICAgICAgIGlmIChwbGFjZWRQb3NpdGlvbi5nZXRZKCkgPCAwKSByZXR1cm47XG5cbiAgICAgICAgICAgIGNvbnN0IGJsb2NrVXBkYXRlID0gbmV3IFVwZGF0ZUJsb2NrUGFja2V0KCk7XG4gICAgICAgICAgICBibG9ja1VwZGF0ZS54ID0gcGxhY2VkUG9zaXRpb24uZ2V0WCgpO1xuICAgICAgICAgICAgYmxvY2tVcGRhdGUueSA9IHBsYWNlZFBvc2l0aW9uLmdldFkoKTtcbiAgICAgICAgICAgIGJsb2NrVXBkYXRlLnogPSBwbGFjZWRQb3NpdGlvbi5nZXRaKCk7XG4gICAgICAgICAgICBibG9ja1VwZGF0ZS5ibG9ja1J1bnRpbWVJZCA9IEJsb2NrTWFwcGluZ3MuZ2V0UnVudGltZUlkKGNsaWNrZWRCbG9jay5nZXROYW1lKCkpO1xuICAgICAgICAgICAgcmV0dXJuO1xuICAgICAgICB9XG5cbiAgICAgICAgY29uc3QgcnVudGltZUlkID0gQmxvY2tNYXBwaW5ncy5nZXRSdW50aW1lSWQoYmxvY2suZ2V0TmFtZSgpKTtcblxuICAgICAgICBjb25zdCBibG9ja1VwZGF0ZSA9IG5ldyBVcGRhdGVCbG9ja1BhY2tldCgpO1xuICAgICAgICBibG9ja1VwZGF0ZS54ID0gcGxhY2VkUG9zaXRpb24uZ2V0WCgpO1xuICAgICAgICBibG9ja1VwZGF0ZS55ID0gcGxhY2VkUG9zaXRpb24uZ2V0WSgpO1xuICAgICAgICBibG9ja1VwZGF0ZS56ID0gcGxhY2VkUG9zaXRpb24uZ2V0WigpO1xuICAgICAgICBibG9ja1VwZGF0ZS5ibG9ja1J1bnRpbWVJZCA9IHJ1bnRpbWVJZDtcblxuICAgICAgICBhd2FpdCBQcm9taXNlLmFsbChcbiAgICAgICAgICAgIHRoaXMuc2VydmVyXG4gICAgICAgICAgICAgICAgLmdldFNlc3Npb25NYW5hZ2VyKClcbiAgICAgICAgICAgICAgICAuZ2V0QWxsUGxheWVycygpXG4gICAgICAgICAgICAgICAgLm1hcChhc3luYyAob25saW5lUGxheWVyKSA9PlxuICAgICAgICAgICAgICAgICAgICBvbmxpbmVQbGF5ZXIuZ2V0TmV0d29ya1Nlc3Npb24oKS5nZXRDb25uZWN0aW9uKCkuc2VuZERhdGFQYWNrZXQoYmxvY2tVcGRhdGUpXG4gICAgICAgICAgICAgICAgKVxuICAgICAgICApO1xuXG4gICAgICAgIGNvbnN0IHBrID0gbmV3IExldmVsU291bmRFdmVudFBhY2tldCgpO1xuICAgICAgICBway5zb3VuZCA9IDY7IC8vIFRPRE86IGVudW1cblxuICAgICAgICBway5wb3NpdGlvblggPSBwbGFjZWRQb3NpdGlvbi5nZXRYKCk7XG4gICAgICAgIHBrLnBvc2l0aW9uWSA9IHBsYWNlZFBvc2l0aW9uLmdldFkoKTtcbiAgICAgICAgcGsucG9zaXRpb25aID0gcGxhY2VkUG9zaXRpb24uZ2V0WigpO1xuXG4gICAgICAgIHBrLmV4dHJhRGF0YSA9IHJ1bnRpbWVJZDsgLy8gSW4gdGhpcyBjYXNlIHJlZmVycyB0byBibG9jayBydW50aW1lIElkXG4gICAgICAgIHBrLmRpc2FibGVSZWxhdGl2ZVZvbHVtZSA9IGZhbHNlO1xuXG4gICAgICAgIGF3YWl0IFByb21pc2UuYWxsKFxuICAgICAgICAgICAgcGxheWVyXG4gICAgICAgICAgICAgICAgLmdldFdvcmxkKClcbiAgICAgICAgICAgICAgICAuZ2V0UGxheWVycygpXG4gICAgICAgICAgICAgICAgLm1hcCgodGFyZ2V0KSA9PiB0YXJnZXQuZ2V0TmV0d29ya1Nlc3Npb24oKS5zZW5kKHBrKSlcbiAgICAgICAgKTtcbiAgICB9XG5cbiAgICAvKipcbiAgICAgKiBTZW5kcyB0aGUgY3VycmVudCB0aW1lIHRvIGFsbCBwbGF5ZXJzIGluIHRoZSB3b3JsZC5cbiAgICAgKi9cbiAgICBwdWJsaWMgYXN5bmMgc2VuZFRpbWUoKTogUHJvbWlzZTx2b2lkPiB7XG4gICAgICAgIC8vIFRyeSB0byBzZW5kIGl0IGF0IHRoZSBzYW1lIHRpbWUgdG8gYWxsXG4gICAgICAgIGF3YWl0IFByb21pc2UuYWxsKHRoaXMuZ2V0UGxheWVycygpLm1hcCgocGxheWVyKSA9PiBwbGF5ZXIuZ2V0TmV0d29ya1Nlc3Npb24oKS5zZW5kVGltZSh0aGlzLmdldFRpY2tzKCkpKSk7XG4gICAgfVxuXG4gICAgLyoqXG4gICAgICogQWRkcyBhbiBlbnRpdHkgdG8gdGhlIGxldmVsLlxuICAgICAqIEBwYXJhbSB7RW50aXR5fSBlbnRpdHkgLSBUaGUgZW50aXR5IHRvIGFkZC5cbiAgICAgKi9cbiAgICBwdWJsaWMgYXN5bmMgYWRkRW50aXR5KGVudGl0eTogRW50aXR5KTogUHJvbWlzZTx2b2lkPiB7XG4gICAgICAgIHRoaXMuZW50aXRpZXMuc2V0KGVudGl0eS5nZXRSdW50aW1lSWQoKSwgZW50aXR5KTtcblxuICAgICAgICBpZiAoIWVudGl0eS5pc1BsYXllcigpKSBhd2FpdCBlbnRpdHkuc2VuZFNwYXduKCk7XG4gICAgICAgIGVsc2UgYXdhaXQgUHJvbWlzZS5hbGwodGhpcy5nZXRFbnRpdGllcygpLm1hcCgoZSkgPT4gZS5zZW5kU3Bhd24oZW50aXR5IGFzIFBsYXllcikpKTtcbiAgICB9XG5cbiAgICAvKipcbiAgICAgKiBSZW1vdmVzIGFuIGVudGl0eSBmcm9tIHRoZSBsZXZlbC5cbiAgICAgKiBAcGFyYW0ge0VudGl0eX0gZW50aXR5IC0gVGhlIGVudGl0eSB0byByZW1vdmUuXG4gICAgICovXG4gICAgcHVibGljIGFzeW5jIHJlbW92ZUVudGl0eShlbnRpdHk6IEVudGl0eSk6IFByb21pc2U8dm9pZD4ge1xuICAgICAgICBpZiAoIWVudGl0eS5pc1BsYXllcigpKSBhd2FpdCBlbnRpdHkuc2VuZERlc3Bhd24oKTtcbiAgICAgICAgZWxzZSBhd2FpdCBQcm9taXNlLmFsbCh0aGlzLmdldEVudGl0aWVzKCkubWFwKChlKSA9PiBlLnNlbmREZXNwYXduKGVudGl0eSBhcyBQbGF5ZXIpKSk7XG5cbiAgICAgICAgdGhpcy5lbnRpdGllcy5kZWxldGUoZW50aXR5LmdldFJ1bnRpbWVJZCgpKTtcbiAgICB9XG5cbiAgICAvKipcbiAgICAgKiBHZXQgYWxsIGVudGl0aWVzIGluIHRoaXMgd29ybGQuXG4gICAgICogQHJldHVybnMge0VudGl0eVtdfSB0aGUgZW50aXRpZXMuXG4gICAgICovXG4gICAgcHVibGljIGdldEVudGl0aWVzKCk6IEVudGl0eVtdIHtcbiAgICAgICAgcmV0dXJuIEFycmF5LmZyb20odGhpcy5lbnRpdGllcy52YWx1ZXMoKSk7XG4gICAgfVxuICAgIC8qKlxuICAgICAqIEdldCBhbGwgcGxheWVycyBpbiB0aGlzIHdvcmxkLlxuICAgICAqIEByZXR1cm5zIHtQbGF5ZXJbXX0gdGhlIHBsYXllcnMuXG4gICAgICovXG4gICAgcHVibGljIGdldFBsYXllcnMoKTogUGxheWVyW10ge1xuICAgICAgICByZXR1cm4gKHRoaXMuZ2V0RW50aXRpZXMoKS5maWx0ZXIoKGUpID0+IGUuaXNQbGF5ZXIoKSkgYXMgUGxheWVyW10pLmZpbHRlcigocCkgPT4gcC5pc09ubGluZSgpKTtcbiAgICB9XG5cbiAgICAvKipcbiAgICAgKiBTYXZlcyBjaGFuZ2VkIGNodW5rcyBpbnRvIGRpc2suXG4gICAgICovXG4gICAgcHVibGljIGFzeW5jIHNhdmVDaHVua3MoKTogUHJvbWlzZTx2b2lkPiB7XG4gICAgICAgIGNvbnN0IHRpbWVyID0gbmV3IFRpbWVyKCk7XG4gICAgICAgIHRoaXMuc2VydmVyLmdldExvZ2dlcigpLmluZm8oYFNhdmluZyBjaHVua3MgZm9yIGxldmVsICR7dGhpcy5nZXRGb3JtYXR0ZWROYW1lKCl9YCk7XG5cbiAgICAgICAgYXdhaXQgUHJvbWlzZS5hbGwoXG4gICAgICAgICAgICBBcnJheS5mcm9tKHRoaXMuY2h1bmtzLnZhbHVlcygpKVxuICAgICAgICAgICAgICAgIC5maWx0ZXIoKGMpID0+IGMuZ2V0SGFzQ2hhbmdlZCgpKVxuICAgICAgICAgICAgICAgIC5tYXAoYXN5bmMgKGNodW5rKSA9PiB0aGlzLnByb3ZpZGVyLndyaXRlQ2h1bmsoY2h1bmspKVxuICAgICAgICApO1xuICAgICAgICB0aGlzLnNlcnZlci5nZXRMb2dnZXIoKS52ZXJib3NlKGAodG9vayDCp2Uke3RpbWVyLnN0b3AoKX0gbXPCp3IpIWApO1xuICAgIH1cblxuICAgIHB1YmxpYyBhc3luYyBzYXZlKCk6IFByb21pc2U8dm9pZD4ge1xuICAgICAgICAvLyBTYXZlIGNodW5rc1xuICAgICAgICB0aGlzLmdldFBsYXllcnMoKS5mb3JFYWNoKGFzeW5jIChwbGF5ZXIpID0+IHtcbiAgICAgICAgICAgIGF3YWl0IHRoaXMuc2F2ZVBsYXllckRhdGEocGxheWVyKTtcbiAgICAgICAgfSk7XG4gICAgICAgIGF3YWl0IHRoaXMuc2F2ZUNodW5rcygpO1xuICAgICAgICBhd2FpdCB0aGlzLnNhdmVMZXZlbERhdGEoKTtcbiAgICB9XG5cbiAgICBwdWJsaWMgZ2V0R2FtZXJ1bGVNYW5hZ2VyKCk6IEdhbWVydWxlTWFuYWdlciB7XG4gICAgICAgIHJldHVybiB0aGlzLmdhbWVydWxlTWFuYWdlcjtcbiAgICB9XG5cbiAgICBwdWJsaWMgZ2V0VGlja3MoKTogbnVtYmVyIHtcbiAgICAgICAgcmV0dXJuIHRoaXMuY3VycmVudFRpY2s7XG4gICAgfVxuXG4gICAgcHVibGljIHNldFRpY2tzKHRpY2s6IG51bWJlcik6IHZvaWQge1xuICAgICAgICB0aGlzLmN1cnJlbnRUaWNrID0gdGljaztcbiAgICB9XG5cbiAgICBwdWJsaWMgZ2V0UHJvdmlkZXIoKTogYW55IHtcbiAgICAgICAgcmV0dXJuIHRoaXMucHJvdmlkZXI7XG4gICAgfVxuXG4gICAgLy8gVGhpcyBpcyB1c2VkIGZvciBleGFtcGxlIGluIHN0YXJ0IGdhbWUgcGFja2V0XG4gICAgcHVibGljIGdldFVVSUQoKTogc3RyaW5nIHtcbiAgICAgICAgcmV0dXJuIHRoaXMudXVpZDtcbiAgICB9XG5cbiAgICBwdWJsaWMgZ2V0TmFtZSgpOiBzdHJpbmcge1xuICAgICAgICByZXR1cm4gdGhpcy5uYW1lO1xuICAgIH1cbiAgICBwdWJsaWMgZ2V0Rm9ybWF0dGVkTmFtZSgpOiBzdHJpbmcge1xuICAgICAgICByZXR1cm4gYMKnYicke3RoaXMubmFtZX0nLyR7dGhpcy5nZW5lcmF0b3IuY29uc3RydWN0b3IubmFtZX3Cp3JgO1xuICAgIH1cblxuICAgIHB1YmxpYyBnZXRTZWVkKCk6IG51bWJlciB7XG4gICAgICAgIHJldHVybiB0aGlzLnNlZWQ7XG4gICAgfVxuXG4gICAgcHJpdmF0ZSBhc3luYyBnZXRMZXZlbERhdGEoKSB7XG4gICAgICAgIGNvbnN0IHBhdGggPSB3aXRoQ3dkKFdPUkxEU19GT0xERVJfTkFNRSwgdGhpcy5uYW1lLCBMRVZFTF9EQVRBX0ZJTEVfTkFNRSk7XG4gICAgICAgIGlmICghZnMuZXhpc3RzU3luYyhwYXRoKSkgcmV0dXJuIHt9O1xuXG4gICAgICAgIHRyeSB7XG4gICAgICAgICAgICBjb25zdCByYXcgPSBhd2FpdCBmcy5wcm9taXNlcy5yZWFkRmlsZShwYXRoLCAndXRmLTgnKTtcbiAgICAgICAgICAgIHJldHVybiBwYXJzZUpTT041KHJhdy50b1N0cmluZygpKSBhcyBQYXJ0aWFsPExldmVsRGF0YT47XG4gICAgICAgIH0gY2F0Y2ggKGVycm9yOiBhbnkpIHtcbiAgICAgICAgICAgIC8vIFNvbWV0aGluZyB3ZW50IHdyb25nIHdoaWxlIHJlYWRpbmcgb3IgcGFyc2luZyB0aGUgbGV2ZWwgZGF0YS5cbiAgICAgICAgICAgIHRoaXMuc2VydmVyLmdldExvZ2dlcigpLmVycm9yKGVycm9yKTtcbiAgICAgICAgfVxuXG4gICAgICAgIHJldHVybiB7fTtcbiAgICB9XG4gICAgcHVibGljIGFzeW5jIHNhdmVMZXZlbERhdGEoKTogUHJvbWlzZTx2b2lkPiB7XG4gICAgICAgIGNvbnN0IGRhdGEgPSB7XG4gICAgICAgICAgICBzcGF3bjogYXdhaXQgdGhpcy5nZXRTcGF3blBvc2l0aW9uKCksXG4gICAgICAgICAgICBnYW1lcnVsZXM6IEFycmF5LmZyb20odGhpcy5nZXRHYW1lcnVsZU1hbmFnZXIoKS5nZXRHYW1lcnVsZXMoKSksXG4gICAgICAgICAgICBlbnRpdGllczogdGhpcy5nZXRFbnRpdGllcygpXG4gICAgICAgICAgICAgICAgLmZpbHRlcigoZW50aXR5KSA9PiAhZW50aXR5LmlzUGxheWVyKCkgJiYgIWVudGl0eS5pc0NvbnNvbGUoKSlcbiAgICAgICAgICAgICAgICAubWFwKChlbnRpdHkpID0+ICh7XG4gICAgICAgICAgICAgICAgICAgIHV1aWQ6IGVudGl0eS5nZXRVVUlEKCksXG4gICAgICAgICAgICAgICAgICAgIHR5cGU6IGVudGl0eS5nZXRUeXBlKCksXG4gICAgICAgICAgICAgICAgICAgIHBvc2l0aW9uOiB7XG4gICAgICAgICAgICAgICAgICAgICAgICB4OiBlbnRpdHkuZ2V0WCgpLFxuICAgICAgICAgICAgICAgICAgICAgICAgeTogZW50aXR5LmdldFkoKSxcbiAgICAgICAgICAgICAgICAgICAgICAgIHo6IGVudGl0eS5nZXRaKCksXG4gICAgICAgICAgICAgICAgICAgICAgICBwaXRjaDogZW50aXR5LnBpdGNoLFxuICAgICAgICAgICAgICAgICAgICAgICAgeWF3OiBlbnRpdHkueWF3LFxuICAgICAgICAgICAgICAgICAgICAgICAgaGVhZFlhdzogZW50aXR5LmhlYWRZYXdcbiAgICAgICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgIH0pKVxuICAgICAgICB9O1xuXG4gICAgICAgIHRyeSB7XG4gICAgICAgICAgICBhd2FpdCBmcy5wcm9taXNlcy53cml0ZUZpbGUoXG4gICAgICAgICAgICAgICAgLy8gRklYTUU6IFRoaXMgb3ZlcndyaXRlcyBjb21tZW50cyBpbiB0aGUgZmlsZS5cbiAgICAgICAgICAgICAgICB3aXRoQ3dkKFdPUkxEU19GT0xERVJfTkFNRSwgdGhpcy5uYW1lLCBMRVZFTF9EQVRBX0ZJTEVfTkFNRSksXG4gICAgICAgICAgICAgICAgSlNPTi5zdHJpbmdpZnkoZGF0YSwgbnVsbCwgNClcbiAgICAgICAgICAgICk7XG4gICAgICAgIH0gY2F0Y2ggKGVycm9yOiB1bmtub3duKSB7XG4gICAgICAgICAgICB0aGlzLnNlcnZlci5nZXRMb2dnZXIoKS5lcnJvcihgRmFpbGVkIHRvIHNhdmUgbGV2ZWwgZGF0YWApO1xuICAgICAgICAgICAgdGhpcy5zZXJ2ZXIuZ2V0TG9nZ2VyKCkuZXJyb3IoZXJyb3IpO1xuICAgICAgICB9XG4gICAgfVxuXG4gICAgLyoqXG4gICAgICogR2V0IHRoZSBwbGF5ZXIgZGF0YSBmb3IgYSBwbGF5ZXIuXG4gICAgICogQHBhcmFtIHtQbGF5ZXJ9IHBsYXllciAtIFRoZSBwbGF5ZXIgdG8gZ2V0IHRoZSBkYXRhIGZvci5cbiAgICAgKiBAcmV0dXJucyB7UHJvbWlzZTxXb3JsZFBsYXllckRhdGE+fSBUaGUgcGxheWVyIGRhdGEuXG4gICAgICovXG4gICAgcHVibGljIGFzeW5jIGdldFBsYXllckRhdGEocGxheWVyOiBQbGF5ZXIpOiBQcm9taXNlPFBhcnRpYWw8V29ybGRQbGF5ZXJEYXRhPj4ge1xuICAgICAgICB0cnkge1xuICAgICAgICAgICAgY29uc3QgZmlsZU5hbWUgPSBwbGF5ZXIuZ2V0WFVJRCgpO1xuICAgICAgICAgICAgaWYgKCFmaWxlTmFtZSkge1xuICAgICAgICAgICAgICAgIHRocm93IG5ldyBFcnJvcignUGxheWVyIGhhcyBubyBYVUlEJyk7XG4gICAgICAgICAgICB9XG5cbiAgICAgICAgICAgIGNvbnN0IHJhdyA9IGF3YWl0IGZzLnByb21pc2VzLnJlYWRGaWxlKFxuICAgICAgICAgICAgICAgIHdpdGhDd2QoV09STERTX0ZPTERFUl9OQU1FLCB0aGlzLm5hbWUsICdwbGF5ZXJkYXRhJywgYCR7cGxheWVyLmdldFhVSUQoKSB8fCBwbGF5ZXIuZ2V0TmFtZSgpfS5qc29uYCksXG4gICAgICAgICAgICAgICAgeyBmbGFnOiAncicsIGVuY29kaW5nOiAndXRmLTgnIH1cbiAgICAgICAgICAgICk7XG4gICAgICAgICAgICByZXR1cm4gcGFyc2VKU09ONShyYXcudG9TdHJpbmcoKSkgYXMgUGFydGlhbDxXb3JsZFBsYXllckRhdGE+O1xuICAgICAgICB9IGNhdGNoIChlcnJvcjogdW5rbm93bikge1xuICAgICAgICAgICAgdGhpcy5zZXJ2ZXIuZ2V0TG9nZ2VyKCkuZGVidWcoYFBsYXllckRhdGEgaXMgbWlzc2luZyBmb3IgcGxheWVyICR7cGxheWVyLmdldFhVSUQoKX1gKTtcbiAgICAgICAgICAgIHRoaXMuc2VydmVyLmdldExvZ2dlcigpLmVycm9yKGVycm9yKTtcblxuICAgICAgICAgICAgY29uc3Qgc3Bhd24gPSBhd2FpdCB0aGlzLmdldFNwYXduUG9zaXRpb24oKTtcbiAgICAgICAgICAgIHJldHVybiB7XG4gICAgICAgICAgICAgICAgZ2FtZW1vZGU6IHRoaXMuc2VydmVyLmdldENvbmZpZygpLmdldEdhbWVtb2RlKCksXG4gICAgICAgICAgICAgICAgcG9zaXRpb246IHtcbiAgICAgICAgICAgICAgICAgICAgeDogc3Bhd24uZ2V0WCgpLFxuICAgICAgICAgICAgICAgICAgICB5OiBzcGF3bi5nZXRZKCksXG4gICAgICAgICAgICAgICAgICAgIHo6IHNwYXduLmdldFooKSxcbiAgICAgICAgICAgICAgICAgICAgcGl0Y2g6IDAsXG4gICAgICAgICAgICAgICAgICAgIHlhdzogMCxcbiAgICAgICAgICAgICAgICAgICAgaGVhZFlhdzogMFxuICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgIH07XG4gICAgICAgIH1cbiAgICB9XG4gICAgcHVibGljIGFzeW5jIHNhdmVQbGF5ZXJEYXRhKHBsYXllcjogUGxheWVyKTogUHJvbWlzZTx2b2lkPiB7XG4gICAgICAgIGNvbnN0IGRhdGEgPSB7XG4gICAgICAgICAgICB1dWlkOiBwbGF5ZXIuZ2V0VVVJRCgpLFxuICAgICAgICAgICAgdXNlcm5hbWU6IHBsYXllci5nZXROYW1lKCksXG4gICAgICAgICAgICBnYW1lbW9kZTogZ2V0R2FtZXR5cGVOYW1lKHBsYXllci5nYW1lbW9kZSksXG4gICAgICAgICAgICBwb3NpdGlvbjoge1xuICAgICAgICAgICAgICAgIHg6IHBsYXllci5nZXRYKCksXG4gICAgICAgICAgICAgICAgeTogcGxheWVyLmdldFkoKSxcbiAgICAgICAgICAgICAgICB6OiBwbGF5ZXIuZ2V0WigpLFxuICAgICAgICAgICAgICAgIHBpdGNoOiBwbGF5ZXIucGl0Y2gsXG4gICAgICAgICAgICAgICAgeWF3OiBwbGF5ZXIueWF3LFxuICAgICAgICAgICAgICAgIGhlYWRZYXc6IHBsYXllci5oZWFkWWF3XG4gICAgICAgICAgICB9XG4gICAgICAgIH0gYXMgV29ybGRQbGF5ZXJEYXRhO1xuXG4gICAgICAgIHRyeSB7XG4gICAgICAgICAgICBhd2FpdCBmcy5wcm9taXNlcy53cml0ZUZpbGUoXG4gICAgICAgICAgICAgICAgLy8gRklYTUU6IFRoaXMgb3ZlcndyaXRlcyBjb21tZW50cyBpbiB0aGUgZmlsZS5cbiAgICAgICAgICAgICAgICB3aXRoQ3dkKFdPUkxEU19GT0xERVJfTkFNRSwgdGhpcy5uYW1lLCAncGxheWVyZGF0YScsIGAke3BsYXllci5nZXRYVUlEKCkgfHwgcGxheWVyLmdldE5hbWUoKX0uanNvbmApLFxuICAgICAgICAgICAgICAgIEpTT04uc3RyaW5naWZ5KGRhdGEsIG51bGwsIDQpLFxuICAgICAgICAgICAgICAgIHsgZmxhZzogJ3crJywgZW5jb2Rpbmc6ICd1dGYtOCcsIGZsdXNoOiB0cnVlIH1cbiAgICAgICAgICAgICk7XG4gICAgICAgIH0gY2F0Y2ggKGVycm9yOiB1bmtub3duKSB7XG4gICAgICAgICAgICB0aGlzLnNlcnZlci5nZXRMb2dnZXIoKS5lcnJvcihgRmFpbGVkIHRvIHNhdmUgcGxheWVyIGRhdGFgKTtcbiAgICAgICAgICAgIHRoaXMuc2VydmVyLmdldExvZ2dlcigpLmVycm9yKGVycm9yKTtcbiAgICAgICAgfVxuICAgIH1cblxuICAgIC8qKlxuICAgICAqIEByZXR1cm5zIHtTZXJ2ZXJ9IFRoZSBzZXJ2ZXIgaW5zdGFuY2UuXG4gICAgICovXG4gICAgcHVibGljIGdldFNlcnZlcigpOiBTZXJ2ZXIge1xuICAgICAgICByZXR1cm4gdGhpcy5zZXJ2ZXI7XG4gICAgfVxufVxuIl0sIm1hcHBpbmdzIjoiOzs7Ozs7Ozs7Ozs7Ozs7Ozs7OztBQXVCQSxJQUFNLHVCQUF1QjtBQUM3QixJQUFNLHFCQUFxQjtBQXFDM0IsSUFBYSxRQUFiLE1BQXNDO0NBQ2xDLE9BQWdDLG1CQUFBLFFBQUssYUFBYTtDQUNsRDtDQUVBLDJCQUFpRCxJQUFJLElBQUk7Q0FDekQseUJBQThDLElBQUksSUFBSTtDQUN0RDtDQUNBLGNBQXNCO0NBQ3RCO0NBQ0E7Q0FDQTtDQUNBO0NBQ0E7Q0FDQSxRQUFnQztDQUVoQyxZQUFtQixFQUFFLE1BQU0sUUFBUSxVQUFVLE1BQU0sV0FBVyxVQUFxQjtFQUMvRSxLQUFLLE9BQU87RUFDWixLQUFLLFNBQVM7RUFDZCxLQUFLLFdBQVc7RUFDaEIsS0FBSyxrQkFBa0IsSUFBSSw4QkFBQSxRQUFnQixNQUFNO0VBQ2pELEtBQUssT0FBTztFQUNaLEtBQUssWUFBWTtFQUNqQixLQUFLLFNBQVMsVUFBVSxDQUFDO0VBRXpCLEtBQUssZ0JBQWdCLFlBQVksOEJBQUEsVUFBVSxpQkFBaUIsTUFBTSxJQUFJO0VBRXRFLElBQUk7R0FFQSxNQUFNLE9BQU8sa0JBQUEsUUFBUSxvQkFBb0IsS0FBSyxNQUFNLFlBQVk7R0FDaEUsSUFBSSxDQUFDLFFBQUEsUUFBRyxXQUFXLElBQUksR0FBRyxRQUFBLFFBQUcsVUFBVSxNQUFNLEVBQUUsV0FBVyxLQUFLLENBQUM7RUFDcEUsU0FBUyxPQUFnQjtHQUNyQixLQUFLLE9BQU8sVUFBVSxFQUFFLE1BQU0sc0NBQXNDLEtBQUssTUFBTTtHQUMvRSxLQUFLLE9BQU8sVUFBVSxFQUFFLE1BQU0sS0FBSztFQUN2QztDQUNKOzs7OztDQU1BLE1BQWEsU0FBd0I7RUFDakMsS0FBSyxPQUFPLEdBQUcsUUFBUSxPQUFPLFFBQVEsS0FBSyxPQUFPLElBQUksUUFBUSxDQUFDLENBQUM7RUFFaEUsTUFBTSxRQUFRLE1BQU0sS0FBSyxhQUFhO0VBQ3RDLElBQUksTUFBTSxPQUFPLEtBQUssaUJBQWlCLG1CQUFBLFFBQVEsV0FBVyxNQUFNLEtBQUssQ0FBQztFQUN0RSxJQUFJLE1BQU0sV0FDTixNQUFNLFVBQVUsU0FBUyxDQUFDLE1BQU0sQ0FBQyxPQUFPLGVBQ3BDLEtBQUssZ0JBQWdCLFlBQVksTUFBTSxPQUFPLFFBQVEsQ0FDMUQ7RUFFSixJQUFJLE1BQU0sVUFDTixLQUFLLE1BQU0sY0FBYyxNQUFNLFVBQVU7R0FDckMsTUFBTSxTQUFTLE1BQU0sS0FBSyxPQUFPLE9BQU8sd0JBQUEsZ0JBQVEsQ0FBQyxFQUFFLE1BQU0sTUFBTSxFQUFFLFdBQVcsV0FBVyxJQUFJO0dBQzNGLElBQUksQ0FBQyxRQUFRO0lBQ1QsS0FBSyxPQUFPLFVBQVUsRUFBRSxLQUFLLGVBQWUsV0FBVyxLQUFLLFdBQVc7SUFDdkU7R0FDSjtHQUVBLE1BQU0sS0FBSyxVQUNQLElBQUksT0FBTztJQUNQLE9BQU87SUFDUCxNQUFNLFdBQVc7SUFDakIsR0FBRyxXQUFXO0lBQ2QsUUFBUSxLQUFLO0dBQ2pCLENBQUMsQ0FDTDtFQUNKO0VBR0osS0FBSyxTQUFTLFNBQVMsSUFBSTtFQUMzQixNQUFNLEtBQUssU0FBUyxPQUFPO0VBRTNCLEtBQUssT0FBTyxVQUFVLEVBQUUsS0FBSyx3Q0FBd0MsS0FBSyxpQkFBaUIsR0FBRztFQUM5RixNQUFNLGVBQXNDLENBQUM7RUFDN0MsTUFBTSxRQUFRLElBQUksb0JBQUEsUUFBTTtFQUV4QixNQUFNLE9BQU8sS0FBSyxPQUFPLFVBQVUsRUFBRSxnQkFBZ0IsSUFBSTtFQUN6RCxLQUFLLElBQUksSUFBSSxHQUFHLElBQUksTUFBTSxLQUN0QixLQUFLLElBQUksSUFBSSxHQUFHLElBQUksTUFBTSxLQUN0QixhQUFhLEtBQUssS0FBSyxVQUFVLEdBQUcsR0FBRyxJQUFJLENBQUM7RUFJcEQsTUFBTSxRQUFRLElBQUksWUFBWTtFQUM5QixLQUFLLE9BQU8sVUFBVSxFQUFFLFFBQVEsV0FBVyxNQUFNLEtBQUssRUFBRSxPQUFPO0NBQ25FOzs7OztDQU1BLE1BQWEsVUFBeUI7RUFDbEMsTUFBTSxLQUFLLEtBQUs7RUFDaEIsTUFBTSxLQUFLLFNBQVMsUUFBUTtDQUNoQztDQUVBLGVBQWlDO0VBQzdCLE9BQU8sS0FBSztDQUNoQjs7Ozs7O0NBT0EsTUFBYSxPQUFPLE1BQTZCO0VBSTdDLEtBQUs7RUFHTCxJQUFJLEtBQUssY0FBYyxPQUFPLEtBQzFCLE1BQU0sS0FBSyxLQUFLO0VBR3BCLE1BQU0sUUFBUSxJQUFJLEtBQUssWUFBWSxFQUFFLEtBQUssV0FBVyxPQUFPLE9BQU8sSUFBSSxDQUFDLENBQUM7RUFDekUsTUFBTSxLQUFLLFNBQVM7Q0FDeEI7Ozs7Ozs7O0NBU0EsTUFBYSxTQUFTLEdBQVcsR0FBVyxHQUFXLFFBQVEsR0FBbUI7RUFDOUUsTUFBTSxXQUFXLE1BQU0sS0FBSyxXQUFXLEdBQUcsQ0FBQyxHQUFHLFNBQVMsR0FBRyxHQUFHLEdBQUcsS0FBSztFQUNyRSxNQUFNLFFBQVEsS0FBSyxPQUFPLGdCQUFnQixFQUFFLG9CQUFvQixRQUFRLElBQUksUUFBUSxJQUFJO0VBRXhGLElBQUksQ0FBQyxPQUFPLE9BQU8sS0FBSyxPQUFPLGdCQUFnQixFQUFFLFNBQVMsZUFBZTtFQUN6RSxPQUFPO0NBQ1g7Ozs7O0NBTUEsTUFBYSxTQUFTLElBQVksSUFBNEI7RUFDMUQsTUFBTSxRQUFRLDBCQUFBLFFBQU0sT0FBTyxJQUFJLEVBQUU7RUFDakMsSUFBSSxDQUFDLEtBQUssT0FBTyxJQUFJLEtBQUssR0FBRyxPQUFPLEtBQUssVUFBVSxJQUFJLEVBQUU7RUFFekQsT0FBTyxLQUFLLE9BQU8sSUFBSSxLQUFLO0NBQ2hDOzs7Ozs7Q0FPQSxNQUFhLFVBQVUsR0FBVyxHQUFXLGFBQXVDO0VBQ2hGLE1BQU0sUUFBUSwwQkFBQSxRQUFNLE9BQU8sR0FBRyxDQUFDO0VBRS9CLE1BQU0sUUFBUSxNQUFNLEtBQUssU0FBUyxVQUFVLEdBQUcsR0FBRyxLQUFLLE1BQU0sS0FBSyxXQUFXLEtBQUssTUFBTTtFQUN4RixLQUFLLE9BQU8sSUFBSSxPQUFPLEtBQUs7RUFHNUIsT0FBTztDQUNYOzs7Ozs7O0NBUUEsTUFBYSxlQUFlLFVBQTBCLE9BQW1CLE1BQTZCO0VBQ2xHLE1BQU0sbUJBQW1CLElBQUksd0NBQUEsUUFBaUI7RUFDOUMsaUJBQWlCLFVBQVU7RUFFM0IsaUJBQWlCLE9BQU87RUFHeEIsTUFBTSxRQUFRLElBQUksS0FBSyxXQUFXLEVBQUUsS0FBSyxXQUFXLE9BQU8sa0JBQWtCLEVBQUUsS0FBSyxnQkFBZ0IsQ0FBQyxDQUFDO0NBQzFHO0NBT0EsTUFBYSxXQUFXLEdBQXFCLElBQVksR0FBbUI7RUFDeEUsSUFBSSxhQUFhLG1CQUFBLFNBQ2IsT0FBTyxLQUFLLFdBQVcsRUFBRSxLQUFLLEdBQUcsRUFBRSxLQUFLLENBQUM7RUFHN0MsT0FBTyxLQUFLLFNBQVMsS0FBSyxHQUFHLEtBQUssQ0FBQztDQUN2Qzs7OztDQUtBLE1BQWEsbUJBQXFDO0VBQzlDLElBQUksS0FBSyxPQUFPLE9BQU8sS0FBSztFQUU1QixNQUFNLElBQUk7RUFDVixNQUFNLElBQUk7RUFHVixPQUFPLElBQUksbUJBQUEsUUFBUSxJQURULE1BRFUsS0FBSyxXQUFXLEdBQUcsQ0FBQyxHQUN4QixrQkFBa0IsR0FBRyxDQUFDLElBQUksSUFDaEIsR0FBRyxDQUFDO0NBQ2xDOzs7OztDQU1BLGlCQUF3QixLQUFjO0VBQ2xDLEtBQUssUUFBUTtDQUNqQjtDQUdBLE1BQWEsVUFDVCxZQUNBLGVBQ0EsTUFDQSxlQUNBLFFBQ2E7RUFDYixJQUFJLHNCQUFzQixrQkFBQSxNQUFNO0VBS2hDLE1BQU0sUUFBUTtFQUNkLE1BQU0sV0FBVyxNQUFNLEtBQUssV0FBVyxhQUFhLEdBQUcsU0FBUyxhQUFhO0VBRTdFLE1BQU0sZUFBZSxLQUFLLE9BQU8sZ0JBQWdCLEVBQUUsb0JBQW9CLFFBQVEsSUFBSSxRQUFRLElBQUk7RUFFL0YsSUFBSSxDQUFDLFNBQVMsQ0FBQyxjQUFjO0VBQzdCLElBQUksYUFBYSxRQUFRLE1BQU0sbUJBQW1CLENBQUMsTUFBTSxZQUFZLEdBQUc7RUFFeEUsTUFBTSxpQkFBaUIsSUFBSSxtQkFBQSxRQUFRLGNBQWMsS0FBSyxHQUFHLGNBQWMsS0FBSyxHQUFHLGNBQWMsS0FBSyxDQUFDO0VBR25HLElBQUksQ0FBQyxhQUFhLGNBQWMsR0FDNUIsUUFBUSxNQUFSO0dBQ0ksS0FBSztJQUNELGVBQWUsS0FBSyxlQUFlLEtBQUssSUFBSSxDQUFDO0lBQzdDO0dBQ0osS0FBSztJQUNELGVBQWUsS0FBSyxlQUFlLEtBQUssSUFBSSxDQUFDO0lBQzdDO0dBQ0osS0FBSztJQUNELGVBQWUsS0FBSyxlQUFlLEtBQUssSUFBSSxDQUFDO0lBQzdDO0dBQ0osS0FBSztJQUNELGVBQWUsS0FBSyxlQUFlLEtBQUssSUFBSSxDQUFDO0lBQzdDO0dBQ0osS0FBSztJQUNELGVBQWUsS0FBSyxlQUFlLEtBQUssSUFBSSxDQUFDO0lBQzdDO0dBQ0osS0FBSztJQUNELGVBQWUsS0FBSyxlQUFlLEtBQUssSUFBSSxDQUFDO0lBQzdDO0dBQ0osU0FDSSxNQUFNLElBQUksTUFBTSxjQUFjO0VBQ3RDO0VBRUosSUFBSSxjQUFjLEtBQUssSUFBSSxLQUFLLGNBQWMsS0FBSyxJQUFJLEtBQUs7RUFnQjVELElBQUksQ0FBQyxNQWQwQixJQUFJLFFBQVEsT0FBTyxZQUFZO0dBQzFELElBQUk7SUFHQSxDQUFBLE1BRm9CLEtBQUssV0FBVyxlQUFlLEtBQUssR0FBRyxlQUFlLEtBQUssQ0FBQyxHQUUxRSxTQUFTLGVBQWUsS0FBSyxHQUFHLGVBQWUsS0FBSyxHQUFHLGVBQWUsS0FBSyxHQUFHLEtBQUs7SUFDekYsUUFBUSxJQUFJO0dBQ2hCLFNBQVMsT0FBZ0I7SUFDckIsT0FBTyxVQUFVLEVBQUUsVUFBVSxFQUFFLEtBQUssR0FBRyxPQUFPLFFBQVEsRUFBRSxnQ0FBZ0MsT0FBTztJQUMvRixNQUFNLE9BQU8sWUFBYSxPQUFlLE9BQU87SUFFaEQsUUFBUSxLQUFLO0dBQ2pCO0VBQ0osQ0FBQyxHQUVhO0dBQ1YsSUFBSSxlQUFlLEtBQUssSUFBSSxHQUFHO0dBRS9CLE1BQU0sY0FBYyxJQUFJLHlDQUFBLFFBQWtCO0dBQzFDLFlBQVksSUFBSSxlQUFlLEtBQUs7R0FDcEMsWUFBWSxJQUFJLGVBQWUsS0FBSztHQUNwQyxZQUFZLElBQUksZUFBZSxLQUFLO0dBQ3BDLFlBQVksaUJBQWlCLDRCQUFBLGNBQWMsYUFBYSxhQUFhLFFBQVEsQ0FBQztHQUM5RTtFQUNKO0VBRUEsTUFBTSxZQUFZLDRCQUFBLGNBQWMsYUFBYSxNQUFNLFFBQVEsQ0FBQztFQUU1RCxNQUFNLGNBQWMsSUFBSSx5Q0FBQSxRQUFrQjtFQUMxQyxZQUFZLElBQUksZUFBZSxLQUFLO0VBQ3BDLFlBQVksSUFBSSxlQUFlLEtBQUs7RUFDcEMsWUFBWSxJQUFJLGVBQWUsS0FBSztFQUNwQyxZQUFZLGlCQUFpQjtFQUU3QixNQUFNLFFBQVEsSUFDVixLQUFLLE9BQ0Esa0JBQWtCLEVBQ2xCLGNBQWMsRUFDZCxJQUFJLE9BQU8saUJBQ1IsYUFBYSxrQkFBa0IsRUFBRSxjQUFjLEVBQUUsZUFBZSxXQUFXLENBQy9FLENBQ1I7RUFFQSxNQUFNLEtBQUssSUFBSSw2Q0FBQSxRQUFzQjtFQUNyQyxHQUFHLFFBQVE7RUFFWCxHQUFHLFlBQVksZUFBZSxLQUFLO0VBQ25DLEdBQUcsWUFBWSxlQUFlLEtBQUs7RUFDbkMsR0FBRyxZQUFZLGVBQWUsS0FBSztFQUVuQyxHQUFHLFlBQVk7RUFDZixHQUFHLHdCQUF3QjtFQUUzQixNQUFNLFFBQVEsSUFDVixPQUNLLFNBQVMsRUFDVCxXQUFXLEVBQ1gsS0FBSyxXQUFXLE9BQU8sa0JBQWtCLEVBQUUsS0FBSyxFQUFFLENBQUMsQ0FDNUQ7Q0FDSjs7OztDQUtBLE1BQWEsV0FBMEI7RUFFbkMsTUFBTSxRQUFRLElBQUksS0FBSyxXQUFXLEVBQUUsS0FBSyxXQUFXLE9BQU8sa0JBQWtCLEVBQUUsU0FBUyxLQUFLLFNBQVMsQ0FBQyxDQUFDLENBQUM7Q0FDN0c7Ozs7O0NBTUEsTUFBYSxVQUFVLFFBQStCO0VBQ2xELEtBQUssU0FBUyxJQUFJLE9BQU8sYUFBYSxHQUFHLE1BQU07RUFFL0MsSUFBSSxDQUFDLE9BQU8sU0FBUyxHQUFHLE1BQU0sT0FBTyxVQUFVO09BQzFDLE1BQU0sUUFBUSxJQUFJLEtBQUssWUFBWSxFQUFFLEtBQUssTUFBTSxFQUFFLFVBQVUsTUFBZ0IsQ0FBQyxDQUFDO0NBQ3ZGOzs7OztDQU1BLE1BQWEsYUFBYSxRQUErQjtFQUNyRCxJQUFJLENBQUMsT0FBTyxTQUFTLEdBQUcsTUFBTSxPQUFPLFlBQVk7T0FDNUMsTUFBTSxRQUFRLElBQUksS0FBSyxZQUFZLEVBQUUsS0FBSyxNQUFNLEVBQUUsWUFBWSxNQUFnQixDQUFDLENBQUM7RUFFckYsS0FBSyxTQUFTLE9BQU8sT0FBTyxhQUFhLENBQUM7Q0FDOUM7Ozs7O0NBTUEsY0FBK0I7RUFDM0IsT0FBTyxNQUFNLEtBQUssS0FBSyxTQUFTLE9BQU8sQ0FBQztDQUM1Qzs7Ozs7Q0FLQ