UNPKG

@jsprismarine/prismarine

Version:

Dedicated Minecraft Bedrock Edition server written in TypeScript

412 lines (411 loc) 53.8 kB
"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