@jsprismarine/prismarine
Version:
Dedicated Minecraft Bedrock Edition server written in TypeScript
570 lines (568 loc) • 83.3 kB
JavaScript
import { creativeitems } from '@jsprismarine/bedrock-data';
import { Gametype } from '@jsprismarine/minecraft';
import Heap from 'heap';
import { CommandArgumentEntity, CommandArgumentGamemode } from '../command/CommandArguments.es.js';
import { WindowIds } from '../inventory/WindowIds.es.js';
import { Item } from '../item/Item.es.js';
import UUID from '../utils/UUID.es.js';
import BlockPosition from '../world/BlockPosition.es.js';
import CoordinateUtils from '../world/CoordinateUtils.es.js';
import Chunk from '../world/chunk/Chunk.es.js';
import './packet/ActorFallPacket.es.js';
import './packet/AddActorPacket.es.js';
import './packet/AddItemActorPacket.es.js';
import AddPlayerPacket from './packet/AddPlayerPacket.es.js';
import './packet/AnimatePacket.es.js';
import './packet/AvailableActorIdentifiersPacket.es.js';
import AvailableCommandsPacket from './packet/AvailableCommandsPacket.es.js';
import BatchPacket from './packet/BatchPacket.es.js';
import './packet/BiomeDefinitionListPacket.es.js';
import './packet/ChangeDimensionPacket.es.js';
import ChunkRadiusUpdatedPacket from './packet/ChunkRadiusUpdatedPacket.es.js';
import './packet/CommandRequestPacket.es.js';
import './packet/ContainerClosePacket.es.js';
import './packet/ContainerOpenPacket.es.js';
import CreativeContentPacket from './packet/CreativeContentPacket.es.js';
import '@jsprismarine/jsbinaryutils';
import DisconnectPacket from './packet/DisconnectPacket.es.js';
import './packet/EmoteListPacket.es.js';
import './packet/InteractPacket.es.js';
import InventoryContentPacket from './packet/InventoryContentPacket.es.js';
import './packet/InventoryTransactionPacket.es.js';
import './packet/ItemComponentPacket.es.js';
import './packet/ItemStackRequestPacket.es.js';
import './packet/ItemStackResponsePacket.es.js';
import LevelChunkPacket from './packet/LevelChunkPacket.es.js';
import './packet/LevelSoundEventPacket.es.js';
import './packet/LoginPacket.es.js';
import MobEquipmentPacket from './packet/MobEquipmentPacket.es.js';
import './packet/MoveActorAbsolutePacket.es.js';
import MovePlayerPacket from './packet/MovePlayerPacket.es.js';
import NetworkChunkPublisherUpdatePacket from './packet/NetworkChunkPublisherUpdatePacket.es.js';
import './packet/OnScreenTextureAnimationPacket.es.js';
import './packet/PacketViolationWarningPacket.es.js';
import './packet/PlaySoundPacket.es.js';
import PlayStatusPacket from './packet/PlayStatusPacket.es.js';
import './packet/PlayerActionPacket.es.js';
import PlayerListPacket, { PlayerListEntry, PlayerListAction } from './packet/PlayerListPacket.es.js';
import './packet/PlayerSkinPacket.es.js';
import RemoveActorPacket from './packet/RemoveActorPacket.es.js';
import './packet/RequestChunkRadiusPacket.es.js';
import './packet/RequestNetworkSettingsPacket.es.js';
import './packet/ResourcePackResponsePacket.es.js';
import './packet/ResourcePackStackPacket.es.js';
import './packet/ResourcePacksInfoPacket.es.js';
import './packet/ServerSettingsRequestPacket.es.js';
import SetActorDataPacket from './packet/SetActorDataPacket.es.js';
import './packet/SetDefaultGametypePacket.es.js';
import './packet/SetHealthPacket.es.js';
import './packet/SetLocalPlayerAsInitializedPacket.es.js';
import SetPlayerGametypePacket from './packet/SetPlayerGametypePacket.es.js';
import SetTimePacket from './packet/SetTimePacket.es.js';
import './packet/ShowProfilePacket.es.js';
import './packet/StartGamePacket.es.js';
import TextPacket from './packet/TextPacket.es.js';
import './packet/TickSyncPacket.es.js';
import './packet/TransferPacket.es.js';
import UpdateAdventureSettingsPacket from './packet/UpdateAdventureSettingsPacket.es.js';
import UpdateAttributesPacket from './packet/UpdateAttributesPacket.es.js';
import './packet/UpdateBlockPacket.es.js';
import './packet/WorldEventPacket.es.js';
import UpdateAbilitiesPacket, { AbilityLayerType, AbilityLayerFlag, AbilityLayer } from './packet/UpdateAbilitiesPacket.es.js';
import CommandData from './type/CommandData.es.js';
import { CommandEnum } from './type/CommandEnum.es.js';
import CommandParameter, { CommandParameterType } from './type/CommandParameter.es.js';
import MovementType from './type/MovementType.es.js';
import PermissionType from './type/PermissionType.es.js';
import PlayerPermissionType from './type/PlayerPermissionType.es.js';
import TextType from './type/TextType.es.js';
class PlayerSession {
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 = /* @__PURE__ */ 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 = 0.05;
mainLayer.walkSpeed = 0.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(
(cmd2) => cmd2.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 parameters2 = parameter.getParameters(this.server);
if (parameters2) return Array.from(parameters2.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;
}
}
export { PlayerSession as default };
//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiUGxheWVyU2Vzc2lvbi5lcy5qcyIsInNvdXJjZXMiOlsiLi4vLi4vc3JjL25ldHdvcmsvUGxheWVyU2Vzc2lvbi50cyJdLCJzb3VyY2VzQ29udGVudCI6WyJpbXBvcnQgeyBjcmVhdGl2ZWl0ZW1zIGFzIENyZWF0aXZlSXRlbXMgfSBmcm9tICdAanNwcmlzbWFyaW5lL2JlZHJvY2stZGF0YSc7XG5pbXBvcnQgeyBHYW1ldHlwZSB9IGZyb20gJ0Bqc3ByaXNtYXJpbmUvbWluZWNyYWZ0JztcbmltcG9ydCBIZWFwIGZyb20gJ2hlYXAnO1xuaW1wb3J0IHR5cGUgUGxheWVyIGZyb20gJy4uL1BsYXllcic7XG5pbXBvcnQgdHlwZSBTZXJ2ZXIgZnJvbSAnLi4vU2VydmVyJztcbmltcG9ydCB0eXBlIHsgQ29tbWFuZEFyZ3VtZW50IH0gZnJvbSAnLi4vY29tbWFuZC9Db21tYW5kQXJndW1lbnRzJztcbmltcG9ydCB7IENvbW1hbmRBcmd1bWVudEVudGl0eSwgQ29tbWFuZEFyZ3VtZW50R2FtZW1vZGUgfSBmcm9tICcuLi9jb21tYW5kL0NvbW1hbmRBcmd1bWVudHMnO1xuaW1wb3J0IHR5cGUgeyBBdHRyaWJ1dGVzIH0gZnJvbSAnLi4vZW50aXR5L0F0dHJpYnV0ZSc7XG5pbXBvcnQgdHlwZSB7IE1ldGFkYXRhIH0gZnJvbSAnLi4vZW50aXR5L01ldGFkYXRhJztcbmltcG9ydCB7IFdpbmRvd0lkcyB9IGZyb20gJy4uL2ludmVudG9yeS9XaW5kb3dJZHMnO1xuaW1wb3J0IHsgSXRlbSB9IGZyb20gJy4uL2l0ZW0vSXRlbSc7XG5pbXBvcnQgVVVJRCBmcm9tICcuLi91dGlscy9VVUlEJztcbmltcG9ydCBCbG9ja1Bvc2l0aW9uIGZyb20gJy4uL3dvcmxkL0Jsb2NrUG9zaXRpb24nO1xuaW1wb3J0IENvb3JkaW5hdGVVdGlscyBmcm9tICcuLi93b3JsZC9Db29yZGluYXRlVXRpbHMnO1xuaW1wb3J0IENodW5rIGZyb20gJy4uL3dvcmxkL2NodW5rL0NodW5rJztcbmltcG9ydCB0eXBlIENsaWVudENvbm5lY3Rpb24gZnJvbSAnLi9DbGllbnRDb25uZWN0aW9uJztcbmltcG9ydCB0eXBlIHsgRGF0YVBhY2tldCB9IGZyb20gJy4vUGFja2V0cyc7XG5pbXBvcnQgeyBCYXRjaFBhY2tldCB9IGZyb20gJy4vUGFja2V0cyc7XG5pbXBvcnQgQWRkUGxheWVyUGFja2V0IGZyb20gJy4vcGFja2V0L0FkZFBsYXllclBhY2tldCc7XG5pbXBvcnQgQXZhaWxhYmxlQ29tbWFuZHNQYWNrZXQgZnJvbSAnLi9wYWNrZXQvQXZhaWxhYmxlQ29tbWFuZHNQYWNrZXQnO1xuaW1wb3J0IENodW5rUmFkaXVzVXBkYXRlZFBhY2tldCBmcm9tICcuL3BhY2tldC9DaHVua1JhZGl1c1VwZGF0ZWRQYWNrZXQnO1xuaW1wb3J0IENyZWF0aXZlQ29udGVudFBhY2tldCBmcm9tICcuL3BhY2tldC9DcmVhdGl2ZUNvbnRlbnRQYWNrZXQnO1xuaW1wb3J0IERpc2Nvbm5lY3RQYWNrZXQgZnJvbSAnLi9wYWNrZXQvRGlzY29ubmVjdFBhY2tldCc7XG5pbXBvcnQgSW52ZW50b3J5Q29udGVudFBhY2tldCBmcm9tICcuL3BhY2tldC9JbnZlbnRvcnlDb250ZW50UGFja2V0JztcbmltcG9ydCBMZXZlbENodW5rUGFja2V0IGZyb20gJy4vcGFja2V0L0xldmVsQ2h1bmtQYWNrZXQnO1xuaW1wb3J0IE1vYkVxdWlwbWVudFBhY2tldCBmcm9tICcuL3BhY2tldC9Nb2JFcXVpcG1lbnRQYWNrZXQnO1xuaW1wb3J0IE1vdmVQbGF5ZXJQYWNrZXQgZnJvbSAnLi9wYWNrZXQvTW92ZVBsYXllclBhY2tldCc7XG5pbXBvcnQgdHlwZSB7IENodW5rQ29vcmQgfSBmcm9tICcuL3BhY2tldC9OZXR3b3JrQ2h1bmtQdWJsaXNoZXJVcGRhdGVQYWNrZXQnO1xuaW1wb3J0IE5ldHdvcmtDaHVua1B1Ymxpc2hlclVwZGF0ZVBhY2tldCBmcm9tICcuL3BhY2tldC9OZXR3b3JrQ2h1bmtQdWJsaXNoZXJVcGRhdGVQYWNrZXQnO1xuaW1wb3J0IFBsYXlTdGF0dXNQYWNrZXQgZnJvbSAnLi9wYWNrZXQvUGxheVN0YXR1c1BhY2tldCc7XG5pbXBvcnQgUGxheWVyTGlzdFBhY2tldCwgeyBQbGF5ZXJMaXN0QWN0aW9uLCBQbGF5ZXJMaXN0RW50cnkgfSBmcm9tICcuL3BhY2tldC9QbGF5ZXJMaXN0UGFja2V0JztcbmltcG9ydCBSZW1vdmVBY3RvclBhY2tldCBmcm9tICcuL3BhY2tldC9SZW1vdmVBY3RvclBhY2tldCc7XG5pbXBvcnQgU2V0QWN0b3JEYXRhUGFja2V0IGZyb20gJy4vcGFja2V0L1NldEFjdG9yRGF0YVBhY2tldCc7XG5pbXBvcnQgU2V0UGxheWVyR2FtZXR5cGVQYWNrZXQgZnJvbSAnLi9wYWNrZXQvU2V0UGxheWVyR2FtZXR5cGVQYWNrZXQnO1xuaW1wb3J0IFNldFRpbWVQYWNrZXQgZnJvbSAnLi9wYWNrZXQvU2V0VGltZVBhY2tldCc7XG5pbXBvcnQgVGV4dFBhY2tldCBmcm9tICcuL3BhY2tldC9UZXh0UGFja2V0JztcbmltcG9ydCBVcGRhdGVBYmlsaXRpZXNQYWNrZXQsIHtcbiAgICBBYmlsaXR5TGF5ZXIsXG4gICAgQWJpbGl0eUxheWVyRmxhZyxcbiAgICBBYmlsaXR5TGF5ZXJUeXBlXG59IGZyb20gJy4vcGFja2V0L1VwZGF0ZUFiaWxpdGllc1BhY2tldCc7XG5pbXBvcnQgVXBkYXRlQWR2ZW50dXJlU2V0dGluZ3NQYWNrZXQgZnJvbSAnLi9wYWNrZXQvVXBkYXRlQWR2ZW50dXJlU2V0dGluZ3NQYWNrZXQnO1xuaW1wb3J0IFVwZGF0ZUF0dHJpYnV0ZXNQYWNrZXQgZnJvbSAnLi9wYWNrZXQvVXBkYXRlQXR0cmlidXRlc1BhY2tldCc7XG5pbXBvcnQgQ29tbWFuZERhdGEgZnJvbSAnLi90eXBlL0NvbW1hbmREYXRhJztcbmltcG9ydCB7IENvbW1hbmRFbnVtIH0gZnJvbSAnLi90eXBlL0NvbW1hbmRFbnVtJztcbmltcG9ydCBDb21tYW5kUGFyYW1ldGVyLCB7IENvbW1hbmRQYXJhbWV0ZXJUeXBlIH0gZnJvbSAnLi90eXBlL0NvbW1hbmRQYXJhbWV0ZXInO1xuaW1wb3J0IE1vdmVtZW50VHlwZSBmcm9tICcuL3R5cGUvTW92ZW1lbnRUeXBlJztcbmltcG9ydCBQZXJtaXNzaW9uVHlwZSBmcm9tICcuL3R5cGUvUGVybWlzc2lvblR5cGUnO1xuaW1wb3J0IFBsYXllclBlcm1pc3Npb25UeXBlIGZyb20gJy4vdHlwZS9QbGF5ZXJQZXJtaXNzaW9uVHlwZSc7XG5pbXBvcnQgVGV4dFR5cGUgZnJvbSAnLi90eXBlL1RleHRUeXBlJztcblxuZXhwb3J0IGRlZmF1bHQgY2xhc3MgUGxheWVyU2Vzc2lvbiB7XG4gICAgcHJpdmF0ZSBjb25uZWN0aW9uOiBDbGllbnRDb25uZWN0aW9uO1xuICAgIHByaXZhdGUgcmVhZG9ubHkgc2VydmVyOiBTZXJ2ZXI7XG4gICAgcHJpdmF0ZSBwbGF5ZXI6IFBsYXllcjtcblxuICAgIHByaXZhdGUgcmVhZG9ubHkgY2h1bmtTZW5kUXVldWU6IENodW5rW10gPSBbXTtcbiAgICBwcml2YXRlIHJlYWRvbmx5IGxvYWRlZENodW5rczogU2V0PGJpZ2ludD4gPSBuZXcgU2V0KCk7XG4gICAgcHJpdmF0ZSByZWFkb25seSBsb2FkaW5nQ2h1bmtzOiBTZXQ8YmlnaW50PiA9IG5ldyBTZXQoKTtcblxuICAgIHB1YmxpYyBjb25zdHJ1Y3RvcihzZXJ2ZXI6IFNlcnZlciwgY29ubmVjdGlvbjogQ2xpZW50Q29ubmVjdGlvbiwgcGxheWVyOiBQbGF5ZXIpIHtcbiAgICAgICAgdGhpcy5zZXJ2ZXIgPSBzZXJ2ZXI7XG4gICAgICAgIHRoaXMuY29ubmVjdGlvbiA9IGNvbm5lY3Rpb247XG4gICAgICAgIHRoaXMucGxheWVyID0gcGxheWVyO1xuICAgIH1cblxuICAgIHB1YmxpYyBhc3luYyB1cGRhdGUoX3RpY2s6IG51bWJlcik6IFByb21pc2U8dm9pZD4ge1xuICAgICAgICBpZiAodGhpcy5jaHVua1NlbmRRdWV1ZS5sZW5ndGggPiAwKSB7XG4gICAgICAgICAgICBjb25zdCBjaHVua3NUb1NlbmQgPSB0aGlzLmNodW5rU2VuZFF1ZXVlLnNwbGljZSgwLCBNYXRoLm1pbih0aGlzLmNodW5rU2VuZFF1ZXVlLmxlbmd0aCwgNTApKTtcbiAgICAgICAgICAgIGNvbnN0IGJhdGNoID0gbmV3IEJhdGNoUGFja2V0KCk7XG4gICAgICAgICAgICBmb3IgKGNvbnN0IGNodW5rIG9mIGNodW5rc1RvU2VuZCkge1xuICAgICAgICAgICAgICAgIGNvbnN0IHBrID0gbmV3IExldmVsQ2h1bmtQYWNrZXQoKTtcbiAgICAgICAgICAgICAgICBway5jaHVua1ggPSBjaHVuay5nZXRYKCk7XG4gICAgICAgICAgICAgICAgcGsuY2h1bmtaID0gY2h1bmsuZ2V0WigpO1xuICAgICAgICAgICAgICAgIHBrLmNsaWVudFN1YkNodW5rUmVxdWVzdHNFbmFibGVkID0gZmFsc2U7XG4gICAgICAgICAgICAgICAgcGsuc3ViQ2h1bmtDb3VudCA9IGNodW5rLmdldFRvcEVtcHR5KCkgKyA0O1xuICAgICAgICAgICAgICAgIHBrLmRhdGEgPSBjaHVuay5uZXR3b3JrU2VyaWFsaXplKCk7XG4gICAgICAgICAgICAgICAgYmF0Y2guYWRkUGFja2V0KHBrKTtcblxuICAgICAgICAgICAgICAgIGNvbnN0IGhhc2ggPSBDaHVuay5wYWNrWFooY2h1bmsuZ2V0WCgpLCBjaHVuay5nZXRaKCkpO1xuICAgICAgICAgICAgICAgIHRoaXMubG9hZGVkQ2h1bmtzLmFkZChoYXNoKTtcbiAgICAgICAgICAgICAgICB0aGlzLmxvYWRpbmdDaHVua3MuZGVsZXRlKGhhc2gpO1xuICAgICAgICAgICAgfVxuICAgICAgICAgICAgdGhpcy5jb25uZWN0aW9uLnNlbmRCYXRjaChiYXRjaCwgZmFsc2UpO1xuICAgICAgICB9XG5cbiAgICAgICAgdGhpcy5wbGF5ZXIudmlld0Rpc3RhbmNlICYmIChhd2FpdCB0aGlzLm5lZWROZXdDaHVua3MoKSk7XG4gICAgfVxuXG4gICAgcHVibGljIGFzeW5jIHNlbmQocGFja2V0OiBEYXRhUGFja2V0KSB7XG4gICAgICAgIHRoaXMuY29ubmVjdGlvbi5zZW5kRGF0YVBhY2tldChwYWNrZXQpO1xuICAgIH1cblxuICAgIC8qKlxuICAgICAqIE5vdGlmeSBhIGNsaWVudCBhYm91dCBjaGFuZ2UocykgdG8gdGhlIGFkdmVudHVyZSBzZXR0aW5ncy5cbiAgICAgKlxuICAgICAqIEBwYXJhbSBwbGF5ZXIgLSBUaGUgY2xpZW50LWNvbnRyb2xsZWQgZW50aXR5XG4gICAgICovXG4gICAgcHVibGljIGFzeW5jIHNlbmRTZXR0aW5ncyhwbGF5ZXI/OiBQbGF5ZXIpOiBQcm9taXNlPHZvaWQ+IHtcbiAgICAgICAgY29uc3QgdGFyZ2V0ID0gcGxheWVyID8/IHRoaXMucGxheWVyO1xuXG4gICAgICAgIGNvbnN0IHBhY2tldCA9IG5ldyBVcGRhdGVBZHZlbnR1cmVTZXR0aW5nc1BhY2tldCgpO1xuICAgICAgICBwYWNrZXQud29ybGRJbW11dGFibGUgPSB0YXJnZXQuZ2FtZW1vZGUgPT09IEdhbWV0eXBlLlNQRUNUQVRPUjtcbiAgICAgICAgcGFja2V0Lm5vQXR0YWNraW5nUGxheWVycyA9IHRhcmdldC5nYW1lbW9kZSA9PT0gR2FtZXR5cGUuU1BFQ1RBVE9SO1xuICAgICAgICBwYWNrZXQubm9BdHRhY2tpbmdNb2JzID0gdGFyZ2V0LmdhbWVtb2RlID09PSBHYW1ldHlwZS5TUEVDVEFUT1I7XG4gICAgICAgIHBhY2tldC5hdXRvSnVtcCA9IHRydWU7XG4gICAgICAgIHBhY2tldC5zaG93TmFtZVRhZ3MgPSB0cnVlO1xuICAgICAgICBhd2FpdCB0aGlzLmNvbm5lY3Rpb24uc2VuZERhdGFQYWNrZXQocGFja2V0KTtcbiAgICB9XG5cbiAgICBwdWJsaWMgYXN5bmMgc2VuZEFiaWxpdGllcyhwbGF5ZXI/OiBQbGF5ZXIpOiBQcm9taXNlPHZvaWQ+IHtcbiAgICAgICAgY29uc3QgdGFyZ2V0ID0gcGxheWVyID8/IHRoaXMucGxheWVyO1xuXG4gICAgICAgIGNvbnN0IG1haW5MYXllciA9IG5ldyBBYmlsaXR5TGF5ZXIoKTtcbiAgICAgICAgbWFpbkxheWVyLmxheWVyVHlwZSA9IEFiaWxpdHlMYXllclR5cGUuQkFTRTtcbiAgICAgICAgbWFpbkxheWVyLmxheWVyRmxhZ3MgPSBuZXcgTWFwKFtcbiAgICAgICAgICAgIFtBYmlsaXR5TGF5ZXJGbGFnLkZMWV9TUEVFRCwgdHJ1ZV0sXG4gICAgICAgICAgICBbQWJpbGl0eUxheWVyRmxhZy5XQUxLX1NQRUVELCB0cnVlXSxcbiAgICAgICAgICAgIFtBYmlsaXR5TGF5ZXJGbGFnLk1BWV9GTFksIHRhcmdldC5tZXRhZGF0YS5jYW5GbHldLFxuICAgICAgICAgICAgW0FiaWxpdHlMYXllckZsYWcuRkxZSU5HLCB0YXJnZXQuaXNGbHlpbmcoKV0sXG4gICAgICAgICAgICBbQWJpbGl0eUxheWVyRmxhZy5OT19DTElQLCB0YXJnZXQuZ2FtZW1vZGUgPT09IEdhbWV0eXBlLlNQRUNUQVRPUl0sXG4gICAgICAgICAgICBbQWJpbGl0eUxheWVyRmxhZy5PUEVSQVRPUl9DT01NQU5EUywgdGFyZ2V0LmlzT3AoKV0sXG4gICAgICAgICAgICBbQWJpbGl0eUxheWVyRmxhZy5URUxFUE9SVCwgdGFyZ2V0LmlzT3AoKV0sXG4gICAgICAgICAgICBbQWJpbGl0eUxheWVyRmxhZy5JTlZVTE5FUkFCTEUsIHRhcmdldC5nYW1lbW9kZSA9PT0gR2FtZXR5cGUuU1BFQ1RBVE9SXSxcbiAgICAgICAgICAgIFtBYmlsaXR5TGF5ZXJGbGFnLk1VVEVELCBmYWxzZV0sXG4gICAgICAgICAgICBbQWJpbGl0eUxheWVyRmxhZy5XT1JMRF9CVUlMREVSLCBmYWxzZV0sXG4gICAgICAgICAgICBbQWJpbGl0eUxheWVyRmxhZy5JTlNUQUJVSUxELCB0YXJnZXQuZ2FtZW1vZGUgPT09IEdhbWV0eXBlLlNQRUNUQVRPUl0sXG4gICAgICAgICAgICBbQWJpbGl0eUxheWVyRmxhZy5MSUdIVE5JTkcsIGZhbHNlXSxcbiAgICAgICAgICAgIFtBYmlsaXR5TGF5ZXJGbGFnLkJVSUxELCB0YXJnZXQuZ2FtZW1vZGUgIT09IEdhbWV0eXBlLlNQRUNUQVRPUl0sXG4gICAgICAgICAgICBbQWJpbGl0eUxheWVyRmxhZy5NSU5FLCB0YXJnZXQuZ2FtZW1vZGUgIT09IEdhbWV0eXBlLlNQRUNUQVRPUl0sXG4gICAgICAgICAgICBbQWJpbGl0eUxheWVyRmxhZy5ET09SU19BTkRfU1dJVENIRVMsIHRhcmdldC5nYW1lbW9kZSAhPT0gR2FtZXR5cGUuU1BFQ1RBVE9SXSxcbiAgICAgICAgICAgIFtBYmlsaXR5TGF5ZXJGbGFnLk9QRU5fQ09OVEFJTkVSUywgdGFyZ2V0LmdhbWVtb2RlICE9PSBHYW1ldHlwZS5TUEVDVEFUT1JdLFxuICAgICAgICAgICAgW0FiaWxpdHlMYXllckZsYWcuQVRUQUNLX1BMQVlFUlMsIHRhcmdldC5nYW1lbW9kZSAhPT0gR2FtZXR5cGUuU1BFQ1RBVE9SXSxcbiAgICAgICAgICAgIFtBYmlsaXR5TGF5ZXJGbGFnLkFUVEFDS19NT0JTLCB0YXJnZXQuZ2FtZW1vZGUgIT09IEdhbWV0eXBlLlNQRUNUQVRPUl1cbiAgICAgICAgXSk7XG4gICAgICAgIG1haW5MYXllci5mbHlTcGVlZCA9IDAuMDU7XG4gICAgICAgIG1haW5MYXllci53YWxrU3BlZWQgPSAwLjE7XG5cbiAgICAgICAgY29uc3QgcGFja2V0ID0gbmV3IFVwZGF0ZUFiaWxpdGllc1BhY2tldCgpO1xuICAgICAgICBwYWNrZXQuY29tbWFuZFBlcm1pc3Npb24gPSB0YXJnZXQuaXNPcCgpID8gUGVybWlzc2lvblR5cGUuT3BlcmF0b3IgOiBQZXJtaXNzaW9uVHlwZS5Ob3JtYWw7XG4gICAgICAgIHBhY2tldC5wbGF5ZXJQZXJtaXNzaW9uID0gdGFyZ2V0LmlzT3AoKSA/IFBsYXllclBlcm1pc3Npb25UeXBlLk9wZXJhdG9yIDogUGxheWVyUGVybWlzc2lvblR5cGUuTWVtYmVyO1xuICAgICAgICBwYWNrZXQudGFyZ2V0QWN0b3JVbmlxdWVJZCA9IHRhcmdldC5nZXRSdW50aW1lSWQoKTtcbiAgICAgICAgcGFja2V0LmFiaWxpdHlMYXllcnMgPSBbbWFpbkxheWVyXTtcbiAgICAgICAgYXdhaXQgdGhpcy5zZW5kKHBhY2tldCk7XG4gICAgfVxuXG4gICAgcHVibGljIGFzeW5jIG5lZWROZXdDaHVua3MoZm9yY2VSZXNlbmQgPSBmYWxzZSwgZGlzdD86IG51bWJlcik6IFByb21pc2U8dm9pZD4ge1xuICAgICAgICBjb25zdCBjdXJyZW50WENodW5rID0gQ29vcmRpbmF0ZVV0aWxzLmZyb21CbG9ja1RvQ2h1bmsodGhpcy5wbGF5ZXIuZ2V0WCgpKTtcbiAgICAgICAgY29uc3QgY3VycmVudFpDaHVuayA9IENvb3JkaW5hdGVVdGlscy5mcm9tQmxvY2tUb0NodW5rKHRoaXMucGxheWVyLmdldFooKSk7XG5cbiAgICAgICAgY29uc3Qgdmlld0Rpc3RhbmNlID0gdGhpcy5wbGF5ZXIudmlld0Rpc3RhbmNlIHx8IGRpc3QgfHwgMDtcblxuICAgICAgICBjb25zdCBjaHVua3NUb1NlbmRIZWFwID0gbmV3IEhlYXAoKGE6IG51bWJlcltdLCBiOiBudW1iZXJbXSkgPT4ge1xuICAgICAgICAgICAgY29uc3QgZGlzdFhGaXJzdCA9IE1hdGguYWJzKGFbMF0hIC0gY3VycmVudFhDaHVuayk7XG4gICAgICAgICAgICBjb25zdCBkaXN0WFNlY29uZCA9IE1hdGguYWJzKGJbMF0hIC0gY3VycmVudFhDaHVuayk7XG5cbiAgICAgICAgICAgIGNvbnN0IGRpc3RaRmlyc3QgPSBNYXRoLmFicyhhWzFdISAtIGN1cnJlbnRaQ2h1bmspO1xuICAgICAgICAgICAgY29uc3QgZGlzdFpTZWNvbmQgPSBNYXRoLmFicyhiWzFdISAtIGN1cnJlbnRaQ2h1bmspO1xuXG4gICAgICAgICAgICByZXR1cm4gZGlzdFhGaXJzdCArIGRpc3RaRmlyc3QgPiBkaXN0WFNlY29uZCArIGRpc3RaU2Vjb25kID8gMSA6IC0xO1xuICAgICAgICB9KTtcblxuICAgICAgICBmb3IgKGxldCBzZW5kWENodW5rID0gLXZpZXdEaXN0YW5jZTsgc2VuZFhDaHVuayA8PSB2aWV3RGlzdGFuY2U7IHNlbmRYQ2h1bmsrKykge1xuICAgICAgICAgICAgZm9yIChsZXQgc2VuZFpDaHVuayA9IC12aWV3RGlzdGFuY2U7IHNlbmRaQ2h1bmsgPD0gdmlld0Rpc3RhbmNlOyBzZW5kWkNodW5rKyspIHtcbiAgICAgICAgICAgICAgICBpZiAoc2VuZFhDaHVuayAqIHNlbmRYQ2h1bmsgKyBzZW5kWkNodW5rICogc2VuZFpDaHVuayA+IHZpZXdEaXN0YW5jZSAqIHZpZXdEaXN0YW5jZSkgY29udGludWU7IC8vIGVhcmx5IGV4aXQgaWYgY2h1bmsgaXMgb3V0c2lkZSBvZiB2aWV3IGRpc3RhbmNlXG4gICAgICAgICAgICAgICAgY29uc3QgbmV3Q2h1bmsgPSBbY3VycmVudFhDaHVuayArIHNlbmRYQ2h1bmssIGN1cnJlbnRaQ2h1bmsgKyBzZW5kWkNodW5rXTtcbiAgICAgICAgICAgICAgICBjb25zdCBoYXNoID0gQ2h1bmsucGFja1haKG5ld0NodW5rWzBdISwgbmV3Q2h1bmtbMV0hKTtcblxuICAgICAgICAgICAgICAgIGlmIChmb3JjZVJlc2VuZCkge1xuICAgICAgICAgICAgICAgICAgICBjaHVua3NUb1NlbmRIZWFwLnB1c2gobmV3Q2h1bmspO1xuICAgICAgICAgICAgICAgIH0gZWxzZSBpZiAoIXRoaXMubG9hZGVkQ2h1bmtzLmhhcyhoYXNoKSAmJiAhdGhpcy5sb2FkaW5nQ2h1bmtzLmhhcyhoYXNoKSkge1xuICAgICAgICAgICAgICAgICAgICBjaHVua3NUb1NlbmRIZWFwLnB1c2gobmV3Q2h1bmspO1xuICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgIH1cbiAgICAgICAgfVxuXG4gICAgICAgIHdoaWxlIChjaHVua3NUb1NlbmRIZWFwLnNpemUoKSA+IDApIHtcbiAgICAgICAgICAgIGNvbnN0IGNsb3Nlc3RDaHVuayA9IGNodW5rc1RvU2VuZEhlYXAucG9wKCkhO1xuICAgICAgICAgICAgY29uc3QgaGFzaCA9IENodW5rLnBhY2tYWihjbG9zZXN0Q2h1bmtbMF0hLCBjbG9zZXN0Q2h1bmtbMV0hKTtcbiAgICAgICAgICAgIGlmIChmb3JjZVJlc2VuZCkge1xuICAgICAgICAgICAgICAgIGlmICghdGhpcy5sb2FkZWRDaHVua3MuaGFzKGhhc2gpICYmICF0aGlzLmxvYWRpbmdDaHVua3MuaGFzKGhhc2gpKSB7XG4gICAgICAgICAgICAgICAgICAgIHRoaXMubG9hZGluZ0NodW5rcy5hZGQoaGFzaCk7XG4gICAgICAgICAgICAgICAgICAgIGF3YWl0IHRoaXMucmVxdWVzdENodW5rKGNsb3Nlc3RDaHVua1swXSEsIGNsb3Nlc3RDaHVua1sxXSEpO1xuICAgICAgICAgICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgICAgICAgICAgIGNvbnN0IGxvYWRlZENodW5rID0gYXdhaXQgdGhpcy5wbGF5ZXIuZ2V0V29ybGQoKS5nZXRDaHVuayhjbG9zZXN0Q2h1bmtbMF0hLCBjbG9zZXN0Q2h1bmtbMV0hKTtcbiAgICAgICAgICAgICAgICAgICAgYXdhaXQgdGhpcy5zZW5kQ2h1bmsobG9hZGVkQ2h1bmspO1xuICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgICAgICAgdGhpcy5sb2FkaW5nQ2h1bmtzLmFkZChoYXNoKTtcbiAgICAgICAgICAgICAgICBhd2FpdCB0aGlzLnJlcXVlc3RDaHVuayhjbG9zZXN0Q2h1bmtbMF0hLCBjbG9zZXN0Q2h1bmtbMV0hKTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgfVxuXG4gICAgICAgIGxldCB1bmxvYWRlZCA9IGZhbHNlO1xuXG4gICAgICAgIGZvciAoY29uc3QgaGFzaCBvZiB0aGlzLmxvYWRlZENodW5rcykge1xuICAgICAgICAgICAgY29uc3QgW3gsIHpdID0gQ2h1bmsudW5wYWNrWFooaGFzaCk7XG5cbiAgICAgICAgICAgIGlmIChNYXRoLmFicyh4ISAtIGN1cnJlbnRYQ2h1bmspID4gdmlld0Rpc3RhbmNlIHx8IE1hdGguYWJzKHohIC0gY3VycmVudFpDaHVuaykgPiB2aWV3RGlzdGFuY2UpIHtcbiAgICAgICAgICAgICAgICB1bmxvYWRlZCA9IHRydWU7XG4gICAgICAgICAgICAgICAgdGhpcy5sb2FkZWRDaHVua3MuZGVsZXRlKGhhc2gpO1xuICAgICAgICAgICAgfVxuICAgICAgICB9XG5cbiAgICAgICAgZm9yIChjb25zdCBoYXNoIG9mIHRoaXMubG9hZGluZ0NodW5rcykge1xuICAgICAgICAgICAgY29uc3QgW3gsIHpdID0gQ2h1bmsudW5wYWNrWFooaGFzaCk7XG5cbiAgICAgICAgICAgIGlmIChNYXRoLmFicyh4ISAtIGN1cnJlbnRYQ2h1bmspID4gdmlld0Rpc3RhbmNlIHx8IE1hdGguYWJzKHohIC0gY3VycmVudFpDaHVuaykgPiB2aWV3RGlzdGFuY2UpIHtcbiAgICAgICAgICAgICAgICB0aGlzLmxvYWRpbmdDaHVua3MuZGVsZXRlKGhhc2gpO1xuICAgICAgICAgICAgfVxuICAgICAgICB9XG5cbiAgICAgICAgaWYgKCF1bmxvYWRlZCAmJiB0aGlzLmNodW5rU2VuZFF1ZXVlLmxlbmd0aCAhPT0gMCkge1xuICAgICAgICAgICAgYXdhaXQgdGhpcy5zZW5kTmV0d29ya0NodW5rUHVibGlzaGVyKGRpc3QgPz8gdmlld0Rpc3RhbmNlLCBbXSk7XG4gICAgICAgIH1cbiAgICB9XG5cbiAgICBwdWJsaWMgYXN5bmMgcmVxdWVzdENodW5rKHg6IG51bWJlciwgejogbnVtYmVyKTogUHJvbWlzZTx2b2lkPiB7XG4gICAgICAgIGNvbnN0IGNodW5rID0gYXdhaXQgdGhpcy5wbGF5ZXIuZ2V0V29ybGQoKS5nZXRDaHVuayh4LCB6KTtcbiAgICAgICAgdGhpcy5jaHVua1NlbmRRdWV1ZS5wdXNoKGNodW5rKTtcbiAgICB9XG5cbiAgICAvKipcbiAgICAgKiBDbGVhciB0aGUgY3VycmVudGx5IGxvYWRlZCBhbmQgbG9hZGluZyBjaHVua3MuXG4gICAgICpcbiAgICAgKiBAcmVtYXJrc1xuICAgICAqIFVzdWFsbHkgdXNlZCBmb3IgY2hhbmdpbmcgZGltZW5zaW9uLCB3b3JsZCwgZXRjLlxuICAgICAqL1xuICAgIHB1YmxpYyBhc3luYyBjbGVhckNodW5rcygpIHtcbiAgICAgICAgdGhpcy5sb2FkZWRDaHVua3MuY2xlYXIoKTtcbiAgICAgICAgdGhpcy5sb2FkaW5nQ2h1bmtzLmNsZWFyKCk7XG4gICAgfVxuXG4gICAgLyoqXG4gICAgICogQFRPRE86IEltcGxlbWVudCB0aGlzLlxuICAgICAqL1xuICAgIHB1YmxpYyBhc3luYyBzZW5kSW52ZW50b3J5KCk6IFByb21pc2U8dm9pZD4ge1xuICAgICAgICBjb25zdCBwayA9IG5ldyBJbnZlbnRvcnlDb250ZW50UGFja2V0KCk7XG4gICAgICAgIHBrLml0ZW1zID0gdGhpcy5wbGF5ZXIuZ2V0SW52ZW50b3J5KCkuZ2V0SXRlbXModHJ1ZSk7XG4gICAgICAgIHBrLndpbmRvd0lkID0gV2luZG93SWRzLklOVkVOVE9SWTsgLy8gSW52ZW50b3J5IHdpbmRvd1xuICAgICAgICAvL2F3YWl0IHRoaXMuY29ubmVjdGlvbi5zZW5kRGF0YVBhY2tldChwayk7XG4gICAgICAgIC8vYXdhaXQgdGhpcy5zZW5kSGFuZEl0ZW0odGhpcy5wbGF5ZXIuZ2V0SW52ZW50b3J5KCkuZ2V0SXRlbUluSGFuZCgpKTtcbiAgICB9XG5cbiAgICBwdWJsaWMgYXN5bmMgc2VuZENyZWF0aXZlQ29udGVudHMoZW1wdHkgPSBmYWxzZSk6IFByb21pc2U8dm9pZD4ge1xuICAgICAgICBjb25zdCBwayA9IG5ldyBDcmVhdGl2ZUNvbnRlbnRQYWNrZXQoKTtcbiAgICAgICAgaWYgKGVtcHR5KSB7XG4gICAgICAgICAgICBhd2FpdCB0aGlzLmNvbm5lY3Rpb24uc2VuZERhdGFQYWNrZXQocGspO1xuICAgICAgICAgICAgcmV0dXJuO1xuICAgICAgICB9XG5cbiAgICAgICAgY29uc3QgZW50cmllcyA9IFtcbiAgICAgICAgICAgIC4uLnRoaXMucGxheWVyLmdldFNlcnZlcigpLmdldEJsb2NrTWFuYWdlcigpLmdldEJsb2NrcygpLFxuICAgICAgICAgICAgLi4udGhpcy5wbGF5ZXIuZ2V0U2VydmVyKCkuZ2V0SXRlbU1hbmFnZXIoKS5nZXRJdGVtcygpXG4gICAgICAgIF07XG5cbiAgICAgICAgLy8gU29ydCBiYXNlZCBvbiBQbW1QIEJlZHJvY2stZGF0YVxuICAgICAgICBDcmVhdGl2ZUl0ZW1zLmZvckVhY2goKGl0ZW06IGFueSkgPT4ge1xuICAgICAgICAgICAgcGsuaXRlbXMucHVzaChcbiAgICAgICAgICAgICAgICAuLi5lbnRyaWVzXG4gICAgICAgICAgICAgICAgICAgIC5maWx0ZXIoKGVudHJ5OiBhbnkpID0+IHtcbiAgICAgICAgICAgICAgICAgICAgICAgIHJldHVybiBlbnRyeS5tZXRhID09PSAoaXRlbS5kYW1hZ2UgPz8gMCkgJiYgZW50cnkuZ2V0SWQoKSA9PT0gaXRlbS5pZDtcbiAgICAgICAgICAgICAgICAgICAgfSlcbiAgICAgICAgICAgICAgICAgICAgLm1hcChcbiAgICAgICAgICAgICAgICAgICAgICAgIChlbnRyeSkgPT5cbiAgICAgICAgICAgICAgICAgICAgICAgICAgICBuZXcgSXRlbSh7XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGlkOiBlbnRyeS5nZXRJZCgpLFxuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBuYW1lOiBlbnRyeS5nZXROYW1lKCksXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG1ldGE6IGVudHJ5Lm1ldGFcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICB9KVxuICAgICAgICAgICAgICAgICAgICApXG4gICAgICAgICAgICApO1xuICAgICAgICB9KTtcblxuICAgICAgICBhd2FpdCB0aGlzLmNvbm5lY3Rpb24uc2VuZERhdGFQYWNrZXQocGspO1xuICAgIH1cblxuICAgIC8qKlxuICAgICAqIFNldHMgdGhlIGl0ZW0gaW4gdGhlIHBsYXllciBoYW5kLlxuICAgICAqIEBwYXJhbSB7SXRlbX0gaXRlbSAtIFRoZSBlbnRpdHkuXG4gICAgICovXG4gICAgcHVibGljIGFzeW5jIHNlbmRIYW5kSXRlbShpdGVtOiBJdGVtKTogUHJvbWlzZTx2b2lkPiB7XG4gICAgICAgIGNvbnN0IHBrID0gbmV3IE1vYkVxdWlwbWVudFBhY2tldCgpO1xuICAgICAgICBway5ydW50aW1lRW50aXR5SWQgPSB0aGlzLnBsYXllci5nZXRSdW50aW1lSWQoKTtcbiAgICAgICAgcGsuaXRlbSA9IGl0ZW07XG4gICAgICAgIHBrLmludmVudG9yeVNsb3QgPSB0aGlzLnBsYXllci5nZXRJbnZlbnRvcnkoKS5nZXRIYW5kU2xvdEluZGV4KCk7XG4gICAgICAgIHBrLmhvdGJhclNsb3QgPSB0aGlzLnBsYXllci5nZXRJbnZlbnRvcnkoKS5nZXRIYW5kU2xvdEluZGV4KCk7XG4gICAgICAgIHBrLndpbmRvd0lkID0gMDsgLy8gSW52ZW50b3J5IElEXG4gICAgICAgIGF3YWl0IHRoaXMuY29ubmVjdGlvbi5zZW5kRGF0YVBhY2tldChwayk7XG4gICAgfVxuXG4gICAgLyoqXG4gICAgICogU2VuZCB0aGUgY3VycmVudCB0aWNrIHRvIGEgY2xpZW50LlxuICAgICAqIEBwYXJhbSB7bnVtYmVyfSB0aWNrIC0gVGhlIHRpY2tcbiAgICAgKi9cbiAgICBwdWJsaWMgYXN5bmMgc2VuZFRpbWUodGljazogbnVtYmVyKTogUHJvbWlzZTx2b2lkPiB7XG4gICAgICAgIGNvbnN0IHBrID0gbmV3IFNldFRpbWVQYWNrZXQoKTtcbiAgICAgICAgcGsudGltZSA9IHRpY2s7XG4gICAgICAgIGF3YWl0IHRoaXMuY29ubmVjdGlvbi5zZW5kRGF0YVBhY2tldChwayk7XG4gICAgfVxuXG4gICAgLyoqXG4gICAgICogU2VuZCBnYW1lbW9kZSB0byBhIGNsaWVudC5cbiAgICAgKiBAcGFyYW0ge0dhbWV0eXBlfSBnYW1lbW9kZSAtIHRoZSBudW1lcmljIGdhbWVtb2RlIElELlxuICAgICAqL1xuICAgIHB1YmxpYyBhc3luYyBzZW5kR2FtZW1vZGUoZ2FtZW1vZGU/OiBHYW1ldHlwZSk6IFByb21pc2U8dm9pZD4ge1xuICAgICAgICBjb25zdCBwYWNrZXQgPSBuZXcgU2V0UGxheWVyR2FtZXR5cGVQYWNrZXQoKTtcbiAgICAgICAgcGFja2V0LmdhbWV0eXBlID0gZ2FtZW1vZGUgPz8gdGhpcy5wbGF5ZXIuZ2FtZW1vZGU7XG4gICAgICAgIGF3YWl0IHRoaXMuY29ubmVjdGlvbi5zZW5kRGF0YVBhY2tldChwYWNrZXQpO1xuICAgIH1cblxuICAgIHB1YmxpYyBhc3luYyBzZW5kTmV0d29ya0NodW5rUHVibGlzaGVyKGRpc3RhbmNlOiBudW1iZXIsIHNhdmVkQ2h1bmtzOiBDaHVua0Nvb3JkW10pOiBQcm9taXNlPHZvaWQ+IHtcbiAgICAgICAgY29uc3QgcGsgPSBuZXcgTmV0d29ya0NodW5rUHVibGlzaGVyVXBkYXRlUGFja2V0KCk7XG4gICAgICAgIHBrLnBvc2l0aW9uID0gQmxvY2tQb3NpdGlvbi5mcm9tVmVjdG9yMyh0aGlzLnBsYXllci5nZXRQb3NpdGlvbigpKTtcbiAgICAgICAgcGsucmFkaXVzID0gZGlzdGFuY2UgPDwgNDtcbiAgICAgICAgcGsuc2F2ZWRDaHVua3MgPSBzYXZlZENodW5rcztcbiAgICAgICAgYXdhaXQgdGhpcy5jb25uZWN0aW9uLnNlbmREYXRhUGFja2V0KHBrKTtcbiAgICB9XG5cbiAgICBwdWJsaWMgYXN5bmMgc2VuZEF2YWlsYWJsZUNvbW1hbmRzKCk6IFByb21pc2U8dm9pZD4ge1xuICAgICAgICBjb25zdCBwbGF5ZXJFbnVtID0gbmV3IENvbW1hbmRFbnVtKCk7XG4gICAgICAgIHBsYXllckVudW0uc29mdCA9IHRydWU7XG4gICAgICAgIHBsYXllckVudW0ubmFtZSA9ICdQbGF5ZXInO1xuICAgICAgICBwbGF5ZXJFbnVtLnZhbHVlcyA9IHRoaXMucGxheWVyXG4gICAgICAgICAgICAuZ2V0U2VydmVyKClcbiAgICAgICAgICAgIC5nZXRTZXNzaW9uTWFuYWdlcigpXG4gICAgICAgICAgICAuZ2V0QWxsUGxheWVycygpXG4gICAgICAgICAgICAubWFwKChwbGF5ZXIpID0+IHBsYXllci5nZXROYW1lKCkpO1xuXG4gICAgICAgIGNvbnN0IHBrID0gbmV3IEF2YWlsYWJsZUNvbW1hbmRzUGFja2V0KCk7XG4gICAgICAgIHBrLnNvZnRFbnVtcyA9IFtwbGF5ZXJFbnVtXTtcbiAgICAgICAgdGhpcy5zZXJ2ZXJcbiAgICAgICAgICAgIC5nZXRDb21tYW5kTWFuYWdlcigpXG4gICAgICAgICAgICAuZ2V0Q29tbWFuZHNMaXN0KClcbiAgICAgICAgICAgIC5mb3JFYWNoKChjb21tYW5kKSA9PiB7XG4gICAgICAgICAgICAgICAgY29uc3QgY29tbWFuZENsYXNzID0gQXJyYXkuZnJvbSh0aGlzLnNlcnZlci5nZXRDb21tYW5kTWFuYWdlcigpLmdldENvbW1hbmRzKCkudmFsdWVzKCkpLmZpbmQoXG4gICAgICAgICAgICAgICAgICAgIChjbWQpID0+IGNtZC5uYW1lID09PSBjb21tYW5kWzBdXG4gICAgICAgICAgICAgICAgKTtcblxuICAgICAgICAgICAgICAgIGlmICghY29tbWFuZENsYXNzKSB7XG4gICAgICAgICAgICAgICAgICAgIHRoaXMucGxheWVyXG4gICAgICAgICAgICAgICAgICAgICAgICAuZ2V0U2VydmVyKClcbiAgICAgICAgICAgICAgICAgICAgICAgIC5nZXRMb2dnZXIoKVxuICAgICAgICAgICAgICAgICAgICAgICAgLndhcm4oYENhbid0IGZpbmQgY29ycmVzcG9uZGluZyBjb21tYW5kIGNsYXNzIGZvciBcIiR7Y29tbWFuZFswXX1cImApO1xuICAgICAgICAgICAgICAgICAgICByZXR1cm47XG4gICAgICAgICAgICAgICAgfVxuXG4gICAgICAgICAgICAgICAgaWYgKCF0aGlzLnBsYXllci5nZXRTZXJ2ZXIoKS5nZXRQZXJtaXNzaW9uTWFuYWdlcigpLmNhbih0aGlzLnBsYXllcikuZXhlY3V0ZShjb21tYW5kQ2xhc3MucGVybWlzc2lvbikpXG4gICAgICAgICAgICAgICAgICAgIHJldHVybjtcblxuICAgICAgICAgICAgICAgIGNvbnN0IGNtZCA9IG5ldyBDb21tYW5kRGF0YSgpO1xuICAgICAgICAgICAgICAgIGNtZC5jb21tYW5kTmFtZSA9IGNvbW1hbmRbMF07XG4gICAgICAgICAgICAgICAgY21kLmNvbW1hbmREZXNjcmlwdGlvbiA9IGNvbW1hbmRDbGFzcy5kZXNjcmlwdGlvbjtcbiAgICAgICAgICAgICAgICBpZiAoY29tbWFuZENsYXNzLmFsaWFzZXMhLmxlbmd0aCA+IDApIHtcbiAgICAgICAgICAgICAgICAgICAgY29uc3QgY21kQWxpYXNlcyA9IG5ldyBDb21tYW5kRW51bSgpO1xuICAgICAgICAgICAgICAgICAgICBjbWRBbGlhc2VzLm5hbWUgPSBgJHtjb21tYW5kWzBdfUFsaWFzZXNgO1xuICAgICAgICAgICAgICAgICAgICBjbWRBbGlhc2VzLnZhbHVlcyA9IGNvbW1hbmRDbGFzcy5hbGlhc2VzIS5jb25jYXQoY29tbWFuZFswXSk7XG4gICAgICAgICAgICAgICAgICAgIGNtZC5hbGlhc2VzID0gY21kQWxpYXNlcztcbiAgICAgICAgICAgICAgICB9XG5cbiAgICAgICAgICAgICAgICBjb21tYW5kWzJdLmZvckVhY2goKGFyZywgaW5kZXgpID0+IHtcbiAgICAgICAgICAgICAgICAgICAgY29uc3QgcGFyYW1ldGVycyA9IGFyZ1xuICAgICAgICAgICAgICAgICAgICAgICAgLm1hcCgocGFyYW1ldGVyOiBDb21tYW5kQXJndW1lbnQgfCBudWxsKSA9PiB7XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgaWYgKCFwYXJhbWV0ZXIgfHwgIShwYXJhbWV0ZXIgYXMgYW55KT8uZ2V0UGFyYW1ldGVycykgcmV0dXJuIFtdO1xuXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgY29uc3QgcGFyYW1ldGVycyA9IHBhcmFtZXRlci5nZXRQYXJhbWV0ZXJzKHRoaXMuc2VydmVyKTtcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICBpZiAocGFyYW1ldGVycykgcmV0dXJuIEFycmF5LmZyb20ocGFyYW1ldGVycy52YWx1ZXMoKSk7XG5cbiAgICAgICAgICAgICAgICAgICAgICAgICAgICBpZiAocGFyYW1ldGVyIGluc3RhbmNlb2YgQ29tbWFuZEFyZ3VtZW50RW50aXR5KVxuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICByZXR1cm4gW1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbmV3IENvbW1hbmRQYXJhbWV0ZXIoe1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHBhcmFtTmFtZTogJ3RhcmdldCcsXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcGFyYW1UeXBlOiBDb21tYW5kUGFyYW1ldGVyVHlwZS5UYXJnZXRcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0pXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIF07XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgaWYgKHBhcmFtZXRlciBpbnN0YW5jZW9mIENvbW1hbmRBcmd1bWVudEdhbWVtb2RlKVxuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICByZXR1cm4gW1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbmV3IENvbW1hbmRQYXJhbWV0ZXIoe1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHBhcmFtTmFtZTogJ2dhbWVtb2RlJyxcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBwYXJhbVR5cGU6IENvbW1hbmRQYXJhbWV0ZXJUeXBlLlN0cmluZ1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfSlcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgXTtcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICBpZiAocGFyYW1ldGVyLmNvbnN0cnVjdG9yLm5hbWUgPT09ICdTdHJpbmdBcmd1bWVudFR5cGUnKVxuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICByZXR1cm4gW1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbmV3IENvbW1hbmRQYXJhbWV0ZXIoe1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHBhcmFtTmFtZTogJ3ZhbHVlJyxcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBwYXJhbVR5cGU6IENvbW1hbmRQYXJhbWV0ZXJUeXBlLlN0cmluZ1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfSlcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgXTtcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICBpZiAocGFyYW1ldGVyLmNvbnN0cnVjdG9yLm5hbWUgPT09ICdJbnRlZ2VyQXJndW1lbnRUeXBlJylcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcmV0dXJuIFtcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG5ldyBDb21tYW5kUGFyYW1ldGVyKHtcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBwYXJhbU5hbWU6ICdudW1iZXInLFxuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHBhcmFtVHlwZTogQ29tbWFuZFBhcmFtZXRlclR5cGUuSW50XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9KVxuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBdO1xuXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgdGhpcy5zZXJ2ZXIuZ2V0TG9nZ2VyKCkud2FybihgSW52YWxpZCBwYXJhbWV0ZXIgJHtwYXJhbWV0ZXIuY29uc3RydWN0b3IubmFtZX1gKTtcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICByZXR1cm4gW1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBuZXcgQ29tbWFuZFBhcmFtZXRlcih7XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBwYXJhbU5hbWU6ICd2YWx1ZScsXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBwYXJhbVR5cGU6IENvbW1hbmRQYXJhbWV0ZXJUeXBlLlN0cmluZ1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9KVxuICAgICAgICAgICAgICAgICAgICAgICAgICAgIF07XG4gICAgICAgICAgICAgICAgICAgICAgICB9KVxuICAgICAgICAgICAgICAgICAgICAgICAgLmZsYXQoKTtcbiAgICAgICAgICAgICAgICAgICAgY21kLm92ZXJsb2Fkc1tpbmRleF0gPSBwYXJhbWV0ZXJzO1xuICAgICAgICAgICAgICAgIH0pO1xuICAgICAgICAgICAgICAgIHBrLmNvbW1hbmREYXRhLnB1c2goY21kKTtcbiAgICAgICAgICAgIH0pO1xuXG4gICAgICAgIGF3YWl0IHRoaXMuZ2V0Q29ubmVjdGlvbigpLnNlbmREYXRhUGFja2V0KHBrKTtcbiAgICB9XG5cbiAgICAvKipcbiAgICAgKiBTZXQgdGhlIGNsaWVudCdzIG1heGltdW0gdmlldyBkaXN0YW5jZS5cbiAgICAgKlxuICAgICAqIEBwYXJhbSBkaXN0YW5jZSAtIFRoZSB2aWV3IGRpc3RhbmNlXG4gICAgICovXG4gICAgcHVibGljIGFzeW5jIHNldFZpZXdEaXN0YW5jZShkaXN0YW5jZTogbnVtYmVyKTogUHJvbWlzZTx2b2lkPiB7XG4gICAgICAgIGNvbnN0IHBhY2tldCA9IG5ldyBDaHVua1JhZGl1c1VwZGF0ZWRQYWNrZXQoKTtcbiAgICAgICAgcGFja2V0LnJhZGl1cyA9IHRoaXMucGxheWVyLnZpZXdEaXN0YW5jZSA9IGRpc3RhbmNlO1xuICAgICAgICBhd2FpdCB0aGlzLmNvbm5lY3Rpb24uc2VuZERhdGFQYWNrZXQocGFja2V0KTtcbiAgICB9XG5cbiAgICBwdWJsaWMgYXN5bmMgc2VuZEF0dHJpYnV0ZXMoYXR0cmlidXRlcz86IEF0dHJpYnV0ZXMpOiBQcm9taXNlPHZvaWQ+IHtcbiAgICAgICAgY29uc3QgdmFsdWUgPSBhdHRyaWJ1dGVzPy5nZXRBdHRyaWJ1dGVzKCkgPz8gdGhpcy5wbGF5ZXIuYXR0cmlidXRlcy5nZXRBdHRyaWJ1dGVzKCk7XG5cbiAgICAgICAgY29uc3QgcGFja2V0ID0gbmV3IFVwZGF0ZUF0dHJpYnV0ZXNQYWNrZXQoKTtcbiAgICAgICAgcGFja2V0LnJ1bnRpbWVFbnRpdHlJZCA9IHRoaXMucGxheWVyLmdldFJ1bnRpbWVJZCgpO1xuICAgICAgICBwYWNrZXQuYXR0cmlidXRlcyA9IHZhbHVlLmxlbmd0aCA+IDAgPyB2YWx1ZSA6IHRoaXMucGxheWVyLmF0dHJpYnV0ZXMuZ2V0RGVmYXVsdHMoKTtcbiAgICAgICAgcGFja2V0LnRpY2sgPSBCaWdJbnQodGhpcy5wbGF5ZXIuZ2V0U2VydmVyKCkuZ2V0VGljaygpKTtcbiAgICAgICAgYXdhaXQgdGhpcy5jb25uZWN0aW9uLnNlbmREYXRhUGFja2V0KHBhY2tldCk7XG4gICAgfVxuXG4gICAgcHVibGljIGFzeW5jIHNlbmRNZXRhZGF0YShtZXRhZGF0YT86IE1ldGFkYXRhKTogUHJvbWlzZTx2b2lkPiB7XG4gICAgICAgIGNvbnN0IHBhY2tldCA9IG5ldyBTZXRBY3RvckRhdGFQYWNrZXQoKTtcbiAgICAgICAgcGFja2V0LnJ1bnRpbWVFbnRpdHlJZCA9IHRoaXMucGxheWVyLmdldFJ1bnRpbWVJZCgpO1xuICAgICAgICBwYWNrZXQubWV0YWRhdGEgPSBtZXRhZGF0YSA/PyB0aGlzLnBsYXllci5tZXRhZGF0YTtcbiAgICAgICAgcGFja2V0LnRpY2sgPSBCaWdJbnQodGhpcy5wbGF5ZXIuZ2V0U2VydmVyKCkuZ2V0VGljaygpKTtcbiAgICAgICAgYXdhaXQgdGhpcy5jb25uZWN0aW9uLnNlbmREYXRhUGFja2V0KHBhY2tldCk7XG4gICAgfVxuXG4gICAgLyoqXG4gICAgICogU2VuZCBhIGNoYXQgbWVzc2FnZSB0byB0aGUgY2xpZW50LlxuICAgICAqIEBwYXJhbSB7b2JqZWN0fSBvcHRpb25zIC0gVGhlIG9wdGlvbnMgZm9yIHRoZSBtZXNzYWdlLlxuICAgICAqIEBwYXJhbSB7c3RyaW5nfSBvcHRpb25zLm1lc3NhZ2UgLSBUaGUgbWVzc2FnZSB0byBzZW5kLlxuICAgICAqIEBwYXJhbSB7c3RyaW5nfSBbb3B0aW9ucy5zb3VyY2VOYW1lPScnXSAtIFRoZSBzb3VyY2Ugb2YgdGhlIG1lc3NhZ2UuXG4gICAgICogQHBhcmFtIHtzdHJpbmd9IFtvcHRpb25zLnh1aWQ9JyddIC0gVGhlIFhVSUQgb2YgdGhlIHBsYXllci5cbiAgICAgKiBAcGFyYW0ge3N0cmluZ30gW29wdGlvbnMucGxhdGZvcm1DaGF0SWQ9JyddIC0gVGhlIHBsYXRmb3JtIGNoYXQgSUQuXG4gICAgICogQHBhcmFtIHtzdHJpbmdbXX0gW29wdGlvbnMucGFyYW1ldGVycz1bXV0gLSBUaGUgcGFyYW1ldGVycyBmb3IgdGhlIG1lc3NhZ2UuXG4gICAgICogQHBhcmFtIHtib29sZWFufSBbb3B0aW9ucy5uZWVkc1RyYW5zbGF0aW9uPWZhbHNlXSAtIFdoZXRoZXIgdGhlIG1lc3NhZ2UgbmVlZHMgdHJhbnNsYXRpb24uXG4gICAgICogQHBhcmFtIHtUZXh0VHlwZX0gW29wdGlvbnMudHlwZT1UZXh0VHlwZS5SYXddIC0gVGhlIHR5cGUgb2YgdGh