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