UNPKG

@jsprismarine/prismarine

Version:

Dedicated Minecraft Bedrock Edition server written in TypeScript

487 lines (486 loc) • 69.4 kB
import { Item } from "../item/Item.es.js"; import Chunk from "../world/chunk/Chunk.es.js"; import BlockPosition from "../world/BlockPosition.es.js"; import RemoveActorPacket from "./packet/RemoveActorPacket.es.js"; import TextType from "./type/TextType.es.js"; import UUID from "../utils/UUID.es.js"; import AddPlayerPacket from "./packet/AddPlayerPacket.es.js"; import AvailableCommandsPacket from "./packet/AvailableCommandsPacket.es.js"; import BatchPacket from "./packet/BatchPacket.es.js"; import ChunkRadiusUpdatedPacket from "./packet/ChunkRadiusUpdatedPacket.es.js"; import CreativeContentPacket from "./packet/CreativeContentPacket.es.js"; import DisconnectPacket from "./packet/DisconnectPacket.es.js"; import InventoryContentPacket from "./packet/InventoryContentPacket.es.js"; import LevelChunkPacket from "./packet/LevelChunkPacket.es.js"; import MobEquipmentPacket from "./packet/MobEquipmentPacket.es.js"; import MovementType from "./type/MovementType.es.js"; import MovePlayerPacket from "./packet/MovePlayerPacket.es.js"; import NetworkChunkPublisherUpdatePacket from "./packet/NetworkChunkPublisherUpdatePacket.es.js"; import PlayStatusPacket from "./packet/PlayStatusPacket.es.js"; import PlayerListPacket, { PlayerListAction, PlayerListEntry } from "./packet/PlayerListPacket.es.js"; import SetActorDataPacket from "./packet/SetActorDataPacket.es.js"; import SetPlayerGametypePacket from "./packet/SetPlayerGametypePacket.es.js"; import SetTimePacket from "./packet/SetTimePacket.es.js"; import TextPacket from "./packet/TextPacket.es.js"; import UpdateAdventureSettingsPacket from "./packet/UpdateAdventureSettingsPacket.es.js"; import UpdateAttributesPacket from "./packet/UpdateAttributesPacket.es.js"; import CommandParameter, { CommandParameterType } from "./type/CommandParameter.es.js"; import { CommandEnum } from "./type/CommandEnum.es.js"; import { CommandArgumentEntity, CommandArgumentGamemode } from "../command/CommandArguments.es.js"; import { WindowIds } from "../inventory/WindowIds.es.js"; import CoordinateUtils from "../world/CoordinateUtils.es.js"; import UpdateAbilitiesPacket, { AbilityLayer, AbilityLayerFlag, AbilityLayerType } from "./packet/UpdateAbilitiesPacket.es.js"; import CommandData from "./type/CommandData.es.js"; import PermissionType from "./type/PermissionType.es.js"; import PlayerPermissionType from "./type/PlayerPermissionType.es.js"; import { creativeitems } from "@jsprismarine/bedrock-data"; import { Gametype } from "@jsprismarine/minecraft"; import Heap from "heap"; //#region src/network/PlayerSession.ts var PlayerSession = class { connection; server; player; chunkSendQueue = []; loadedChunks = /* @__PURE__ */ new Set(); loadingChunks = /* @__PURE__ */ new Set(); constructor(server, connection, player) { this.server = server; this.connection = connection; this.player = player; } async update(_tick) { if (this.chunkSendQueue.length > 0) { const chunksToSend = this.chunkSendQueue.splice(0, Math.min(this.chunkSendQueue.length, 50)); const batch = new BatchPacket(); for (const chunk of chunksToSend) { const pk = new LevelChunkPacket(); pk.chunkX = chunk.getX(); pk.chunkZ = chunk.getZ(); pk.clientSubChunkRequestsEnabled = false; pk.subChunkCount = chunk.getTopEmpty() + 4; pk.data = chunk.networkSerialize(); batch.addPacket(pk); const hash = Chunk.packXZ(chunk.getX(), chunk.getZ()); this.loadedChunks.add(hash); this.loadingChunks.delete(hash); } this.connection.sendBatch(batch, false); } this.player.viewDistance && await this.needNewChunks(); } async send(packet) { this.connection.sendDataPacket(packet); } /** * Notify a client about change(s) to the adventure settings. * * @param player - The client-controlled entity */ async sendSettings(player) { const target = player ?? this.player; const packet = new UpdateAdventureSettingsPacket(); packet.worldImmutable = target.gamemode === Gametype.SPECTATOR; packet.noAttackingPlayers = target.gamemode === Gametype.SPECTATOR; packet.noAttackingMobs = target.gamemode === Gametype.SPECTATOR; packet.autoJump = true; packet.showNameTags = true; await this.connection.sendDataPacket(packet); } async sendAbilities(player) { const target = player ?? this.player; const mainLayer = new AbilityLayer(); mainLayer.layerType = AbilityLayerType.BASE; mainLayer.layerFlags = new Map([ [AbilityLayerFlag.FLY_SPEED, true], [AbilityLayerFlag.WALK_SPEED, true], [AbilityLayerFlag.MAY_FLY, target.metadata.canFly], [AbilityLayerFlag.FLYING, target.isFlying()], [AbilityLayerFlag.NO_CLIP, target.gamemode === Gametype.SPECTATOR], [AbilityLayerFlag.OPERATOR_COMMANDS, target.isOp()], [AbilityLayerFlag.TELEPORT, target.isOp()], [AbilityLayerFlag.INVULNERABLE, target.gamemode === Gametype.SPECTATOR], [AbilityLayerFlag.MUTED, false], [AbilityLayerFlag.WORLD_BUILDER, false], [AbilityLayerFlag.INSTABUILD, target.gamemode === Gametype.SPECTATOR], [AbilityLayerFlag.LIGHTNING, false], [AbilityLayerFlag.BUILD, target.gamemode !== Gametype.SPECTATOR], [AbilityLayerFlag.MINE, target.gamemode !== Gametype.SPECTATOR], [AbilityLayerFlag.DOORS_AND_SWITCHES, target.gamemode !== Gametype.SPECTATOR], [AbilityLayerFlag.OPEN_CONTAINERS, target.gamemode !== Gametype.SPECTATOR], [AbilityLayerFlag.ATTACK_PLAYERS, target.gamemode !== Gametype.SPECTATOR], [AbilityLayerFlag.ATTACK_MOBS, target.gamemode !== Gametype.SPECTATOR] ]); mainLayer.flySpeed = .05; mainLayer.walkSpeed = .1; const packet = new UpdateAbilitiesPacket(); packet.commandPermission = target.isOp() ? PermissionType.Operator : PermissionType.Normal; packet.playerPermission = target.isOp() ? PlayerPermissionType.Operator : PlayerPermissionType.Member; packet.targetActorUniqueId = target.getRuntimeId(); packet.abilityLayers = [mainLayer]; await this.send(packet); } async needNewChunks(forceResend = false, dist) { const currentXChunk = CoordinateUtils.fromBlockToChunk(this.player.getX()); const currentZChunk = CoordinateUtils.fromBlockToChunk(this.player.getZ()); const viewDistance = this.player.viewDistance || dist || 0; const chunksToSendHeap = new Heap((a, b) => { const distXFirst = Math.abs(a[0] - currentXChunk); const distXSecond = Math.abs(b[0] - currentXChunk); const distZFirst = Math.abs(a[1] - currentZChunk); const distZSecond = Math.abs(b[1] - currentZChunk); return distXFirst + distZFirst > distXSecond + distZSecond ? 1 : -1; }); for (let sendXChunk = -viewDistance; sendXChunk <= viewDistance; sendXChunk++) for (let sendZChunk = -viewDistance; sendZChunk <= viewDistance; sendZChunk++) { if (sendXChunk * sendXChunk + sendZChunk * sendZChunk > viewDistance * viewDistance) continue; const newChunk = [currentXChunk + sendXChunk, currentZChunk + sendZChunk]; const hash = Chunk.packXZ(newChunk[0], newChunk[1]); if (forceResend) chunksToSendHeap.push(newChunk); else if (!this.loadedChunks.has(hash) && !this.loadingChunks.has(hash)) chunksToSendHeap.push(newChunk); } while (chunksToSendHeap.size() > 0) { const closestChunk = chunksToSendHeap.pop(); const hash = Chunk.packXZ(closestChunk[0], closestChunk[1]); if (forceResend) if (!this.loadedChunks.has(hash) && !this.loadingChunks.has(hash)) { this.loadingChunks.add(hash); await this.requestChunk(closestChunk[0], closestChunk[1]); } else { const loadedChunk = await this.player.getWorld().getChunk(closestChunk[0], closestChunk[1]); await this.sendChunk(loadedChunk); } else { this.loadingChunks.add(hash); await this.requestChunk(closestChunk[0], closestChunk[1]); } } let unloaded = false; for (const hash of this.loadedChunks) { const [x, z] = Chunk.unpackXZ(hash); if (Math.abs(x - currentXChunk) > viewDistance || Math.abs(z - currentZChunk) > viewDistance) { unloaded = true; this.loadedChunks.delete(hash); } } for (const hash of this.loadingChunks) { const [x, z] = Chunk.unpackXZ(hash); if (Math.abs(x - currentXChunk) > viewDistance || Math.abs(z - currentZChunk) > viewDistance) this.loadingChunks.delete(hash); } if (!unloaded && this.chunkSendQueue.length !== 0) await this.sendNetworkChunkPublisher(dist ?? viewDistance, []); } async requestChunk(x, z) { const chunk = await this.player.getWorld().getChunk(x, z); this.chunkSendQueue.push(chunk); } /** * Clear the currently loaded and loading chunks. * * @remarks * Usually used for changing dimension, world, etc. */ async clearChunks() { this.loadedChunks.clear(); this.loadingChunks.clear(); } /** * @TODO: Implement this. */ async sendInventory() { const pk = new InventoryContentPacket(); pk.items = this.player.getInventory().getItems(true); pk.windowId = WindowIds.INVENTORY; } async sendCreativeContents(empty = false) { const pk = new CreativeContentPacket(); if (empty) { await this.connection.sendDataPacket(pk); return; } const entries = [...this.player.getServer().getBlockManager().getBlocks(), ...this.player.getServer().getItemManager().getItems()]; creativeitems.forEach((item) => { pk.items.push(...entries.filter((entry) => { return entry.meta === (item.damage ?? 0) && entry.getId() === item.id; }).map((entry) => new Item({ id: entry.getId(), name: entry.getName(), meta: entry.meta }))); }); await this.connection.sendDataPacket(pk); } /** * Sets the item in the player hand. * @param {Item} item - The entity. */ async sendHandItem(item) { const pk = new MobEquipmentPacket(); pk.runtimeEntityId = this.player.getRuntimeId(); pk.item = item; pk.inventorySlot = this.player.getInventory().getHandSlotIndex(); pk.hotbarSlot = this.player.getInventory().getHandSlotIndex(); pk.windowId = 0; await this.connection.sendDataPacket(pk); } /** * Send the current tick to a client. * @param {number} tick - The tick */ async sendTime(tick) { const pk = new SetTimePacket(); pk.time = tick; await this.connection.sendDataPacket(pk); } /** * Send gamemode to a client. * @param {Gametype} gamemode - the numeric gamemode ID. */ async sendGamemode(gamemode) { const packet = new SetPlayerGametypePacket(); packet.gametype = gamemode ?? this.player.gamemode; await this.connection.sendDataPacket(packet); } async sendNetworkChunkPublisher(distance, savedChunks) { const pk = new NetworkChunkPublisherUpdatePacket(); pk.position = BlockPosition.fromVector3(this.player.getPosition()); pk.radius = distance << 4; pk.savedChunks = savedChunks; await this.connection.sendDataPacket(pk); } async sendAvailableCommands() { const playerEnum = new CommandEnum(); playerEnum.soft = true; playerEnum.name = "Player"; playerEnum.values = this.player.getServer().getSessionManager().getAllPlayers().map((player) => player.getName()); const pk = new AvailableCommandsPacket(); pk.softEnums = [playerEnum]; this.server.getCommandManager().getCommandsList().forEach((command) => { const commandClass = Array.from(this.server.getCommandManager().getCommands().values()).find((cmd) => cmd.name === command[0]); if (!commandClass) { this.player.getServer().getLogger().warn(`Can't find corresponding command class for "${command[0]}"`); return; } if (!this.player.getServer().getPermissionManager().can(this.player).execute(commandClass.permission)) return; const cmd = new CommandData(); cmd.commandName = command[0]; cmd.commandDescription = commandClass.description; if (commandClass.aliases.length > 0) { const cmdAliases = new CommandEnum(); cmdAliases.name = `${command[0]}Aliases`; cmdAliases.values = commandClass.aliases.concat(command[0]); cmd.aliases = cmdAliases; } command[2].forEach((arg, index) => { const parameters = arg.map((parameter) => { if (!parameter || !parameter?.getParameters) return []; const parameters = parameter.getParameters(this.server); if (parameters) return Array.from(parameters.values()); if (parameter instanceof CommandArgumentEntity) return [new CommandParameter({ paramName: "target", paramType: CommandParameterType.Target })]; if (parameter instanceof CommandArgumentGamemode) return [new CommandParameter({ paramName: "gamemode", paramType: CommandParameterType.String })]; if (parameter.constructor.name === "StringArgumentType") return [new CommandParameter({ paramName: "value", paramType: CommandParameterType.String })]; if (parameter.constructor.name === "IntegerArgumentType") return [new CommandParameter({ paramName: "number", paramType: CommandParameterType.Int })]; this.server.getLogger().warn(`Invalid parameter ${parameter.constructor.name}`); return [new CommandParameter({ paramName: "value", paramType: CommandParameterType.String })]; }).flat(); cmd.overloads[index] = parameters; }); pk.commandData.push(cmd); }); await this.getConnection().sendDataPacket(pk); } /** * Set the client's maximum view distance. * * @param distance - The view distance */ async setViewDistance(distance) { const packet = new ChunkRadiusUpdatedPacket(); packet.radius = this.player.viewDistance = distance; await this.connection.sendDataPacket(packet); } async sendAttributes(attributes) { const value = attributes?.getAttributes() ?? this.player.attributes.getAttributes(); const packet = new UpdateAttributesPacket(); packet.runtimeEntityId = this.player.getRuntimeId(); packet.attributes = value.length > 0 ? value : this.player.attributes.getDefaults(); packet.tick = BigInt(this.player.getServer().getTick()); await this.connection.sendDataPacket(packet); } async sendMetadata(metadata) { const packet = new SetActorDataPacket(); packet.runtimeEntityId = this.player.getRuntimeId(); packet.metadata = metadata ?? this.player.metadata; packet.tick = BigInt(this.player.getServer().getTick()); await this.connection.sendDataPacket(packet); } /** * Send a chat message to the client. * @param {object} options - The options for the message. * @param {string} options.message - The message to send. * @param {string} [options.sourceName=''] - The source of the message. * @param {string} [options.xuid=''] - The XUID of the player. * @param {string} [options.platformChatId=''] - The platform chat ID. * @param {string[]} [options.parameters=[]] - The parameters for the message. * @param {boolean} [options.needsTranslation=false] - Whether the message needs translation. * @param {TextType} [options.type=TextType.Raw] - The type of the message. * @returns {Promise<void>} A promise that resolves when the message is sent. */ async sendMessage({ message, sourceName = "", xuid = "", platformChatId = "", parameters = [], needsTranslation = false, type = TextType.Raw }) { if (!message) throw new Error("A message is required"); const pk = new TextPacket(); pk.message = message; pk.filtered = message; pk.sourceName = sourceName; pk.xuid = xuid; pk.platformChatId = platformChatId; pk.parameters = parameters; pk.needsTranslation = needsTranslation; pk.type = type; await this.connection.sendDataPacket(pk); } async sendChunk(chunk) { const hash = Chunk.packXZ(chunk.getX(), chunk.getZ()); const packet = new LevelChunkPacket(); packet.chunkX = chunk.getX(); packet.chunkZ = chunk.getZ(); packet.clientSubChunkRequestsEnabled = false; packet.subChunkCount = chunk.getTopEmpty() + 4; packet.data = chunk.networkSerialize(); await this.send(packet); this.loadingChunks.delete(hash); this.loadedChunks.add(hash); } /** * Broadcast the movement to a defined player */ async broadcastMove(player, mode = MovementType.Normal) { const packet = new MovePlayerPacket(); packet.runtimeEntityId = player.getRuntimeId(); packet.position = player.getPosition(); packet.pitch = player.pitch; packet.yaw = player.yaw; packet.headYaw = player.headYaw; packet.mode = mode; packet.onGround = player.isOnGround(); if (mode === MovementType.Teleport) { packet.teleportCause = 0; packet.teleportItemId = 0; } packet.ridingEntityRuntimeId = BigInt(0); packet.tick = BigInt(this.player.getServer().getTick()); await this.send(packet); } /** * Adds the client to the player list of every player inside * the server and also to the player itself. */ async addToPlayerList() { const entry = new PlayerListEntry({ uuid: UUID.fromString(this.player.getUUID()), runtimeId: this.player.getRuntimeId(), name: this.player.getName(), xuid: this.player.xuid, platformChatId: this.player.platformChatId, buildPlatform: this.player.device?.os ?? -1, skin: this.player.skin, isTeacher: false, isHost: false }); this.server.getSessionManager().getPlayerList().set(this.player.getUUID(), entry); const packet = new PlayerListPacket(); packet.type = PlayerListAction.TYPE_ADD; packet.entries.push(entry); await this.server.broadcastPacket(packet); } /** * Removes a player from other players list */ async removeFromPlayerList() { const entry = new PlayerListEntry({ uuid: UUID.fromString(this.player.getUUID()) }); this.server.getSessionManager().getPlayerList().delete(this.player.getUUID()); const packet = new PlayerListPacket(); packet.type = PlayerListAction.TYPE_REMOVE; packet.entries.push(entry); await this.server.broadcastPacket(packet); } /** * Sends the full player list to the player. */ async sendPlayerList() { const packet = new PlayerListPacket(); packet.type = PlayerListAction.TYPE_ADD; packet.entries = Array.from(this.server.getSessionManager().getPlayerList().values()); await this.send(packet); } /** * Spawn the player for another player * * @param player - the player to send the AddPlayerPacket to */ async sendSpawn(player) { if (!player.getUUID()) { this.server.getLogger().error(`UUID for player ${player.getName()} is undefined`); return; } const pk = new AddPlayerPacket(); pk.uuid = UUID.fromString(this.player.getUUID()); pk.runtimeEntityId = this.player.getRuntimeId(); pk.name = this.player.getName(); pk.positionX = this.player.getX(); pk.positionY = this.player.getY(); pk.positionZ = this.player.getZ(); pk.motionX = 0; pk.motionY = 0; pk.motionZ = 0; pk.pitch = this.player.pitch; pk.yaw = this.player.yaw; pk.headYaw = this.player.headYaw; pk.gamemode = this.player.gamemode; pk.item = this.player.getInventory().getItemInHand(); pk.deviceId = this.player.device?.id ?? ""; pk.metadata = this.player.metadata; await player.getNetworkSession().send(pk); await this.sendSettings(player); } /** * Despawn the player entity from another player */ async sendDespawn(player) { const pk = new RemoveActorPacket(); pk.uniqueEntityId = this.player.getRuntimeId(); await player.getNetworkSession().getConnection().sendDataPacket(pk); } async sendPlayStatus(status) { const pk = new PlayStatusPacket(); pk.status = status; await this.connection.sendDataPacket(pk, true); } async kick(reason = "unknown reason") { const pk = new DisconnectPacket(); pk.skipMessage = false; pk.message = reason; await this.connection.sendDataPacket(pk, true); } getConnection() { return this.connection; } getPlayer() { return this.player; } }; //#endregion export { PlayerSession as default }; //# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiUGxheWVyU2Vzc2lvbi5lcy5qcyIsIm5hbWVzIjpbXSwic291cmNlcyI6WyIuLi8uLi9zcmMvbmV0d29yay9QbGF5ZXJTZXNzaW9uLnRzIl0sInNvdXJjZXNDb250ZW50IjpbImltcG9ydCB7IGNyZWF0aXZlaXRlbXMgYXMgQ3JlYXRpdmVJdGVtcyB9IGZyb20gJ0Bqc3ByaXNtYXJpbmUvYmVkcm9jay1kYXRhJztcbmltcG9ydCB7IEdhbWV0eXBlIH0gZnJvbSAnQGpzcHJpc21hcmluZS9taW5lY3JhZnQnO1xuaW1wb3J0IEhlYXAgZnJvbSAnaGVhcCc7XG5pbXBvcnQgdHlwZSBQbGF5ZXIgZnJvbSAnLi4vUGxheWVyJztcbmltcG9ydCB0eXBlIFNlcnZlciBmcm9tICcuLi9TZXJ2ZXInO1xuaW1wb3J0IHR5cGUgeyBDb21tYW5kQXJndW1lbnQgfSBmcm9tICcuLi9jb21tYW5kL0NvbW1hbmRBcmd1bWVudHMnO1xuaW1wb3J0IHsgQ29tbWFuZEFyZ3VtZW50RW50aXR5LCBDb21tYW5kQXJndW1lbnRHYW1lbW9kZSB9IGZyb20gJy4uL2NvbW1hbmQvQ29tbWFuZEFyZ3VtZW50cyc7XG5pbXBvcnQgdHlwZSB7IEF0dHJpYnV0ZXMgfSBmcm9tICcuLi9lbnRpdHkvQXR0cmlidXRlJztcbmltcG9ydCB0eXBlIHsgTWV0YWRhdGEgfSBmcm9tICcuLi9lbnRpdHkvTWV0YWRhdGEnO1xuaW1wb3J0IHsgV2luZG93SWRzIH0gZnJvbSAnLi4vaW52ZW50b3J5L1dpbmRvd0lkcyc7XG5pbXBvcnQgeyBJdGVtIH0gZnJvbSAnLi4vaXRlbS9JdGVtJztcbmltcG9ydCBVVUlEIGZyb20gJy4uL3V0aWxzL1VVSUQnO1xuaW1wb3J0IEJsb2NrUG9zaXRpb24gZnJvbSAnLi4vd29ybGQvQmxvY2tQb3NpdGlvbic7XG5pbXBvcnQgQ29vcmRpbmF0ZVV0aWxzIGZyb20gJy4uL3dvcmxkL0Nvb3JkaW5hdGVVdGlscyc7XG5pbXBvcnQgQ2h1bmsgZnJvbSAnLi4vd29ybGQvY2h1bmsvQ2h1bmsnO1xuaW1wb3J0IHR5cGUgQ2xpZW50Q29ubmVjdGlvbiBmcm9tICcuL0NsaWVudENvbm5lY3Rpb24nO1xuaW1wb3J0IHR5cGUgeyBEYXRhUGFja2V0IH0gZnJvbSAnLi9QYWNrZXRzJztcbmltcG9ydCB7IEJhdGNoUGFja2V0IH0gZnJvbSAnLi9QYWNrZXRzJztcbmltcG9ydCBBZGRQbGF5ZXJQYWNrZXQgZnJvbSAnLi9wYWNrZXQvQWRkUGxheWVyUGFja2V0JztcbmltcG9ydCBBdmFpbGFibGVDb21tYW5kc1BhY2tldCBmcm9tICcuL3BhY2tldC9BdmFpbGFibGVDb21tYW5kc1BhY2tldCc7XG5pbXBvcnQgQ2h1bmtSYWRpdXNVcGRhdGVkUGFja2V0IGZyb20gJy4vcGFja2V0L0NodW5rUmFkaXVzVXBkYXRlZFBhY2tldCc7XG5pbXBvcnQgQ3JlYXRpdmVDb250ZW50UGFja2V0IGZyb20gJy4vcGFja2V0L0NyZWF0aXZlQ29udGVudFBhY2tldCc7XG5pbXBvcnQgRGlzY29ubmVjdFBhY2tldCBmcm9tICcuL3BhY2tldC9EaXNjb25uZWN0UGFja2V0JztcbmltcG9ydCBJbnZlbnRvcnlDb250ZW50UGFja2V0IGZyb20gJy4vcGFja2V0L0ludmVudG9yeUNvbnRlbnRQYWNrZXQnO1xuaW1wb3J0IExldmVsQ2h1bmtQYWNrZXQgZnJvbSAnLi9wYWNrZXQvTGV2ZWxDaHVua1BhY2tldCc7XG5pbXBvcnQgTW9iRXF1aXBtZW50UGFja2V0IGZyb20gJy4vcGFja2V0L01vYkVxdWlwbWVudFBhY2tldCc7XG5pbXBvcnQgTW92ZVBsYXllclBhY2tldCBmcm9tICcuL3BhY2tldC9Nb3ZlUGxheWVyUGFja2V0JztcbmltcG9ydCB0eXBlIHsgQ2h1bmtDb29yZCB9IGZyb20gJy4vcGFja2V0L05ldHdvcmtDaHVua1B1Ymxpc2hlclVwZGF0ZVBhY2tldCc7XG5pbXBvcnQgTmV0d29ya0NodW5rUHVibGlzaGVyVXBkYXRlUGFja2V0IGZyb20gJy4vcGFja2V0L05ldHdvcmtDaHVua1B1Ymxpc2hlclVwZGF0ZVBhY2tldCc7XG5pbXBvcnQgUGxheVN0YXR1c1BhY2tldCBmcm9tICcuL3BhY2tldC9QbGF5U3RhdHVzUGFja2V0JztcbmltcG9ydCBQbGF5ZXJMaXN0UGFja2V0LCB7IFBsYXllckxpc3RBY3Rpb24sIFBsYXllckxpc3RFbnRyeSB9IGZyb20gJy4vcGFja2V0L1BsYXllckxpc3RQYWNrZXQnO1xuaW1wb3J0IFJlbW92ZUFjdG9yUGFja2V0IGZyb20gJy4vcGFja2V0L1JlbW92ZUFjdG9yUGFja2V0JztcbmltcG9ydCBTZXRBY3RvckRhdGFQYWNrZXQgZnJvbSAnLi9wYWNrZXQvU2V0QWN0b3JEYXRhUGFja2V0JztcbmltcG9ydCBTZXRQbGF5ZXJHYW1ldHlwZVBhY2tldCBmcm9tICcuL3BhY2tldC9TZXRQbGF5ZXJHYW1ldHlwZVBhY2tldCc7XG5pbXBvcnQgU2V0VGltZVBhY2tldCBmcm9tICcuL3BhY2tldC9TZXRUaW1lUGFja2V0JztcbmltcG9ydCBUZXh0UGFja2V0IGZyb20gJy4vcGFja2V0L1RleHRQYWNrZXQnO1xuaW1wb3J0IFVwZGF0ZUFiaWxpdGllc1BhY2tldCwge1xuICAgIEFiaWxpdHlMYXllcixcbiAgICBBYmlsaXR5TGF5ZXJGbGFnLFxuICAgIEFiaWxpdHlMYXllclR5cGVcbn0gZnJvbSAnLi9wYWNrZXQvVXBkYXRlQWJpbGl0aWVzUGFja2V0JztcbmltcG9ydCBVcGRhdGVBZHZlbnR1cmVTZXR0aW5nc1BhY2tldCBmcm9tICcuL3BhY2tldC9VcGRhdGVBZHZlbnR1cmVTZXR0aW5nc1BhY2tldCc7XG5pbXBvcnQgVXBkYXRlQXR0cmlidXRlc1BhY2tldCBmcm9tICcuL3BhY2tldC9VcGRhdGVBdHRyaWJ1dGVzUGFja2V0JztcbmltcG9ydCBDb21tYW5kRGF0YSBmcm9tICcuL3R5cGUvQ29tbWFuZERhdGEnO1xuaW1wb3J0IHsgQ29tbWFuZEVudW0gfSBmcm9tICcuL3R5cGUvQ29tbWFuZEVudW0nO1xuaW1wb3J0IENvbW1hbmRQYXJhbWV0ZXIsIHsgQ29tbWFuZFBhcmFtZXRlclR5cGUgfSBmcm9tICcuL3R5cGUvQ29tbWFuZFBhcmFtZXRlcic7XG5pbXBvcnQgTW92ZW1lbnRUeXBlIGZyb20gJy4vdHlwZS9Nb3ZlbWVudFR5cGUnO1xuaW1wb3J0IFBlcm1pc3Npb25UeXBlIGZyb20gJy4vdHlwZS9QZXJtaXNzaW9uVHlwZSc7XG5pbXBvcnQgUGxheWVyUGVybWlzc2lvblR5cGUgZnJvbSAnLi90eXBlL1BsYXllclBlcm1pc3Npb25UeXBlJztcbmltcG9ydCBUZXh0VHlwZSBmcm9tICcuL3R5cGUvVGV4dFR5cGUnO1xuXG5leHBvcnQgZGVmYXVsdCBjbGFzcyBQbGF5ZXJTZXNzaW9uIHtcbiAgICBwcml2YXRlIGNvbm5lY3Rpb246IENsaWVudENvbm5lY3Rpb247XG4gICAgcHJpdmF0ZSByZWFkb25seSBzZXJ2ZXI6IFNlcnZlcjtcbiAgICBwcml2YXRlIHBsYXllcjogUGxheWVyO1xuXG4gICAgcHJpdmF0ZSByZWFkb25seSBjaHVua1NlbmRRdWV1ZTogQ2h1bmtbXSA9IFtdO1xuICAgIHByaXZhdGUgcmVhZG9ubHkgbG9hZGVkQ2h1bmtzOiBTZXQ8YmlnaW50PiA9IG5ldyBTZXQoKTtcbiAgICBwcml2YXRlIHJlYWRvbmx5IGxvYWRpbmdDaHVua3M6IFNldDxiaWdpbnQ+ID0gbmV3IFNldCgpO1xuXG4gICAgcHVibGljIGNvbnN0cnVjdG9yKHNlcnZlcjogU2VydmVyLCBjb25uZWN0aW9uOiBDbGllbnRDb25uZWN0aW9uLCBwbGF5ZXI6IFBsYXllcikge1xuICAgICAgICB0aGlzLnNlcnZlciA9IHNlcnZlcjtcbiAgICAgICAgdGhpcy5jb25uZWN0aW9uID0gY29ubmVjdGlvbjtcbiAgICAgICAgdGhpcy5wbGF5ZXIgPSBwbGF5ZXI7XG4gICAgfVxuXG4gICAgcHVibGljIGFzeW5jIHVwZGF0ZShfdGljazogbnVtYmVyKTogUHJvbWlzZTx2b2lkPiB7XG4gICAgICAgIGlmICh0aGlzLmNodW5rU2VuZFF1ZXVlLmxlbmd0aCA+IDApIHtcbiAgICAgICAgICAgIGNvbnN0IGNodW5rc1RvU2VuZCA9IHRoaXMuY2h1bmtTZW5kUXVldWUuc3BsaWNlKDAsIE1hdGgubWluKHRoaXMuY2h1bmtTZW5kUXVldWUubGVuZ3RoLCA1MCkpO1xuICAgICAgICAgICAgY29uc3QgYmF0Y2ggPSBuZXcgQmF0Y2hQYWNrZXQoKTtcbiAgICAgICAgICAgIGZvciAoY29uc3QgY2h1bmsgb2YgY2h1bmtzVG9TZW5kKSB7XG4gICAgICAgICAgICAgICAgY29uc3QgcGsgPSBuZXcgTGV2ZWxDaHVua1BhY2tldCgpO1xuICAgICAgICAgICAgICAgIHBrLmNodW5rWCA9IGNodW5rLmdldFgoKTtcbiAgICAgICAgICAgICAgICBway5jaHVua1ogPSBjaHVuay5nZXRaKCk7XG4gICAgICAgICAgICAgICAgcGsuY2xpZW50U3ViQ2h1bmtSZXF1ZXN0c0VuYWJsZWQgPSBmYWxzZTtcbiAgICAgICAgICAgICAgICBway5zdWJDaHVua0NvdW50ID0gY2h1bmsuZ2V0VG9wRW1wdHkoKSArIDQ7XG4gICAgICAgICAgICAgICAgcGsuZGF0YSA9IGNodW5rLm5ldHdvcmtTZXJpYWxpemUoKTtcbiAgICAgICAgICAgICAgICBiYXRjaC5hZGRQYWNrZXQocGspO1xuXG4gICAgICAgICAgICAgICAgY29uc3QgaGFzaCA9IENodW5rLnBhY2tYWihjaHVuay5nZXRYKCksIGNodW5rLmdldFooKSk7XG4gICAgICAgICAgICAgICAgdGhpcy5sb2FkZWRDaHVua3MuYWRkKGhhc2gpO1xuICAgICAgICAgICAgICAgIHRoaXMubG9hZGluZ0NodW5rcy5kZWxldGUoaGFzaCk7XG4gICAgICAgICAgICB9XG4gICAgICAgICAgICB0aGlzLmNvbm5lY3Rpb24uc2VuZEJhdGNoKGJhdGNoLCBmYWxzZSk7XG4gICAgICAgIH1cblxuICAgICAgICB0aGlzLnBsYXllci52aWV3RGlzdGFuY2UgJiYgKGF3YWl0IHRoaXMubmVlZE5ld0NodW5rcygpKTtcbiAgICB9XG5cbiAgICBwdWJsaWMgYXN5bmMgc2VuZChwYWNrZXQ6IERhdGFQYWNrZXQpIHtcbiAgICAgICAgdGhpcy5jb25uZWN0aW9uLnNlbmREYXRhUGFja2V0KHBhY2tldCk7XG4gICAgfVxuXG4gICAgLyoqXG4gICAgICogTm90aWZ5IGEgY2xpZW50IGFib3V0IGNoYW5nZShzKSB0byB0aGUgYWR2ZW50dXJlIHNldHRpbmdzLlxuICAgICAqXG4gICAgICogQHBhcmFtIHBsYXllciAtIFRoZSBjbGllbnQtY29udHJvbGxlZCBlbnRpdHlcbiAgICAgKi9cbiAgICBwdWJsaWMgYXN5bmMgc2VuZFNldHRpbmdzKHBsYXllcj86IFBsYXllcik6IFByb21pc2U8dm9pZD4ge1xuICAgICAgICBjb25zdCB0YXJnZXQgPSBwbGF5ZXIgPz8gdGhpcy5wbGF5ZXI7XG5cbiAgICAgICAgY29uc3QgcGFja2V0ID0gbmV3IFVwZGF0ZUFkdmVudHVyZVNldHRpbmdzUGFja2V0KCk7XG4gICAgICAgIHBhY2tldC53b3JsZEltbXV0YWJsZSA9IHRhcmdldC5nYW1lbW9kZSA9PT0gR2FtZXR5cGUuU1BFQ1RBVE9SO1xuICAgICAgICBwYWNrZXQubm9BdHRhY2tpbmdQbGF5ZXJzID0gdGFyZ2V0LmdhbWVtb2RlID09PSBHYW1ldHlwZS5TUEVDVEFUT1I7XG4gICAgICAgIHBhY2tldC5ub0F0dGFja2luZ01vYnMgPSB0YXJnZXQuZ2FtZW1vZGUgPT09IEdhbWV0eXBlLlNQRUNUQVRPUjtcbiAgICAgICAgcGFja2V0LmF1dG9KdW1wID0gdHJ1ZTtcbiAgICAgICAgcGFja2V0LnNob3dOYW1lVGFncyA9IHRydWU7XG4gICAgICAgIGF3YWl0IHRoaXMuY29ubmVjdGlvbi5zZW5kRGF0YVBhY2tldChwYWNrZXQpO1xuICAgIH1cblxuICAgIHB1YmxpYyBhc3luYyBzZW5kQWJpbGl0aWVzKHBsYXllcj86IFBsYXllcik6IFByb21pc2U8dm9pZD4ge1xuICAgICAgICBjb25zdCB0YXJnZXQgPSBwbGF5ZXIgPz8gdGhpcy5wbGF5ZXI7XG5cbiAgICAgICAgY29uc3QgbWFpbkxheWVyID0gbmV3IEFiaWxpdHlMYXllcigpO1xuICAgICAgICBtYWluTGF5ZXIubGF5ZXJUeXBlID0gQWJpbGl0eUxheWVyVHlwZS5CQVNFO1xuICAgICAgICBtYWluTGF5ZXIubGF5ZXJGbGFncyA9IG5ldyBNYXAoW1xuICAgICAgICAgICAgW0FiaWxpdHlMYXllckZsYWcuRkxZX1NQRUVELCB0cnVlXSxcbiAgICAgICAgICAgIFtBYmlsaXR5TGF5ZXJGbGFnLldBTEtfU1BFRUQsIHRydWVdLFxuICAgICAgICAgICAgW0FiaWxpdHlMYXllckZsYWcuTUFZX0ZMWSwgdGFyZ2V0Lm1ldGFkYXRhLmNhbkZseV0sXG4gICAgICAgICAgICBbQWJpbGl0eUxheWVyRmxhZy5GTFlJTkcsIHRhcmdldC5pc0ZseWluZygpXSxcbiAgICAgICAgICAgIFtBYmlsaXR5TGF5ZXJGbGFnLk5PX0NMSVAsIHRhcmdldC5nYW1lbW9kZSA9PT0gR2FtZXR5cGUuU1BFQ1RBVE9SXSxcbiAgICAgICAgICAgIFtBYmlsaXR5TGF5ZXJGbGFnLk9QRVJBVE9SX0NPTU1BTkRTLCB0YXJnZXQuaXNPcCgpXSxcbiAgICAgICAgICAgIFtBYmlsaXR5TGF5ZXJGbGFnLlRFTEVQT1JULCB0YXJnZXQuaXNPcCgpXSxcbiAgICAgICAgICAgIFtBYmlsaXR5TGF5ZXJGbGFnLklOVlVMTkVSQUJMRSwgdGFyZ2V0LmdhbWVtb2RlID09PSBHYW1ldHlwZS5TUEVDVEFUT1JdLFxuICAgICAgICAgICAgW0FiaWxpdHlMYXllckZsYWcuTVVURUQsIGZhbHNlXSxcbiAgICAgICAgICAgIFtBYmlsaXR5TGF5ZXJGbGFnLldPUkxEX0JVSUxERVIsIGZhbHNlXSxcbiAgICAgICAgICAgIFtBYmlsaXR5TGF5ZXJGbGFnLklOU1RBQlVJTEQsIHRhcmdldC5nYW1lbW9kZSA9PT0gR2FtZXR5cGUuU1BFQ1RBVE9SXSxcbiAgICAgICAgICAgIFtBYmlsaXR5TGF5ZXJGbGFnLkxJR0hUTklORywgZmFsc2VdLFxuICAgICAgICAgICAgW0FiaWxpdHlMYXllckZsYWcuQlVJTEQsIHRhcmdldC5nYW1lbW9kZSAhPT0gR2FtZXR5cGUuU1BFQ1RBVE9SXSxcbiAgICAgICAgICAgIFtBYmlsaXR5TGF5ZXJGbGFnLk1JTkUsIHRhcmdldC5nYW1lbW9kZSAhPT0gR2FtZXR5cGUuU1BFQ1RBVE9SXSxcbiAgICAgICAgICAgIFtBYmlsaXR5TGF5ZXJGbGFnLkRPT1JTX0FORF9TV0lUQ0hFUywgdGFyZ2V0LmdhbWVtb2RlICE9PSBHYW1ldHlwZS5TUEVDVEFUT1JdLFxuICAgICAgICAgICAgW0FiaWxpdHlMYXllckZsYWcuT1BFTl9DT05UQUlORVJTLCB0YXJnZXQuZ2FtZW1vZGUgIT09IEdhbWV0eXBlLlNQRUNUQVRPUl0sXG4gICAgICAgICAgICBbQWJpbGl0eUxheWVyRmxhZy5BVFRBQ0tfUExBWUVSUywgdGFyZ2V0LmdhbWVtb2RlICE9PSBHYW1ldHlwZS5TUEVDVEFUT1JdLFxuICAgICAgICAgICAgW0FiaWxpdHlMYXllckZsYWcuQVRUQUNLX01PQlMsIHRhcmdldC5nYW1lbW9kZSAhPT0gR2FtZXR5cGUuU1BFQ1RBVE9SXVxuICAgICAgICBdKTtcbiAgICAgICAgbWFpbkxheWVyLmZseVNwZWVkID0gMC4wNTtcbiAgICAgICAgbWFpbkxheWVyLndhbGtTcGVlZCA9IDAuMTtcblxuICAgICAgICBjb25zdCBwYWNrZXQgPSBuZXcgVXBkYXRlQWJpbGl0aWVzUGFja2V0KCk7XG4gICAgICAgIHBhY2tldC5jb21tYW5kUGVybWlzc2lvbiA9IHRhcmdldC5pc09wKCkgPyBQZXJtaXNzaW9uVHlwZS5PcGVyYXRvciA6IFBlcm1pc3Npb25UeXBlLk5vcm1hbDtcbiAgICAgICAgcGFja2V0LnBsYXllclBlcm1pc3Npb24gPSB0YXJnZXQuaXNPcCgpID8gUGxheWVyUGVybWlzc2lvblR5cGUuT3BlcmF0b3IgOiBQbGF5ZXJQZXJtaXNzaW9uVHlwZS5NZW1iZXI7XG4gICAgICAgIHBhY2tldC50YXJnZXRBY3RvclVuaXF1ZUlkID0gdGFyZ2V0LmdldFJ1bnRpbWVJZCgpO1xuICAgICAgICBwYWNrZXQuYWJpbGl0eUxheWVycyA9IFttYWluTGF5ZXJdO1xuICAgICAgICBhd2FpdCB0aGlzLnNlbmQocGFja2V0KTtcbiAgICB9XG5cbiAgICBwdWJsaWMgYXN5bmMgbmVlZE5ld0NodW5rcyhmb3JjZVJlc2VuZCA9IGZhbHNlLCBkaXN0PzogbnVtYmVyKTogUHJvbWlzZTx2b2lkPiB7XG4gICAgICAgIGNvbnN0IGN1cnJlbnRYQ2h1bmsgPSBDb29yZGluYXRlVXRpbHMuZnJvbUJsb2NrVG9DaHVuayh0aGlzLnBsYXllci5nZXRYKCkpO1xuICAgICAgICBjb25zdCBjdXJyZW50WkNodW5rID0gQ29vcmRpbmF0ZVV0aWxzLmZyb21CbG9ja1RvQ2h1bmsodGhpcy5wbGF5ZXIuZ2V0WigpKTtcblxuICAgICAgICBjb25zdCB2aWV3RGlzdGFuY2UgPSB0aGlzLnBsYXllci52aWV3RGlzdGFuY2UgfHwgZGlzdCB8fCAwO1xuXG4gICAgICAgIGNvbnN0IGNodW5rc1RvU2VuZEhlYXAgPSBuZXcgSGVhcCgoYTogbnVtYmVyW10sIGI6IG51bWJlcltdKSA9PiB7XG4gICAgICAgICAgICBjb25zdCBkaXN0WEZpcnN0ID0gTWF0aC5hYnMoYVswXSEgLSBjdXJyZW50WENodW5rKTtcbiAgICAgICAgICAgIGNvbnN0IGRpc3RYU2Vjb25kID0gTWF0aC5hYnMoYlswXSEgLSBjdXJyZW50WENodW5rKTtcblxuICAgICAgICAgICAgY29uc3QgZGlzdFpGaXJzdCA9IE1hdGguYWJzKGFbMV0hIC0gY3VycmVudFpDaHVuayk7XG4gICAgICAgICAgICBjb25zdCBkaXN0WlNlY29uZCA9IE1hdGguYWJzKGJbMV0hIC0gY3VycmVudFpDaHVuayk7XG5cbiAgICAgICAgICAgIHJldHVybiBkaXN0WEZpcnN0ICsgZGlzdFpGaXJzdCA+IGRpc3RYU2Vjb25kICsgZGlzdFpTZWNvbmQgPyAxIDogLTE7XG4gICAgICAgIH0pO1xuXG4gICAgICAgIGZvciAobGV0IHNlbmRYQ2h1bmsgPSAtdmlld0Rpc3RhbmNlOyBzZW5kWENodW5rIDw9IHZpZXdEaXN0YW5jZTsgc2VuZFhDaHVuaysrKSB7XG4gICAgICAgICAgICBmb3IgKGxldCBzZW5kWkNodW5rID0gLXZpZXdEaXN0YW5jZTsgc2VuZFpDaHVuayA8PSB2aWV3RGlzdGFuY2U7IHNlbmRaQ2h1bmsrKykge1xuICAgICAgICAgICAgICAgIGlmIChzZW5kWENodW5rICogc2VuZFhDaHVuayArIHNlbmRaQ2h1bmsgKiBzZW5kWkNodW5rID4gdmlld0Rpc3RhbmNlICogdmlld0Rpc3RhbmNlKSBjb250aW51ZTsgLy8gZWFybHkgZXhpdCBpZiBjaHVuayBpcyBvdXRzaWRlIG9mIHZpZXcgZGlzdGFuY2VcbiAgICAgICAgICAgICAgICBjb25zdCBuZXdDaHVuayA9IFtjdXJyZW50WENodW5rICsgc2VuZFhDaHVuaywgY3VycmVudFpDaHVuayArIHNlbmRaQ2h1bmtdO1xuICAgICAgICAgICAgICAgIGNvbnN0IGhhc2ggPSBDaHVuay5wYWNrWFoobmV3Q2h1bmtbMF0hLCBuZXdDaHVua1sxXSEpO1xuXG4gICAgICAgICAgICAgICAgaWYgKGZvcmNlUmVzZW5kKSB7XG4gICAgICAgICAgICAgICAgICAgIGNodW5rc1RvU2VuZEhlYXAucHVzaChuZXdDaHVuayk7XG4gICAgICAgICAgICAgICAgfSBlbHNlIGlmICghdGhpcy5sb2FkZWRDaHVua3MuaGFzKGhhc2gpICYmICF0aGlzLmxvYWRpbmdDaHVua3MuaGFzKGhhc2gpKSB7XG4gICAgICAgICAgICAgICAgICAgIGNodW5rc1RvU2VuZEhlYXAucHVzaChuZXdDaHVuayk7XG4gICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgfVxuICAgICAgICB9XG5cbiAgICAgICAgd2hpbGUgKGNodW5rc1RvU2VuZEhlYXAuc2l6ZSgpID4gMCkge1xuICAgICAgICAgICAgY29uc3QgY2xvc2VzdENodW5rID0gY2h1bmtzVG9TZW5kSGVhcC5wb3AoKSE7XG4gICAgICAgICAgICBjb25zdCBoYXNoID0gQ2h1bmsucGFja1haKGNsb3Nlc3RDaHVua1swXSEsIGNsb3Nlc3RDaHVua1sxXSEpO1xuICAgICAgICAgICAgaWYgKGZvcmNlUmVzZW5kKSB7XG4gICAgICAgICAgICAgICAgaWYgKCF0aGlzLmxvYWRlZENodW5rcy5oYXMoaGFzaCkgJiYgIXRoaXMubG9hZGluZ0NodW5rcy5oYXMoaGFzaCkpIHtcbiAgICAgICAgICAgICAgICAgICAgdGhpcy5sb2FkaW5nQ2h1bmtzLmFkZChoYXNoKTtcbiAgICAgICAgICAgICAgICAgICAgYXdhaXQgdGhpcy5yZXF1ZXN0Q2h1bmsoY2xvc2VzdENodW5rWzBdISwgY2xvc2VzdENodW5rWzFdISk7XG4gICAgICAgICAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgICAgICAgICAgY29uc3QgbG9hZGVkQ2h1bmsgPSBhd2FpdCB0aGlzLnBsYXllci5nZXRXb3JsZCgpLmdldENodW5rKGNsb3Nlc3RDaHVua1swXSEsIGNsb3Nlc3RDaHVua1sxXSEpO1xuICAgICAgICAgICAgICAgICAgICBhd2FpdCB0aGlzLnNlbmRDaHVuayhsb2FkZWRDaHVuayk7XG4gICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgICAgICB0aGlzLmxvYWRpbmdDaHVua3MuYWRkKGhhc2gpO1xuICAgICAgICAgICAgICAgIGF3YWl0IHRoaXMucmVxdWVzdENodW5rKGNsb3Nlc3RDaHVua1swXSEsIGNsb3Nlc3RDaHVua1sxXSEpO1xuICAgICAgICAgICAgfVxuICAgICAgICB9XG5cbiAgICAgICAgbGV0IHVubG9hZGVkID0gZmFsc2U7XG5cbiAgICAgICAgZm9yIChjb25zdCBoYXNoIG9mIHRoaXMubG9hZGVkQ2h1bmtzKSB7XG4gICAgICAgICAgICBjb25zdCBbeCwgel0gPSBDaHVuay51bnBhY2tYWihoYXNoKTtcblxuICAgICAgICAgICAgaWYgKE1hdGguYWJzKHghIC0gY3VycmVudFhDaHVuaykgPiB2aWV3RGlzdGFuY2UgfHwgTWF0aC5hYnMoeiEgLSBjdXJyZW50WkNodW5rKSA+IHZpZXdEaXN0YW5jZSkge1xuICAgICAgICAgICAgICAgIHVubG9hZGVkID0gdHJ1ZTtcbiAgICAgICAgICAgICAgICB0aGlzLmxvYWRlZENodW5rcy5kZWxldGUoaGFzaCk7XG4gICAgICAgICAgICB9XG4gICAgICAgIH1cblxuICAgICAgICBmb3IgKGNvbnN0IGhhc2ggb2YgdGhpcy5sb2FkaW5nQ2h1bmtzKSB7XG4gICAgICAgICAgICBjb25zdCBbeCwgel0gPSBDaHVuay51bnBhY2tYWihoYXNoKTtcblxuICAgICAgICAgICAgaWYgKE1hdGguYWJzKHghIC0gY3VycmVudFhDaHVuaykgPiB2aWV3RGlzdGFuY2UgfHwgTWF0aC5hYnMoeiEgLSBjdXJyZW50WkNodW5rKSA+IHZpZXdEaXN0YW5jZSkge1xuICAgICAgICAgICAgICAgIHRoaXMubG9hZGluZ0NodW5rcy5kZWxldGUoaGFzaCk7XG4gICAgICAgICAgICB9XG4gICAgICAgIH1cblxuICAgICAgICBpZiAoIXVubG9hZGVkICYmIHRoaXMuY2h1bmtTZW5kUXVldWUubGVuZ3RoICE9PSAwKSB7XG4gICAgICAgICAgICBhd2FpdCB0aGlzLnNlbmROZXR3b3JrQ2h1bmtQdWJsaXNoZXIoZGlzdCA/PyB2aWV3RGlzdGFuY2UsIFtdKTtcbiAgICAgICAgfVxuICAgIH1cblxuICAgIHB1YmxpYyBhc3luYyByZXF1ZXN0Q2h1bmsoeDogbnVtYmVyLCB6OiBudW1iZXIpOiBQcm9taXNlPHZvaWQ+IHtcbiAgICAgICAgY29uc3QgY2h1bmsgPSBhd2FpdCB0aGlzLnBsYXllci5nZXRXb3JsZCgpLmdldENodW5rKHgsIHopO1xuICAgICAgICB0aGlzLmNodW5rU2VuZFF1ZXVlLnB1c2goY2h1bmspO1xuICAgIH1cblxuICAgIC8qKlxuICAgICAqIENsZWFyIHRoZSBjdXJyZW50bHkgbG9hZGVkIGFuZCBsb2FkaW5nIGNodW5rcy5cbiAgICAgKlxuICAgICAqIEByZW1hcmtzXG4gICAgICogVXN1YWxseSB1c2VkIGZvciBjaGFuZ2luZyBkaW1lbnNpb24sIHdvcmxkLCBldGMuXG4gICAgICovXG4gICAgcHVibGljIGFzeW5jIGNsZWFyQ2h1bmtzKCkge1xuICAgICAgICB0aGlzLmxvYWRlZENodW5rcy5jbGVhcigpO1xuICAgICAgICB0aGlzLmxvYWRpbmdDaHVua3MuY2xlYXIoKTtcbiAgICB9XG5cbiAgICAvKipcbiAgICAgKiBAVE9ETzogSW1wbGVtZW50IHRoaXMuXG4gICAgICovXG4gICAgcHVibGljIGFzeW5jIHNlbmRJbnZlbnRvcnkoKTogUHJvbWlzZTx2b2lkPiB7XG4gICAgICAgIGNvbnN0IHBrID0gbmV3IEludmVudG9yeUNvbnRlbnRQYWNrZXQoKTtcbiAgICAgICAgcGsuaXRlbXMgPSB0aGlzLnBsYXllci5nZXRJbnZlbnRvcnkoKS5nZXRJdGVtcyh0cnVlKTtcbiAgICAgICAgcGsud2luZG93SWQgPSBXaW5kb3dJZHMuSU5WRU5UT1JZOyAvLyBJbnZlbnRvcnkgd2luZG93XG4gICAgICAgIC8vYXdhaXQgdGhpcy5jb25uZWN0aW9uLnNlbmREYXRhUGFja2V0KHBrKTtcbiAgICAgICAgLy9hd2FpdCB0aGlzLnNlbmRIYW5kSXRlbSh0aGlzLnBsYXllci5nZXRJbnZlbnRvcnkoKS5nZXRJdGVtSW5IYW5kKCkpO1xuICAgIH1cblxuICAgIHB1YmxpYyBhc3luYyBzZW5kQ3JlYXRpdmVDb250ZW50cyhlbXB0eSA9IGZhbHNlKTogUHJvbWlzZTx2b2lkPiB7XG4gICAgICAgIGNvbnN0IHBrID0gbmV3IENyZWF0aXZlQ29udGVudFBhY2tldCgpO1xuICAgICAgICBpZiAoZW1wdHkpIHtcbiAgICAgICAgICAgIGF3YWl0IHRoaXMuY29ubmVjdGlvbi5zZW5kRGF0YVBhY2tldChwayk7XG4gICAgICAgICAgICByZXR1cm47XG4gICAgICAgIH1cblxuICAgICAgICBjb25zdCBlbnRyaWVzID0gW1xuICAgICAgICAgICAgLi4udGhpcy5wbGF5ZXIuZ2V0U2VydmVyKCkuZ2V0QmxvY2tNYW5hZ2VyKCkuZ2V0QmxvY2tzKCksXG4gICAgICAgICAgICAuLi50aGlzLnBsYXllci5nZXRTZXJ2ZXIoKS5nZXRJdGVtTWFuYWdlcigpLmdldEl0ZW1zKClcbiAgICAgICAgXTtcblxuICAgICAgICAvLyBTb3J0IGJhc2VkIG9uIFBtbVAgQmVkcm9jay1kYXRhXG4gICAgICAgIENyZWF0aXZlSXRlbXMuZm9yRWFjaCgoaXRlbTogYW55KSA9PiB7XG4gICAgICAgICAgICBway5pdGVtcy5wdXNoKFxuICAgICAgICAgICAgICAgIC4uLmVudHJpZXNcbiAgICAgICAgICAgICAgICAgICAgLmZpbHRlcigoZW50cnk6IGFueSkgPT4ge1xuICAgICAgICAgICAgICAgICAgICAgICAgcmV0dXJuIGVudHJ5Lm1ldGEgPT09IChpdGVtLmRhbWFnZSA/PyAwKSAmJiBlbnRyeS5nZXRJZCgpID09PSBpdGVtLmlkO1xuICAgICAgICAgICAgICAgICAgICB9KVxuICAgICAgICAgICAgICAgICAgICAubWFwKFxuICAgICAgICAgICAgICAgICAgICAgICAgKGVudHJ5KSA9PlxuICAgICAgICAgICAgICAgICAgICAgICAgICAgIG5ldyBJdGVtKHtcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgaWQ6IGVudHJ5LmdldElkKCksXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG5hbWU6IGVudHJ5LmdldE5hbWUoKSxcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbWV0YTogZW50cnkubWV0YVxuICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0pXG4gICAgICAgICAgICAgICAgICAgIClcbiAgICAgICAgICAgICk7XG4gICAgICAgIH0pO1xuXG4gICAgICAgIGF3YWl0IHRoaXMuY29ubmVjdGlvbi5zZW5kRGF0YVBhY2tldChwayk7XG4gICAgfVxuXG4gICAgLyoqXG4gICAgICogU2V0cyB0aGUgaXRlbSBpbiB0aGUgcGxheWVyIGhhbmQuXG4gICAgICogQHBhcmFtIHtJdGVtfSBpdGVtIC0gVGhlIGVudGl0eS5cbiAgICAgKi9cbiAgICBwdWJsaWMgYXN5bmMgc2VuZEhhbmRJdGVtKGl0ZW06IEl0ZW0pOiBQcm9taXNlPHZvaWQ+IHtcbiAgICAgICAgY29uc3QgcGsgPSBuZXcgTW9iRXF1aXBtZW50UGFja2V0KCk7XG4gICAgICAgIHBrLnJ1bnRpbWVFbnRpdHlJZCA9IHRoaXMucGxheWVyLmdldFJ1bnRpbWVJZCgpO1xuICAgICAgICBway5pdGVtID0gaXRlbTtcbiAgICAgICAgcGsuaW52ZW50b3J5U2xvdCA9IHRoaXMucGxheWVyLmdldEludmVudG9yeSgpLmdldEhhbmRTbG90SW5kZXgoKTtcbiAgICAgICAgcGsuaG90YmFyU2xvdCA9IHRoaXMucGxheWVyLmdldEludmVudG9yeSgpLmdldEhhbmRTbG90SW5kZXgoKTtcbiAgICAgICAgcGsud2luZG93SWQgPSAwOyAvLyBJbnZlbnRvcnkgSURcbiAgICAgICAgYXdhaXQgdGhpcy5jb25uZWN0aW9uLnNlbmREYXRhUGFja2V0KHBrKTtcbiAgICB9XG5cbiAgICAvKipcbiAgICAgKiBTZW5kIHRoZSBjdXJyZW50IHRpY2sgdG8gYSBjbGllbnQuXG4gICAgICogQHBhcmFtIHtudW1iZXJ9IHRpY2sgLSBUaGUgdGlja1xuICAgICAqL1xuICAgIHB1YmxpYyBhc3luYyBzZW5kVGltZSh0aWNrOiBudW1iZXIpOiBQcm9taXNlPHZvaWQ+IHtcbiAgICAgICAgY29uc3QgcGsgPSBuZXcgU2V0VGltZVBhY2tldCgpO1xuICAgICAgICBway50aW1lID0gdGljaztcbiAgICAgICAgYXdhaXQgdGhpcy5jb25uZWN0aW9uLnNlbmREYXRhUGFja2V0KHBrKTtcbiAgICB9XG5cbiAgICAvKipcbiAgICAgKiBTZW5kIGdhbWVtb2RlIHRvIGEgY2xpZW50LlxuICAgICAqIEBwYXJhbSB7R2FtZXR5cGV9IGdhbWVtb2RlIC0gdGhlIG51bWVyaWMgZ2FtZW1vZGUgSUQuXG4gICAgICovXG4gICAgcHVibGljIGFzeW5jIHNlbmRHYW1lbW9kZShnYW1lbW9kZT86IEdhbWV0eXBlKTogUHJvbWlzZTx2b2lkPiB7XG4gICAgICAgIGNvbnN0IHBhY2tldCA9IG5ldyBTZXRQbGF5ZXJHYW1ldHlwZVBhY2tldCgpO1xuICAgICAgICBwYWNrZXQuZ2FtZXR5cGUgPSBnYW1lbW9kZSA/PyB0aGlzLnBsYXllci5nYW1lbW9kZTtcbiAgICAgICAgYXdhaXQgdGhpcy5jb25uZWN0aW9uLnNlbmREYXRhUGFja2V0KHBhY2tldCk7XG4gICAgfVxuXG4gICAgcHVibGljIGFzeW5jIHNlbmROZXR3b3JrQ2h1bmtQdWJsaXNoZXIoZGlzdGFuY2U6IG51bWJlciwgc2F2ZWRDaHVua3M6IENodW5rQ29vcmRbXSk6IFByb21pc2U8dm9pZD4ge1xuICAgICAgICBjb25zdCBwayA9IG5ldyBOZXR3b3JrQ2h1bmtQdWJsaXNoZXJVcGRhdGVQYWNrZXQoKTtcbiAgICAgICAgcGsucG9zaXRpb24gPSBCbG9ja1Bvc2l0aW9uLmZyb21WZWN0b3IzKHRoaXMucGxheWVyLmdldFBvc2l0aW9uKCkpO1xuICAgICAgICBway5yYWRpdXMgPSBkaXN0YW5jZSA8PCA0O1xuICAgICAgICBway5zYXZlZENodW5rcyA9IHNhdmVkQ2h1bmtzO1xuICAgICAgICBhd2FpdCB0aGlzLmNvbm5lY3Rpb24uc2VuZERhdGFQYWNrZXQocGspO1xuICAgIH1cblxuICAgIHB1YmxpYyBhc3luYyBzZW5kQXZhaWxhYmxlQ29tbWFuZHMoKTogUHJvbWlzZTx2b2lkPiB7XG4gICAgICAgIGNvbnN0IHBsYXllckVudW0gPSBuZXcgQ29tbWFuZEVudW0oKTtcbiAgICAgICAgcGxheWVyRW51bS5zb2Z0ID0gdHJ1ZTtcbiAgICAgICAgcGxheWVyRW51bS5uYW1lID0gJ1BsYXllcic7XG4gICAgICAgIHBsYXllckVudW0udmFsdWVzID0gdGhpcy5wbGF5ZXJcbiAgICAgICAgICAgIC5nZXRTZXJ2ZXIoKVxuICAgICAgICAgICAgLmdldFNlc3Npb25NYW5hZ2VyKClcbiAgICAgICAgICAgIC5nZXRBbGxQbGF5ZXJzKClcbiAgICAgICAgICAgIC5tYXAoKHBsYXllcikgPT4gcGxheWVyLmdldE5hbWUoKSk7XG5cbiAgICAgICAgY29uc3QgcGsgPSBuZXcgQXZhaWxhYmxlQ29tbWFuZHNQYWNrZXQoKTtcbiAgICAgICAgcGsuc29mdEVudW1zID0gW3BsYXllckVudW1dO1xuICAgICAgICB0aGlzLnNlcnZlclxuICAgICAgICAgICAgLmdldENvbW1hbmRNYW5hZ2VyKClcbiAgICAgICAgICAgIC5nZXRDb21tYW5kc0xpc3QoKVxuICAgICAgICAgICAgLmZvckVhY2goKGNvbW1hbmQpID0+IHtcbiAgICAgICAgICAgICAgICBjb25zdCBjb21tYW5kQ2xhc3MgPSBBcnJheS5mcm9tKHRoaXMuc2VydmVyLmdldENvbW1hbmRNYW5hZ2VyKCkuZ2V0Q29tbWFuZHMoKS52YWx1ZXMoKSkuZmluZChcbiAgICAgICAgICAgICAgICAgICAgKGNtZCkgPT4gY21kLm5hbWUgPT09IGNvbW1hbmRbMF1cbiAgICAgICAgICAgICAgICApO1xuXG4gICAgICAgICAgICAgICAgaWYgKCFjb21tYW5kQ2xhc3MpIHtcbiAgICAgICAgICAgICAgICAgICAgdGhpcy5wbGF5ZXJcbiAgICAgICAgICAgICAgICAgICAgICAgIC5nZXRTZXJ2ZXIoKVxuICAgICAgICAgICAgICAgICAgICAgICAgLmdldExvZ2dlcigpXG4gICAgICAgICAgICAgICAgICAgICAgICAud2FybihgQ2FuJ3QgZmluZCBjb3JyZXNwb25kaW5nIGNvbW1hbmQgY2xhc3MgZm9yIFwiJHtjb21tYW5kWzBdfVwiYCk7XG4gICAgICAgICAgICAgICAgICAgIHJldHVybjtcbiAgICAgICAgICAgICAgICB9XG5cbiAgICAgICAgICAgICAgICBpZiAoIXRoaXMucGxheWVyLmdldFNlcnZlcigpLmdldFBlcm1pc3Npb25NYW5hZ2VyKCkuY2FuKHRoaXMucGxheWVyKS5leGVjdXRlKGNvbW1hbmRDbGFzcy5wZXJtaXNzaW9uKSlcbiAgICAgICAgICAgICAgICAgICAgcmV0dXJuO1xuXG4gICAgICAgICAgICAgICAgY29uc3QgY21kID0gbmV3IENvbW1hbmREYXRhKCk7XG4gICAgICAgICAgICAgICAgY21kLmNvbW1hbmROYW1lID0gY29tbWFuZFswXTtcbiAgICAgICAgICAgICAgICBjbWQuY29tbWFuZERlc2NyaXB0aW9uID0gY29tbWFuZENsYXNzLmRlc2NyaXB0aW9uO1xuICAgICAgICAgICAgICAgIGlmIChjb21tYW5kQ2xhc3MuYWxpYXNlcyEubGVuZ3RoID4gMCkge1xuICAgICAgICAgICAgICAgICAgICBjb25zdCBjbWRBbGlhc2VzID0gbmV3IENvbW1hbmRFbnVtKCk7XG4gICAgICAgICAgICAgICAgICAgIGNtZEFsaWFzZXMubmFtZSA9IGAke2NvbW1hbmRbMF19QWxpYXNlc2A7XG4gICAgICAgICAgICAgICAgICAgIGNtZEFsaWFzZXMudmFsdWVzID0gY29tbWFuZENsYXNzLmFsaWFzZXMhLmNvbmNhdChjb21tYW5kWzBdKTtcbiAgICAgICAgICAgICAgICAgICAgY21kLmFsaWFzZXMgPSBjbWRBbGlhc2VzO1xuICAgICAgICAgICAgICAgIH1cblxuICAgICAgICAgICAgICAgIGNvbW1hbmRbMl0uZm9yRWFjaCgoYXJnLCBpbmRleCkgPT4ge1xuICAgICAgICAgICAgICAgICAgICBjb25zdCBwYXJhbWV0ZXJzID0gYXJnXG4gICAgICAgICAgICAgICAgICAgICAgICAubWFwKChwYXJhbWV0ZXI6IENvbW1hbmRBcmd1bWVudCB8IG51bGwpID0+IHtcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICBpZiAoIXBhcmFtZXRlciB8fCAhKHBhcmFtZXRlciBhcyBhbnkpPy5nZXRQYXJhbWV0ZXJzKSByZXR1cm4gW107XG5cbiAgICAgICAgICAgICAgICAgICAgICAgICAgICBjb25zdCBwYXJhbWV0ZXJzID0gcGFyYW1ldGVyLmdldFBhcmFtZXRlcnModGhpcy5zZXJ2ZXIpO1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgIGlmIChwYXJhbWV0ZXJzKSByZXR1cm4gQXJyYXkuZnJvbShwYXJhbWV0ZXJzLnZhbHVlcygpKTtcblxuICAgICAgICAgICAgICAgICAgICAgICAgICAgIGlmIChwYXJhbWV0ZXIgaW5zdGFuY2VvZiBDb21tYW5kQXJndW1lbnRFbnRpdHkpXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHJldHVybiBbXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBuZXcgQ29tbWFuZFBhcmFtZXRlcih7XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcGFyYW1OYW1lOiAndGFyZ2V0JyxcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBwYXJhbVR5cGU6IENvbW1hbmRQYXJhbWV0ZXJUeXBlLlRhcmdldFxuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfSlcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgXTtcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICBpZiAocGFyYW1ldGVyIGluc3RhbmNlb2YgQ29tbWFuZEFyZ3VtZW50R2FtZW1vZGUpXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHJldHVybiBbXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBuZXcgQ29tbWFuZFBhcmFtZXRlcih7XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcGFyYW1OYW1lOiAnZ2FtZW1vZGUnLFxuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHBhcmFtVHlwZTogQ29tbWFuZFBhcmFtZXRlclR5cGUuU3RyaW5nXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9KVxuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBdO1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgIGlmIChwYXJhbWV0ZXIuY29uc3RydWN0b3IubmFtZSA9PT0gJ1N0cmluZ0FyZ3VtZW50VHlwZScpXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHJldHVybiBbXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBuZXcgQ29tbWFuZFBhcmFtZXRlcih7XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcGFyYW1OYW1lOiAndmFsdWUnLFxuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHBhcmFtVHlwZTogQ29tbWFuZFBhcmFtZXRlclR5cGUuU3RyaW5nXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9KVxuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBdO1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgIGlmIChwYXJhbWV0ZXIuY29uc3RydWN0b3IubmFtZSA9PT0gJ0ludGVnZXJBcmd1bWVudFR5cGUnKVxuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICByZXR1cm4gW1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbmV3IENvbW1hbmRQYXJhbWV0ZXIoe1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHBhcmFtTmFtZTogJ251bWJlcicsXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcGFyYW1UeXBlOiBDb21tYW5kUGFyYW1ldGVyVHlwZS5JbnRcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0pXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIF07XG5cbiAgICAgICAgICAgICAgICAgICAgICAgICAgICB0aGlzLnNlcnZlci5nZXRMb2dnZXIoKS53YXJuKGBJbnZhbGlkIHBhcmFtZXRlciAke3BhcmFtZXRlci5jb25zdHJ1Y3Rvci5uYW1lfWApO1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgIHJldHVybiBbXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG5ldyBDb21tYW5kUGFyYW1ldGVyKHtcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHBhcmFtTmFtZTogJ3ZhbHVlJyxcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHBhcmFtVHlwZTogQ29tbWFuZFBhcmFtZXRlclR5cGUuU3RyaW5nXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0pXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgXTtcbiAgICAgICAgICAgICAgICAgICAgICAgIH0pXG4gICAgICAgICAgICAgICAgICAgICAgICAuZmxhdCgpO1xuICAgICAgICAgICAgICAgICAgICBjbWQub3ZlcmxvYWRzW2luZGV4XSA9IHBhcmFtZXRlcnM7XG4gICAgICAgICAgICAgICAgfSk7XG4gICAgICAgICAgICAgICAgcGsuY29tbWFuZERhdGEucHVzaChjbWQpO1xuICAgICAgICAgICAgfSk7XG5cbiAgICAgICAgYXdhaXQgdGhpcy5nZXRDb25uZWN0aW9uKCkuc2VuZERhdGFQYWNrZXQocGspO1xuICAgIH1cblxuICAgIC8qKlxuICAgICAqIFNldCB0aGUgY2xpZW50J3MgbWF4aW11bSB2aWV3IGRpc3RhbmNlLlxuICAgICAqXG4gICAgICogQHBhcmFtIGRpc3RhbmNlIC0gVGhlIHZpZXcgZGlzdGFuY2VcbiAgICAgKi9cbiAgICBwdWJsaWMgYXN5bmMgc2V0Vmlld0Rpc3RhbmNlKGRpc3RhbmNlOiBudW1iZXIpOiBQcm9taXNlPHZvaWQ+IHtcbiAgICAgICAgY29uc3QgcGFja2V0ID0gbmV3IENodW5rUmFkaXVzVXBkYXRlZFBhY2tldCgpO1xuICAgICAgICBwYWNrZXQucmFkaXVzID0gdGhpcy5wbGF5ZXIudmlld0Rpc3RhbmNlID0gZGlzdGFuY2U7XG4gICAgICAgIGF3YWl0IHRoaXMuY29ubmVjdGlvbi5zZW5kRGF0YVBhY2tldChwYWNrZXQpO1xuICAgIH1cblxuICAgIHB1YmxpYyBhc3luYyBzZW5kQXR0cmlidXRlcyhhdHRyaWJ1dGVzPzogQXR0cmlidXRlcyk6IFByb21pc2U8dm9pZD4ge1xuICAgICAgICBjb25zdCB2YWx1ZSA9IGF0dHJpYnV0ZXM/LmdldEF0dHJpYnV0ZXMoKSA/PyB0aGlzLnBsYXllci5hdHRyaWJ1dGVzLmdldEF0dHJpYnV0ZXMoKTtcblxuICAgICAgICBjb25zdCBwYWNrZXQgPSBuZXcgVXBkYXRlQXR0cmlidXRlc1BhY2tldCgpO1xuICAgICAgICBwYWNrZXQucnVudGltZUVudGl0eUlkID0gdGhpcy5wbGF5ZXIuZ2V0UnVudGltZUlkKCk7XG4gICAgICAgIHBhY2tldC5hdHRyaWJ1dGVzID0gdmFsdWUubGVuZ3RoID4gMCA/IHZhbHVlIDogdGhpcy5wbGF5ZXIuYXR0cmlidXRlcy5nZXREZWZhdWx0cygpO1xuICAgICAgICBwYWNrZXQudGljayA9IEJpZ0ludCh0aGlzLnBsYXllci5nZXRTZXJ2ZXIoKS5nZXRUaWNrKCkpO1xuICAgICAgICBhd2FpdCB0aGlzLmNvbm5lY3Rpb24uc2VuZERhdGFQYWNrZXQocGFja2V0KTtcbiAgICB9XG5cbiAgICBwdWJsaWMgYXN5bmMgc2VuZE1ldGFkYXRhKG1ldGFkYXRhPzogTWV0YWRhdGEpOiBQcm9taXNlPHZvaWQ+IHtcbiAgICAgICAgY29uc3QgcGFja2V0ID0gbmV3IFNldEFjdG9yRGF0YVBhY2tldCgpO1xuICAgICAgICBwYWNrZXQucnVudGltZUVudGl0eUlkID0gdGhpcy5wbGF5ZXIuZ2V0UnVudGltZUlkKCk7XG4gICAgICAgIHBhY2tldC5tZXRhZGF0YSA9IG1ldGFkYXRhID8/IHRoaXMucGxheWVyLm1ldGFkYXRhO1xuICAgICAgICBwYWNrZXQudGljayA9IEJpZ0ludCh0aGlzLnBsYXllci5nZXRTZXJ2ZXIoKS5nZXRUaWNrKCkpO1xuICAgICAgICBhd2FpdCB0aGlzLmNvbm5lY3Rpb24uc2VuZERhdGFQYWNrZXQocGFja2V0KTtcbiAgICB9XG5cbiAgICAvKipcbiAgICAgKiBTZW5kIGEgY2hhdCBtZXNzYWdlIHRvIHRoZSBjbGllbnQuXG4gICAgICogQHBhcmFtIHtvYmplY3R9IG9wdGlvbnMgLSBUaGUgb3B0aW9ucyBmb3IgdGhlIG1lc3NhZ2UuXG4gICAgICogQHBhcmFtIHtzdHJpbmd9IG9wdGlvbnMubWVzc2FnZSAtIFRoZSBtZXNzYWdlIHRvIHNlbmQuXG4gICAgICogQHBhcmFtIHtzdHJpbmd9IFtvcHRpb25zLnNvdXJjZU5hbWU9JyddIC0gVGhlIHNvdXJjZSBvZiB0aGUgbWVzc2FnZS5cbiAgICAgKiBAcGFyYW0ge3N0cmluZ30gW29wdGlvbnMueHVpZD0nJ10gLSBUaGUgWFVJRCBvZiB0aGUgcGxheWVyLlxuICAgICAqIEBwYXJhbSB7c3RyaW5nfSBbb3B0aW9ucy5wbGF0Zm9ybUNoYXRJZD0nJ10gLSBUaGUgcGxhdGZvcm0gY2hhdCBJRC5cbiAgICAgKiBAcGFyYW0ge3N0cmluZ1tdfSBbb3B0aW9ucy5wYXJhbWV0ZXJzPVtdXSAtIFRoZSBwYXJhbWV0ZXJzIGZvciB0aGUgbWVzc2FnZS5cbiAgICAgKiBAcGFyYW0ge2Jvb2xlYW59IFtvcHRpb25zLm5lZWRzVHJhbnNsYXRpb249ZmFsc2VdIC0gV2hldGhlciB0aGUgbWVzc2FnZSBuZWVkcyB0cmFuc2xhdGlvbi5cbiAgICAgKiBAcGFyYW0ge1RleHRUeXBlfSBbb3B0aW9ucy50eXBlPVRleHRUeXBlLlJhd10gLSBUaGUgdHlwZSBvZiB0aGUgbWVzc2FnZS5cbiAgICAgKiBAcmV0dXJucyB7UHJvbWlzZTx2b2lkPn0gQSBwcm9taXNlIHRoYXQgcmVzb2x2ZXMgd2hlbiB0aGUgbWVzc2FnZSBpcyBzZW50LlxuICAgICAqL1xuICAgIHB1YmxpYyBhc3luYyBzZW5kTWVzc2FnZSh7XG4gICAgICAgIG1lc3NhZ2UsXG4gICAgICAgIHNvdXJjZU5hbWUgPSAnJyxcbiAgICAgICAgeHVpZCA9ICcnLFxuICAgICAgICBwbGF0Zm9ybUNoYXRJZCA9ICcnLFxuICAgICAgICBwYXJhbWV0ZXJzID0gW10sXG4gICAgICAgIG5lZWRzVHJhbnNsYXRpb24gPSBmYWxzZSxcbiAgICAgICAgdHlwZSA9IFRleHRUeXBlLlJhd1xuICAgIH06IHtcbiAgICAgICAgbWVzc2FnZTogc3RyaW5nO1xuICAgICAgICBzb3VyY2VOYW1lPzogc3RyaW5nO1xuICAgICAgICB4dWlkPzogc3RyaW5nO1xuICAgICAgICBwbGF0Zm9ybUNoYXRJZD86IHN0cmluZztcbiAgICAgICAgcGFyYW1ldGVycz86IHN0cmluZ1tdO1xuICAgICAgICBuZWVkc1RyYW5zbGF0aW9uPzogYm9vbGVhbjtcbiAgICAgICAgdHlwZT86IFRleHRUeXBlO1xuICAgIH0pOiBQcm9taXNlPHZvaWQ+IHtcbiAgICAgICAgaWYgKCFtZXNzYWdlKSB0aHJvdyBuZXcgRXJyb3IoJ0EgbWVzc2FnZSBpcyByZXF1aXJlZCcpO1xuXG4gICAgICAgIGNvbnN0IHBrID0gbmV3IFRleHRQYWNrZXQoKTtcbiAgICAgICAgcGsubWVzc2FnZSA9IG1lc3NhZ2U7XG4gICAgICAgIHBrLmZpbHRlcmVkID0gbWVzc2FnZTtcbiAgICAgICAgcGsuc291cmNlTmFtZSA9IHNvdXJjZU5hbWU7XG4gICAgICAgIHBrLnh1aWQgPSB4dWlkO1xuICAgICAgICBway5wbGF0Zm9ybUNoYXRJZCA9IHBsYXRmb3JtQ2hhdElkO1xuICAgICAgICBway5wYXJhbWV0ZXJzID0gcGFyYW1ldGVycztcbiAgICAgICAgcGsubmVlZHNUcmFuc2xhdGlvbiA9IG5lZWRzVHJhbnNsYXRpb247XG4gICAgICAgIHBrLnR5cGUgPSB0eXBlO1xuICAgICAgICBhd2FpdCB0aGlzLmNvbm5lY3Rpb24uc2VuZERhdGFQYWNrZXQocGspO1xuICAgIH1cblxuICAgIHB1YmxpYyBhc3luYyBzZW5kQ2h1bmsoY2h1bms6IENodW5rKTogUHJvbWlzZTx2b2lkPiB7XG4gICAgICAgIGNvbnN0IGhhc2ggPSBDaHVuay5wYWNrWFooY2h1bmsuZ2V0WCgpLCBjaHVuay5nZXRaKCkpO1xuXG4gICAgICAgIGNvbnN0IHBhY2tldCA9IG5ldyBMZXZlbENodW5rUGFja2V0KCk7XG4gICAgICAgIHBhY2tldC5jaHVua1ggPSBjaHVuay5nZXRYKCk7XG4gICAgICAgIHBhY2tldC5jaHVua1ogPSBjaHVuay5nZXRaKCk7XG4gICAgICAgIHBhY2tldC5jbGllbnRTdWJDaHVua1JlcXVlc3RzRW5hYmxlZCA9IGZhbHNlO1xuICAgICAgICBwYWNrZXQuc3ViQ2h1bmtDb3VudCA9IGNodW5rLmdldFRvcEVtcHR5KCkgKyA0OyAvLyBhZGQgdGhlIHVzZWxlc3MgbGF5ZXJzIGhhY2tcbiAgICAgICAgcGFja2V0LmRhdGEgPSBjaHVuay5uZXR3b3JrU2VyaWFsaXplKCk7XG4gICAgICAgIGF3YWl0IHRoaXMuc2VuZChwYWNrZXQpO1xuXG4gICAgICAgIHRoaXMubG9hZGluZ0NodW5rcy5kZWxldGUoaGFzaCk7XG4gICAgICAgIHRoaXMubG9hZGVkQ2h1bmtzLmFkZChoYXNoKTtcbiAgICB9XG5cbiAgICAvKipcbiAgICAgKiBCcm9hZGNhc3QgdGhlIG1vdmVtZW50IHRvIGEgZGVmaW5lZCBwbGF5ZXJcbiAgICAgKi9cbiAgICBwdWJsaWMgYXN5bmMgYnJvYWRjYXN0TW92ZShwbGF5ZXI6IFBsYXllciwgbW9kZSA9IE1vdmVtZW50VHlwZS5Ob3JtYWwpOiBQcm9taXNlPHZvaWQ+IHtcbiAgICAgICAgY29uc3QgcGFja2V0ID0gbmV3IE1vdmVQbGF5ZXJQYWNrZXQoKTtcbiAgICAgICAgcGFja2V0LnJ1bnRpbWVFbnRpdHlJZCA9IHBsYXllci5nZXRSdW50aW1lSWQoKTtcblxuICAgICAgICBwYWNrZXQucG9zaXRpb24gPSBwbGF5ZXIuZ2V0UG9zaXRpb24oKTtcbiAgICAgICAgcGFja2V0LnBpdGNoID0gcGxheWVyLnBpdGNoO1xuICAgICAgICBwYWNrZXQueWF3ID0gcGxheWVyLnlhdztcbiAgICAgICAgcGFja2V0LmhlYWRZYXcgPSBwbGF5ZXIuaGVhZFlhdztcblxuICAgICAgICBwYWNrZXQubW9kZSA9IG1vZGU7XG5cbiAgICAgICAgcGFja2V0Lm9uR3JvdW5kID0gcGxheWVyLmlzT25Hcm91bmQoKTtcblxuICAgICAgICAvLyBUT0RPXG4gICAgICAgIGlmIChtb2RlID09PSBNb3ZlbWVudFR5cGUuVGVsZXBvcnQpIHtcbiAgICAgICAgICAgIHBhY2tldC50ZWxlcG9ydENhdXNlID0gMDtcbiAgICAgICAgICAgIHBhY2tldC50ZWxlcG9ydEl0ZW1JZCA9IDA7XG4gICAgICAgIH1cblxuICAgICAgICBwYWNrZXQucmlkaW5nRW50aXR5UnVudGltZUlkID0gQmlnSW50KDApO1xuICAgICAgICBwYWNrZXQudGljayA9IEJpZ0ludCh0aGlzLnBsYXllci5nZXRTZXJ2ZXIoKS5nZXRUaWNrKCkpO1xuICAgICAgICBhd2FpdCB0aGlzLnNlbmQocGFja2V0KTtcbiAgICB9XG5cbiAgICAvKipcbiAgICAgKiBBZGRzIHRoZSBjbGllbnQgdG8gdGhlIHBsYXllciBsaXN0IG9mIGV2ZXJ5IHBsYXllciBpbnNpZGVcbi