UNPKG

@jsprismarine/prismarine

Version:

Dedicated Minecraft Bedrock Edition server written in TypeScript

551 lines (545 loc) 68.7 kB
'use strict'; Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' }); const world_GameruleManager = require('./GameruleManager.cjs.cjs'); const fs = require('node:fs'); const confbox = require('confbox'); const math = require('@jsprismarine/math'); const minecraft = require('@jsprismarine/minecraft'); require('@jsprismarine/errors'); require('@jsprismarine/jsbinaryutils'); require('node:assert'); const utils_cwd = require('../utils/cwd.cjs.cjs'); require('../block/BlockToolType.cjs.cjs'); require('@jsprismarine/bedrock-data'); const utils_Timer = require('../utils/Timer.cjs.cjs'); const item_Item = require('../item/Item.cjs.cjs'); const world_chunk_Chunk = require('./chunk/Chunk.cjs.cjs'); require('node:path'); const block_BlockMappings = require('../block/BlockMappings.cjs.cjs'); require('@jsprismarine/brigadier'); const entity_Entities = require('../entity/Entities.cjs.cjs'); require('../config/Config.cjs.cjs'); require('../network/packet/AddActorPacket.cjs.cjs'); require('../network/packet/MoveActorAbsolutePacket.cjs.cjs'); require('../network/packet/RemoveActorPacket.cjs.cjs'); const utils_UUID = require('../utils/UUID.cjs.cjs'); require('../network/handler/AnimateHandler.cjs.cjs'); require('../network/handler/CommandRequestHandler.cjs.cjs'); require('../network/handler/ContainerCloseHandler.cjs.cjs'); require('../network/handler/EmoteListHandler.cjs.cjs'); require('../network/handler/InteractHandler.cjs.cjs'); require('../network/handler/InventoryTransactionHandler.cjs.cjs'); require('../network/handler/LevelSoundEventHandler.cjs.cjs'); require('../network/handler/LoginHandler.cjs.cjs'); require('../network/handler/MobEquipmentHandler.cjs.cjs'); require('../network/handler/MovePlayerHandler.cjs.cjs'); require('../network/handler/PacketViolationWarningHandler.cjs.cjs'); require('../network/handler/PlayerActionHandler.cjs.cjs'); require('../network/handler/RequestChunkRadiusHandler.cjs.cjs'); require('../network/handler/RequestNetworkSettingsHandler.cjs.cjs'); require('../network/handler/ResourcePackResponseHandler.cjs.cjs'); require('../network/handler/ServerSettingsRequestHandler.cjs.cjs'); require('../network/handler/SetDefaultGametypeHandler.cjs.cjs'); require('../network/handler/SetLocalPlayerAsInitializedHandler.cjs.cjs'); require('../network/handler/SetPlayerGametypeHandler.cjs.cjs'); require('../network/handler/TextHandler.cjs.cjs'); require('../network/handler/TickSyncHandler.cjs.cjs'); require('../network/packet/ActorFallPacket.cjs.cjs'); require('../network/packet/AddItemActorPacket.cjs.cjs'); require('../network/packet/AddPlayerPacket.cjs.cjs'); require('../network/packet/AnimatePacket.cjs.cjs'); require('../network/packet/AvailableActorIdentifiersPacket.cjs.cjs'); require('../network/packet/AvailableCommandsPacket.cjs.cjs'); require('zlib'); require('../network/CompressionProvider.cjs.cjs'); require('../network/packet/NetworkSettingsPacket.cjs.cjs'); require('../network/packet/BiomeDefinitionListPacket.cjs.cjs'); require('../network/packet/ChangeDimensionPacket.cjs.cjs'); require('../network/packet/ChunkRadiusUpdatedPacket.cjs.cjs'); require('../network/packet/CommandRequestPacket.cjs.cjs'); require('../network/packet/ContainerClosePacket.cjs.cjs'); require('../network/packet/ContainerOpenPacket.cjs.cjs'); require('../network/packet/CreativeContentPacket.cjs.cjs'); require('../network/packet/DisconnectPacket.cjs.cjs'); require('../network/packet/EmoteListPacket.cjs.cjs'); require('../network/packet/InteractPacket.cjs.cjs'); require('../network/packet/InventoryContentPacket.cjs.cjs'); require('../network/packet/InventoryTransactionPacket.cjs.cjs'); require('../network/packet/ItemComponentPacket.cjs.cjs'); require('../network/packet/ItemStackRequestPacket.cjs.cjs'); require('../network/packet/ItemStackResponsePacket.cjs.cjs'); require('../network/packet/LevelChunkPacket.cjs.cjs'); const network_packet_LevelSoundEventPacket = require('../network/packet/LevelSoundEventPacket.cjs.cjs'); require('../network/packet/LoginPacket.cjs.cjs'); require('../network/packet/MobEquipmentPacket.cjs.cjs'); require('../network/packet/MovePlayerPacket.cjs.cjs'); require('../network/packet/NetworkChunkPublisherUpdatePacket.cjs.cjs'); require('../network/packet/OnScreenTextureAnimationPacket.cjs.cjs'); require('../network/packet/PacketViolationWarningPacket.cjs.cjs'); require('../network/packet/PlaySoundPacket.cjs.cjs'); require('../network/packet/PlayStatusPacket.cjs.cjs'); require('../network/packet/PlayerActionPacket.cjs.cjs'); require('../network/packet/PlayerListPacket.cjs.cjs'); require('../network/packet/PlayerSkinPacket.cjs.cjs'); require('../network/packet/RequestChunkRadiusPacket.cjs.cjs'); require('../network/packet/RequestNetworkSettingsPacket.cjs.cjs'); require('../network/packet/ResourcePackResponsePacket.cjs.cjs'); require('../network/packet/ResourcePackStackPacket.cjs.cjs'); require('../network/packet/ResourcePacksInfoPacket.cjs.cjs'); require('../network/packet/ServerSettingsRequestPacket.cjs.cjs'); require('../network/packet/SetActorDataPacket.cjs.cjs'); require('../network/packet/SetDefaultGametypePacket.cjs.cjs'); require('../network/packet/SetHealthPacket.cjs.cjs'); require('../network/packet/SetLocalPlayerAsInitializedPacket.cjs.cjs'); require('../network/packet/SetPlayerGametypePacket.cjs.cjs'); require('../network/packet/SetTimePacket.cjs.cjs'); require('../network/packet/ShowProfilePacket.cjs.cjs'); require('../network/packet/StartGamePacket.cjs.cjs'); require('../network/packet/TextPacket.cjs.cjs'); require('../network/packet/TickSyncPacket.cjs.cjs'); require('../network/packet/TransferPacket.cjs.cjs'); require('../network/packet/UpdateAdventureSettingsPacket.cjs.cjs'); require('../network/packet/UpdateAttributesPacket.cjs.cjs'); const network_packet_UpdateBlockPacket = require('../network/packet/UpdateBlockPacket.cjs.cjs'); const network_packet_WorldEventPacket = require('../network/packet/WorldEventPacket.cjs.cjs'); require('node:process'); require('node:readline'); require('heap'); require('../network/packet/UpdateAbilitiesPacket.cjs.cjs'); require('@jsprismarine/raknet'); require('evt'); require('assert'); const _interopDefault = e => e && e.__esModule ? e : { default: e }; const fs__default = /*#__PURE__*/_interopDefault(fs); const LEVEL_DATA_FILE_NAME = "level.json"; const WORLDS_FOLDER_NAME = "worlds"; class World { uuid = utils_UUID.default.randomString(); name; entities = /* @__PURE__ */ new Map(); chunks = /* @__PURE__ */ new Map(); gameruleManager; currentTick = 0; provider; server; seed; generator; config; spawn = null; constructor({ name, server, provider, seed, generator, config }) { this.name = name; this.server = server; this.provider = provider; this.gameruleManager = new world_GameruleManager.default(server); this.seed = seed; this.generator = generator; this.config = config ?? {}; this.gameruleManager.setGamerule(world_GameruleManager.GameRules.ShowCoordinates, true, true); try { const path = utils_cwd.withCwd(WORLDS_FOLDER_NAME, this.name, "playerdata"); if (!fs__default.default.existsSync(path)) fs__default.default.mkdirSync(path, { recursive: true }); } catch (error) { this.server.getLogger().error(`Failed to create world folders for ${this.name}`); this.server.getLogger().error(error); } } /** * On enable hook. * @group Lifecycle */ async enable() { this.server.on("tick", async (evt) => this.update(evt.getTick())); const level = await this.getLevelData(); if (level.spawn) this.setSpawnPosition(math.Vector3.fromObject(level.spawn)); if (level.gameRules) { level.gameRules.forEach( ([name, [value, editable]]) => this.gameruleManager.setGamerule(name, value, editable) ); } if (level.entities) { for (const entityData of level.entities) { const Entity = Array.from(Object.values(entity_Entities)).find((e) => e.MOB_ID === entityData.type); if (!Entity) { this.server.getLogger().warn(`Entity type ${entityData.type} not found`); continue; } await this.addEntity( new Entity({ world: this, uuid: entityData.uuid, ...entityData.position, server: this.server }) ); } } this.provider.setWorld(this); await this.provider.enable(); this.server.getLogger().info(`Preparing start region for dimension ${this.getFormattedName()}`); const chunksToLoad = []; const timer = new utils_Timer.default(); const size = this.server.getConfig().getViewDistance() * 5; for (let x = 0; x < size; x++) { for (let z = 0; z < size; z++) { chunksToLoad.push(this.loadChunk(x, z, true)); } } await Promise.all(chunksToLoad); this.server.getLogger().verbose(`(took §e${timer.stop()} ms§r)`); } /** * On disable hook. * @group Lifecycle */ async disable() { await this.save(); await this.provider.disable(); } getGenerator() { return this.generator; } /** * Called every tick. * * @param tick */ async update(tick) { this.currentTick++; if (this.currentTick / 20 === 120) { await this.save(); } await Promise.all(this.getEntities().map((entity) => entity.update(tick))); await this.sendTime(); } /** * Returns a block instance in the given world position. * @param {number} x - block x * @param {number} y - block y * @param {number} z - block z * @param {number} [layer=0] - block storage layer (0 for blocks, 1 for liquids) */ async getBlock(x, y, z, layer = 0) { const blockId = (await this.getChunkAt(x, z)).getBlock(x, y, z, layer); const block = this.server.getBlockManager().getBlockByIdAndMeta(blockId.id, blockId.meta); if (!block) return this.server.getBlockManager().getBlock("minecraft:air"); return block; } /** * Returns the chunk in the specifies x and z, if the chunk doesn't exists * it is generated. */ async getChunk(cx, cz) { const index = world_chunk_Chunk.default.packXZ(cx, cz); if (!this.chunks.has(index)) return this.loadChunk(cx, cz); return this.chunks.get(index); } /** * Loads a chunk in a given x and z and returns its. * @param {number} x - x coordinate. * @param {number} z - z coordinate. */ async loadChunk(x, z, _ignoreWarn) { const index = world_chunk_Chunk.default.packXZ(x, z); const chunk = await this.provider.readChunk(x, z, this.seed, this.generator, this.config); this.chunks.set(index, chunk); return chunk; } /** * Sends a world event packet to all the viewers in the position chunk. * @param {Vector3} position - world position. * @param {number} event - event identifier. * @param {number} data - event data. */ async sendWorldEvent(position, event, data) { const worldEventPacket = new network_packet_WorldEventPacket.default(); worldEventPacket.eventId = event; worldEventPacket.data = data; await Promise.all(this.getPlayers().map((player) => player.getNetworkSession().send(worldEventPacket))); } async getChunkAt(x, z = 0) { if (x instanceof math.Vector3) { return this.getChunkAt(x.getX(), x.getZ()); } return this.getChunk(x >> 4, z >> 4); } /** * Returns the world default spawn position. */ async getSpawnPosition() { if (this.spawn) return this.spawn; const x = 0; const z = 0; const chunk = await this.getChunkAt(x, z); const y = chunk.getHighestBlockAt(x, z) + 1; return new math.Vector3(z, y + 2, z); } /** * Set the world's spawn position. * @param {Vector3} pos - The position. */ setSpawnPosition(pos) { this.spawn = pos; } // TODO: move this? async useItemOn(itemInHand, blockPosition, face, clickPosition, player) { if (itemInHand instanceof item_Item.Item) return; const block = itemInHand; const blockId = (await this.getChunkAt(blockPosition)).getBlock(blockPosition); const clickedBlock = this.server.getBlockManager().getBlockByIdAndMeta(blockId.id, blockId.meta); if (!block || !clickedBlock) return; if (clickedBlock.getName() === "minecraft:air" || !block.canBePlaced()) return; const placedPosition = new math.Vector3(blockPosition.getX(), blockPosition.getY(), blockPosition.getZ()); if (!clickedBlock.canBeReplaced()) switch (face) { case 0: placedPosition.setY(placedPosition.getY() - 1); break; case 1: placedPosition.setY(placedPosition.getY() + 1); break; case 2: placedPosition.setZ(placedPosition.getZ() - 1); break; case 3: placedPosition.setZ(placedPosition.getZ() + 1); break; case 4: placedPosition.setX(placedPosition.getX() - 1); break; case 5: placedPosition.setX(placedPosition.getX() + 1); break; default: throw new Error("Invalid Face"); } if (blockPosition.getY() < 0 || blockPosition.getY() > 255) return; const success = await new Promise(async (resolve) => { try { const chunk = await this.getChunkAt(placedPosition.getX(), placedPosition.getZ()); chunk.setBlock(placedPosition.getX(), placedPosition.getY(), placedPosition.getZ(), block); resolve(true); } catch (error) { player.getServer().getLogger().warn(`${player.getName()} failed to place block due to ${error}`); await player.sendMessage(error?.message); resolve(false); } }); if (!success) { if (placedPosition.getY() < 0) return; const blockUpdate2 = new network_packet_UpdateBlockPacket.default(); blockUpdate2.x = placedPosition.getX(); blockUpdate2.y = placedPosition.getY(); blockUpdate2.z = placedPosition.getZ(); blockUpdate2.blockRuntimeId = block_BlockMappings.BlockMappings.getRuntimeId(clickedBlock.getName()); return; } const runtimeId = block_BlockMappings.BlockMappings.getRuntimeId(block.getName()); const blockUpdate = new network_packet_UpdateBlockPacket.default(); blockUpdate.x = placedPosition.getX(); blockUpdate.y = placedPosition.getY(); blockUpdate.z = placedPosition.getZ(); blockUpdate.blockRuntimeId = runtimeId; await Promise.all( this.server.getSessionManager().getAllPlayers().map( async (onlinePlayer) => onlinePlayer.getNetworkSession().getConnection().sendDataPacket(blockUpdate) ) ); const pk = new network_packet_LevelSoundEventPacket.default(); pk.sound = 6; pk.positionX = placedPosition.getX(); pk.positionY = placedPosition.getY(); pk.positionZ = placedPosition.getZ(); pk.extraData = runtimeId; pk.disableRelativeVolume = false; await Promise.all( player.getWorld().getPlayers().map((target) => target.getNetworkSession().send(pk)) ); } /** * Sends the current time to all players in the world. */ async sendTime() { await Promise.all(this.getPlayers().map((player) => player.getNetworkSession().sendTime(this.getTicks()))); } /** * Adds an entity to the level. * @param {Entity} entity - The entity to add. */ async addEntity(entity) { this.entities.set(entity.getRuntimeId(), entity); if (!entity.isPlayer()) await entity.sendSpawn(); else await Promise.all(this.getEntities().map((e) => e.sendSpawn(entity))); } /** * Removes an entity from the level. * @param {Entity} entity - The entity to remove. */ async removeEntity(entity) { if (!entity.isPlayer()) await entity.sendDespawn(); else await Promise.all(this.getEntities().map((e) => e.sendDespawn(entity))); this.entities.delete(entity.getRuntimeId()); } /** * Get all entities in this world. * @returns {Entity[]} the entities. */ getEntities() { return Array.from(this.entities.values()); } /** * Get all players in this world. * @returns {Player[]} the players. */ getPlayers() { return this.getEntities().filter((e) => e.isPlayer()).filter((p) => p.isOnline()); } /** * Saves changed chunks into disk. */ async saveChunks() { const timer = new utils_Timer.default(); this.server.getLogger().info(`Saving chunks for level ${this.getFormattedName()}`); await Promise.all( Array.from(this.chunks.values()).filter((c) => c.getHasChanged()).map(async (chunk) => this.provider.writeChunk(chunk)) ); this.server.getLogger().verbose(`(took §e${timer.stop()} ms§r)!`); } async save() { this.getPlayers().forEach(async (player) => { await this.savePlayerData(player); }); await this.saveChunks(); await this.saveLevelData(); } getGameruleManager() { return this.gameruleManager; } getTicks() { return this.currentTick; } setTicks(tick) { this.currentTick = tick; } getProvider() { return this.provider; } // This is used for example in start game packet getUUID() { return this.uuid; } getName() { return this.name; } getFormattedName() { return `§b'${this.name}'/${this.generator.constructor.name}§r`; } getSeed() { return this.seed; } async getLevelData() { const path = utils_cwd.withCwd(WORLDS_FOLDER_NAME, this.name, LEVEL_DATA_FILE_NAME); if (!fs__default.default.existsSync(path)) return {}; try { const raw = await fs__default.default.promises.readFile(path, "utf-8"); return confbox.parseJSON5(raw.toString()); } catch (error) { this.server.getLogger().error(error); } return {}; } async saveLevelData() { const data = { spawn: await this.getSpawnPosition(), gamerules: Array.from(this.getGameruleManager().getGamerules()), entities: this.getEntities().filter((entity) => !entity.isPlayer() && !entity.isConsole()).map((entity) => ({ uuid: entity.getUUID(), type: entity.getType(), position: { x: entity.getX(), y: entity.getY(), z: entity.getZ(), pitch: entity.pitch, yaw: entity.yaw, headYaw: entity.headYaw } })) }; try { await fs__default.default.promises.writeFile( // FIXME: This overwrites comments in the file. utils_cwd.withCwd(WORLDS_FOLDER_NAME, this.name, LEVEL_DATA_FILE_NAME), JSON.stringify(data, null, 4) ); } catch (error) { this.server.getLogger().error(`Failed to save level data`); this.server.getLogger().error(error); } } /** * Get the player data for a player. * @param {Player} player - The player to get the data for. * @returns {Promise<WorldPlayerData>} The player data. */ async getPlayerData(player) { try { const fileName = player.getXUID(); if (!fileName) { throw new Error("Player has no XUID"); } const raw = await fs__default.default.promises.readFile( utils_cwd.withCwd(WORLDS_FOLDER_NAME, this.name, "playerdata", `${player.getXUID() || player.getName()}.json`), { flag: "r", encoding: "utf-8" } ); return confbox.parseJSON5(raw.toString()); } catch (error) { this.server.getLogger().debug(`PlayerData is missing for player ${player.getXUID()}`); this.server.getLogger().error(error); const spawn = await this.getSpawnPosition(); return { gamemode: this.server.getConfig().getGamemode(), position: { x: spawn.getX(), y: spawn.getY(), z: spawn.getZ(), pitch: 0, yaw: 0, headYaw: 0 } }; } } async savePlayerData(player) { const data = { uuid: player.getUUID(), username: player.getName(), gamemode: minecraft.getGametypeName(player.gamemode), position: { x: player.getX(), y: player.getY(), z: player.getZ(), pitch: player.pitch, yaw: player.yaw, headYaw: player.headYaw } }; try { await fs__default.default.promises.writeFile( // FIXME: This overwrites comments in the file. utils_cwd.withCwd(WORLDS_FOLDER_NAME, this.name, "playerdata", `${player.getXUID() || player.getName()}.json`), JSON.stringify(data, null, 4), { flag: "w+", encoding: "utf-8", flush: true } ); } catch (error) { this.server.getLogger().error(`Failed to save player data`); this.server.getLogger().error(error); } } /** * @returns {Server} The server instance. */ getServer() { return this.server; } } exports.World = World; //# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiV29ybGQuY2pzLmNqcyIsInNvdXJjZXMiOlsiLi4vLi4vc3JjL3dvcmxkL1dvcmxkLnRzIl0sInNvdXJjZXNDb250ZW50IjpbImltcG9ydCBHYW1lcnVsZU1hbmFnZXIsIHsgR2FtZVJ1bGVzIH0gZnJvbSAnLi9HYW1lcnVsZU1hbmFnZXInO1xuXG5pbXBvcnQgZnMgZnJvbSAnbm9kZTpmcyc7XG5cbmltcG9ydCB7IHBhcnNlSlNPTjUgfSBmcm9tICdjb25mYm94JztcblxuaW1wb3J0IHsgVmVjdG9yMyB9IGZyb20gJ0Bqc3ByaXNtYXJpbmUvbWF0aCc7XG5pbXBvcnQgeyBnZXRHYW1ldHlwZU5hbWUgfSBmcm9tICdAanNwcmlzbWFyaW5lL21pbmVjcmFmdCc7XG5pbXBvcnQgdHlwZSB7IEJsb2NrLCBQbGF5ZXIsIFNlcnZlciwgU2VydmljZSB9IGZyb20gJy4uLyc7XG5pbXBvcnQgeyBUaW1lciwgVVVJRCB9IGZyb20gJy4uLyc7XG5pbXBvcnQgeyBCbG9ja01hcHBpbmdzIH0gZnJvbSAnLi4vYmxvY2svQmxvY2tNYXBwaW5ncyc7XG5pbXBvcnQgKiBhcyBFbnRpdGllcyBmcm9tICcuLi9lbnRpdHkvRW50aXRpZXMnO1xuaW1wb3J0IHR5cGUgeyBFbnRpdHkgfSBmcm9tICcuLi9lbnRpdHkvRW50aXR5JztcbmltcG9ydCB7IEl0ZW0gfSBmcm9tICcuLi9pdGVtL0l0ZW0nO1xuaW1wb3J0IExldmVsU291bmRFdmVudFBhY2tldCBmcm9tICcuLi9uZXR3b3JrL3BhY2tldC9MZXZlbFNvdW5kRXZlbnRQYWNrZXQnO1xuaW1wb3J0IFVwZGF0ZUJsb2NrUGFja2V0IGZyb20gJy4uL25ldHdvcmsvcGFja2V0L1VwZGF0ZUJsb2NrUGFja2V0JztcbmltcG9ydCB0eXBlIHsgV29ybGRFdmVudCB9IGZyb20gJy4uL25ldHdvcmsvcGFja2V0L1dvcmxkRXZlbnRQYWNrZXQnO1xuaW1wb3J0IFdvcmxkRXZlbnRQYWNrZXQgZnJvbSAnLi4vbmV0d29yay9wYWNrZXQvV29ybGRFdmVudFBhY2tldCc7XG5pbXBvcnQgeyB3aXRoQ3dkIH0gZnJvbSAnLi4vdXRpbHMvY3dkJztcbmltcG9ydCB0eXBlIHsgR2VuZXJhdG9yIH0gZnJvbSAnLi9HZW5lcmF0b3InO1xuaW1wb3J0IENodW5rIGZyb20gJy4vY2h1bmsvQ2h1bmsnO1xuaW1wb3J0IHR5cGUgQmFzZVByb3ZpZGVyIGZyb20gJy4vcHJvdmlkZXJzL0Jhc2VQcm92aWRlcic7XG5cbmNvbnN0IExFVkVMX0RBVEFfRklMRV9OQU1FID0gJ2xldmVsLmpzb24nO1xuY29uc3QgV09STERTX0ZPTERFUl9OQU1FID0gJ3dvcmxkcyc7XG5cbmV4cG9ydCBpbnRlcmZhY2UgV29ybGREYXRhIHtcbiAgICBuYW1lOiBzdHJpbmc7XG4gICAgcGF0aDogc3RyaW5nO1xuICAgIHNlcnZlcjogU2VydmVyO1xuICAgIHByb3ZpZGVyOiBCYXNlUHJvdmlkZXI7XG4gICAgc2VlZDogbnVtYmVyO1xuICAgIGdlbmVyYXRvcjogR2VuZXJhdG9yO1xuICAgIGNvbmZpZz86IGFueTtcbn1cblxuZXhwb3J0IGludGVyZmFjZSBMZXZlbERhdGEge1xuICAgIHNwYXduOiB7IHg6IG51bWJlcjsgeTogbnVtYmVyOyB6OiBudW1iZXIgfSB8IHVuZGVmaW5lZDtcbiAgICBnYW1lUnVsZXM6IEFycmF5PFtzdHJpbmcsIGFueV0+O1xuICAgIGVudGl0aWVzOiBBcnJheTx7XG4gICAgICAgIHV1aWQ6IHN0cmluZztcbiAgICAgICAgdHlwZTogc3RyaW5nO1xuICAgICAgICBwb3NpdGlvbjoge1xuICAgICAgICAgICAgeDogbnVtYmVyO1xuICAgICAgICAgICAgeTogbnVtYmVyO1xuICAgICAgICAgICAgejogbnVtYmVyO1xuICAgICAgICB9O1xuICAgIH0+O1xufVxuZXhwb3J0IGludGVyZmFjZSBXb3JsZFBsYXllckRhdGEge1xuICAgIGdhbWVtb2RlOiBzdHJpbmc7XG4gICAgcG9zaXRpb246IHtcbiAgICAgICAgeDogbnVtYmVyO1xuICAgICAgICB5OiBudW1iZXI7XG4gICAgICAgIHo6IG51bWJlcjtcbiAgICAgICAgcGl0Y2g6IG51bWJlcjtcbiAgICAgICAgeWF3OiBudW1iZXI7XG4gICAgICAgIGhlYWRZYXc6IG51bWJlcjtcbiAgICB9O1xufVxuXG5leHBvcnQgY2xhc3MgV29ybGQgaW1wbGVtZW50cyBTZXJ2aWNlIHtcbiAgICBwcml2YXRlIHJlYWRvbmx5IHV1aWQ6IHN0cmluZyA9IFVVSUQucmFuZG9tU3RyaW5nKCk7XG4gICAgcHJpdmF0ZSBuYW1lOiBzdHJpbmc7XG5cbiAgICBwcml2YXRlIHJlYWRvbmx5IGVudGl0aWVzOiBNYXA8YmlnaW50LCBFbnRpdHk+ID0gbmV3IE1hcCgpO1xuICAgIHByaXZhdGUgcmVhZG9ubHkgY2h1bmtzOiBNYXA8YmlnaW50LCBDaHVuaz4gPSBuZXcgTWFwKCk7XG4gICAgcHJpdmF0ZSByZWFkb25seSBnYW1lcnVsZU1hbmFnZXI6IEdhbWVydWxlTWFuYWdlcjtcbiAgICBwcml2YXRlIGN1cnJlbnRUaWNrID0gMDtcbiAgICBwcml2YXRlIHJlYWRvbmx5IHByb3ZpZGVyOiBCYXNlUHJvdmlkZXI7XG4gICAgcHJpdmF0ZSByZWFkb25seSBzZXJ2ZXI6IFNlcnZlcjtcbiAgICBwcml2YXRlIHJlYWRvbmx5IHNlZWQ6IG51bWJlcjtcbiAgICBwcml2YXRlIHJlYWRvbmx5IGdlbmVyYXRvcjogR2VuZXJhdG9yO1xuICAgIHByaXZhdGUgcmVhZG9ubHkgY29uZmlnOiBPYmplY3Q7XG4gICAgcHJpdmF0ZSBzcGF3bjogVmVjdG9yMyB8IG51bGwgPSBudWxsO1xuXG4gICAgcHVibGljIGNvbnN0cnVjdG9yKHsgbmFtZSwgc2VydmVyLCBwcm92aWRlciwgc2VlZCwgZ2VuZXJhdG9yLCBjb25maWcgfTogV29ybGREYXRhKSB7XG4gICAgICAgIHRoaXMubmFtZSA9IG5hbWU7XG4gICAgICAgIHRoaXMuc2VydmVyID0gc2VydmVyO1xuICAgICAgICB0aGlzLnByb3ZpZGVyID0gcHJvdmlkZXI7XG4gICAgICAgIHRoaXMuZ2FtZXJ1bGVNYW5hZ2VyID0gbmV3IEdhbWVydWxlTWFuYWdlcihzZXJ2ZXIpO1xuICAgICAgICB0aGlzLnNlZWQgPSBzZWVkO1xuICAgICAgICB0aGlzLmdlbmVyYXRvciA9IGdlbmVyYXRvcjtcbiAgICAgICAgdGhpcy5jb25maWcgPSBjb25maWcgPz8ge307XG5cbiAgICAgICAgdGhpcy5nYW1lcnVsZU1hbmFnZXIuc2V0R2FtZXJ1bGUoR2FtZVJ1bGVzLlNob3dDb29yZGluYXRlcywgdHJ1ZSwgdHJ1ZSk7XG5cbiAgICAgICAgdHJ5IHtcbiAgICAgICAgICAgIC8vIENyZWF0ZSBmb2xkZXJzIGlmIHRoZXkgZG9uJ3QgZXhpc3QuXG4gICAgICAgICAgICBjb25zdCBwYXRoID0gd2l0aEN3ZChXT1JMRFNfRk9MREVSX05BTUUsIHRoaXMubmFtZSwgJ3BsYXllcmRhdGEnKTtcbiAgICAgICAgICAgIGlmICghZnMuZXhpc3RzU3luYyhwYXRoKSkgZnMubWtkaXJTeW5jKHBhdGgsIHsgcmVjdXJzaXZlOiB0cnVlIH0pO1xuICAgICAgICB9IGNhdGNoIChlcnJvcjogdW5rbm93bikge1xuICAgICAgICAgICAgdGhpcy5zZXJ2ZXIuZ2V0TG9nZ2VyKCkuZXJyb3IoYEZhaWxlZCB0byBjcmVhdGUgd29ybGQgZm9sZGVycyBmb3IgJHt0aGlzLm5hbWV9YCk7XG4gICAgICAgICAgICB0aGlzLnNlcnZlci5nZXRMb2dnZXIoKS5lcnJvcihlcnJvcik7XG4gICAgICAgIH1cbiAgICB9XG5cbiAgICAvKipcbiAgICAgKiBPbiBlbmFibGUgaG9vay5cbiAgICAgKiBAZ3JvdXAgTGlmZWN5Y2xlXG4gICAgICovXG4gICAgcHVibGljIGFzeW5jIGVuYWJsZSgpOiBQcm9taXNlPHZvaWQ+IHtcbiAgICAgICAgdGhpcy5zZXJ2ZXIub24oJ3RpY2snLCBhc3luYyAoZXZ0KSA9PiB0aGlzLnVwZGF0ZShldnQuZ2V0VGljaygpKSk7XG5cbiAgICAgICAgY29uc3QgbGV2ZWwgPSBhd2FpdCB0aGlzLmdldExldmVsRGF0YSgpO1xuICAgICAgICBpZiAobGV2ZWwuc3Bhd24pIHRoaXMuc2V0U3Bhd25Qb3NpdGlvbihWZWN0b3IzLmZyb21PYmplY3QobGV2ZWwuc3Bhd24pKTtcbiAgICAgICAgaWYgKGxldmVsLmdhbWVSdWxlcykge1xuICAgICAgICAgICAgbGV2ZWwuZ2FtZVJ1bGVzLmZvckVhY2goKFtuYW1lLCBbdmFsdWUsIGVkaXRhYmxlXV0pID0+XG4gICAgICAgICAgICAgICAgdGhpcy5nYW1lcnVsZU1hbmFnZXIuc2V0R2FtZXJ1bGUobmFtZSwgdmFsdWUsIGVkaXRhYmxlKVxuICAgICAgICAgICAgKTtcbiAgICAgICAgfVxuICAgICAgICBpZiAobGV2ZWwuZW50aXRpZXMpIHtcbiAgICAgICAgICAgIGZvciAoY29uc3QgZW50aXR5RGF0YSBvZiBsZXZlbC5lbnRpdGllcykge1xuICAgICAgICAgICAgICAgIGNvbnN0IEVudGl0eSA9IEFycmF5LmZyb20oT2JqZWN0LnZhbHVlcyhFbnRpdGllcykpLmZpbmQoKGUpID0+IGUuTU9CX0lEID09PSBlbnRpdHlEYXRhLnR5cGUpO1xuICAgICAgICAgICAgICAgIGlmICghRW50aXR5KSB7XG4gICAgICAgICAgICAgICAgICAgIHRoaXMuc2VydmVyLmdldExvZ2dlcigpLndhcm4oYEVudGl0eSB0eXBlICR7ZW50aXR5RGF0YS50eXBlfSBub3QgZm91bmRgKTtcbiAgICAgICAgICAgICAgICAgICAgY29udGludWU7XG4gICAgICAgICAgICAgICAgfVxuXG4gICAgICAgICAgICAgICAgYXdhaXQgdGhpcy5hZGRFbnRpdHkoXG4gICAgICAgICAgICAgICAgICAgIG5ldyBFbnRpdHkoe1xuICAgICAgICAgICAgICAgICAgICAgICAgd29ybGQ6IHRoaXMsXG4gICAgICAgICAgICAgICAgICAgICAgICB1dWlkOiBlbnRpdHlEYXRhLnV1aWQsXG4gICAgICAgICAgICAgICAgICAgICAgICAuLi5lbnRpdHlEYXRhLnBvc2l0aW9uLFxuICAgICAgICAgICAgICAgICAgICAgICAgc2VydmVyOiB0aGlzLnNlcnZlclxuICAgICAgICAgICAgICAgICAgICB9KVxuICAgICAgICAgICAgICAgICk7XG4gICAgICAgICAgICB9XG4gICAgICAgIH1cblxuICAgICAgICB0aGlzLnByb3ZpZGVyLnNldFdvcmxkKHRoaXMpO1xuICAgICAgICBhd2FpdCB0aGlzLnByb3ZpZGVyLmVuYWJsZSgpO1xuXG4gICAgICAgIHRoaXMuc2VydmVyLmdldExvZ2dlcigpLmluZm8oYFByZXBhcmluZyBzdGFydCByZWdpb24gZm9yIGRpbWVuc2lvbiAke3RoaXMuZ2V0Rm9ybWF0dGVkTmFtZSgpfWApO1xuICAgICAgICBjb25zdCBjaHVua3NUb0xvYWQ6IEFycmF5PFByb21pc2U8Q2h1bms+PiA9IFtdO1xuICAgICAgICBjb25zdCB0aW1lciA9IG5ldyBUaW1lcigpO1xuXG4gICAgICAgIGNvbnN0IHNpemUgPSB0aGlzLnNlcnZlci5nZXRDb25maWcoKS5nZXRWaWV3RGlzdGFuY2UoKSAqIDU7XG4gICAgICAgIGZvciAobGV0IHggPSAwOyB4IDwgc2l6ZTsgeCsrKSB7XG4gICAgICAgICAgICBmb3IgKGxldCB6ID0gMDsgeiA8IHNpemU7IHorKykge1xuICAgICAgICAgICAgICAgIGNodW5rc1RvTG9hZC5wdXNoKHRoaXMubG9hZENodW5rKHgsIHosIHRydWUpKTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgfVxuXG4gICAgICAgIGF3YWl0IFByb21pc2UuYWxsKGNodW5rc1RvTG9hZCk7XG4gICAgICAgIHRoaXMuc2VydmVyLmdldExvZ2dlcigpLnZlcmJvc2UoYCh0b29rIMKnZSR7dGltZXIuc3RvcCgpfSBtc8KncilgKTtcbiAgICB9XG5cbiAgICAvKipcbiAgICAgKiBPbiBkaXNhYmxlIGhvb2suXG4gICAgICogQGdyb3VwIExpZmVjeWNsZVxuICAgICAqL1xuICAgIHB1YmxpYyBhc3luYyBkaXNhYmxlKCk6IFByb21pc2U8dm9pZD4ge1xuICAgICAgICBhd2FpdCB0aGlzLnNhdmUoKTtcbiAgICAgICAgYXdhaXQgdGhpcy5wcm92aWRlci5kaXNhYmxlKCk7XG4gICAgfVxuXG4gICAgcHVibGljIGdldEdlbmVyYXRvcigpOiBHZW5lcmF0b3Ige1xuICAgICAgICByZXR1cm4gdGhpcy5nZW5lcmF0b3I7XG4gICAgfVxuXG4gICAgLyoqXG4gICAgICogQ2FsbGVkIGV2ZXJ5IHRpY2suXG4gICAgICpcbiAgICAgKiBAcGFyYW0gdGlja1xuICAgICAqL1xuICAgIHB1YmxpYyBhc3luYyB1cGRhdGUodGljazogbnVtYmVyKTogUHJvbWlzZTx2b2lkPiB7XG4gICAgICAgIC8vIFRPRE86IHRpY2sgY2h1bmtzXG5cbiAgICAgICAgLy8gQ29udGludWUgd29ybGQgdGltZSB0aWNrc1xuICAgICAgICB0aGlzLmN1cnJlbnRUaWNrKys7XG5cbiAgICAgICAgLy8gQXV0byBzYXZlIGV2ZXJ5IDIgbWludXRlc1xuICAgICAgICBpZiAodGhpcy5jdXJyZW50VGljayAvIDIwID09PSAxMjApIHtcbiAgICAgICAgICAgIGF3YWl0IHRoaXMuc2F2ZSgpO1xuICAgICAgICB9XG5cbiAgICAgICAgYXdhaXQgUHJvbWlzZS5hbGwodGhpcy5nZXRFbnRpdGllcygpLm1hcCgoZW50aXR5KSA9PiBlbnRpdHkudXBkYXRlKHRpY2spKSk7XG4gICAgICAgIGF3YWl0IHRoaXMuc2VuZFRpbWUoKTtcbiAgICB9XG5cbiAgICAvKipcbiAgICAgKiBSZXR1cm5zIGEgYmxvY2sgaW5zdGFuY2UgaW4gdGhlIGdpdmVuIHdvcmxkIHBvc2l0aW9uLlxuICAgICAqIEBwYXJhbSB7bnVtYmVyfSB4IC0gYmxvY2sgeFxuICAgICAqIEBwYXJhbSB7bnVtYmVyfSB5IC0gYmxvY2sgeVxuICAgICAqIEBwYXJhbSB7bnVtYmVyfSB6IC0gYmxvY2sgelxuICAgICAqIEBwYXJhbSB7bnVtYmVyfSBbbGF5ZXI9MF0gLSBibG9jayBzdG9yYWdlIGxheWVyICgwIGZvciBibG9ja3MsIDEgZm9yIGxpcXVpZHMpXG4gICAgICovXG4gICAgcHVibGljIGFzeW5jIGdldEJsb2NrKHg6IG51bWJlciwgeTogbnVtYmVyLCB6OiBudW1iZXIsIGxheWVyID0gMCk6IFByb21pc2U8QmxvY2s+IHtcbiAgICAgICAgY29uc3QgYmxvY2tJZCA9IChhd2FpdCB0aGlzLmdldENodW5rQXQoeCwgeikpLmdldEJsb2NrKHgsIHksIHosIGxheWVyKTtcbiAgICAgICAgY29uc3QgYmxvY2sgPSB0aGlzLnNlcnZlci5nZXRCbG9ja01hbmFnZXIoKS5nZXRCbG9ja0J5SWRBbmRNZXRhKGJsb2NrSWQuaWQsIGJsb2NrSWQubWV0YSk7XG5cbiAgICAgICAgaWYgKCFibG9jaykgcmV0dXJuIHRoaXMuc2VydmVyLmdldEJsb2NrTWFuYWdlcigpLmdldEJsb2NrKCdtaW5lY3JhZnQ6YWlyJyk7XG4gICAgICAgIHJldHVybiBibG9jaztcbiAgICB9XG5cbiAgICAvKipcbiAgICAgKiBSZXR1cm5zIHRoZSBjaHVuayBpbiB0aGUgc3BlY2lmaWVzIHggYW5kIHosIGlmIHRoZSBjaHVuayBkb2Vzbid0IGV4aXN0c1xuICAgICAqIGl0IGlzIGdlbmVyYXRlZC5cbiAgICAgKi9cbiAgICBwdWJsaWMgYXN5bmMgZ2V0Q2h1bmsoY3g6IG51bWJlciwgY3o6IG51bWJlcik6IFByb21pc2U8Q2h1bms+IHtcbiAgICAgICAgY29uc3QgaW5kZXggPSBDaHVuay5wYWNrWFooY3gsIGN6KTtcbiAgICAgICAgaWYgKCF0aGlzLmNodW5rcy5oYXMoaW5kZXgpKSByZXR1cm4gdGhpcy5sb2FkQ2h1bmsoY3gsIGN6KTtcblxuICAgICAgICByZXR1cm4gdGhpcy5jaHVua3MuZ2V0KGluZGV4KSE7XG4gICAgfVxuXG4gICAgLyoqXG4gICAgICogTG9hZHMgYSBjaHVuayBpbiBhIGdpdmVuIHggYW5kIHogYW5kIHJldHVybnMgaXRzLlxuICAgICAqIEBwYXJhbSB7bnVtYmVyfSB4IC0geCBjb29yZGluYXRlLlxuICAgICAqIEBwYXJhbSB7bnVtYmVyfSB6IC0geiBjb29yZGluYXRlLlxuICAgICAqL1xuICAgIHB1YmxpYyBhc3luYyBsb2FkQ2h1bmsoeDogbnVtYmVyLCB6OiBudW1iZXIsIF9pZ25vcmVXYXJuPzogYm9vbGVhbik6IFByb21pc2U8Q2h1bms+IHtcbiAgICAgICAgY29uc3QgaW5kZXggPSBDaHVuay5wYWNrWFooeCwgeik7XG4gICAgICAgIC8vIFRyeSAtIGNhdGNoIGZvciBwcm92aWRlciBlcnJvcnNcbiAgICAgICAgY29uc3QgY2h1bmsgPSBhd2FpdCB0aGlzLnByb3ZpZGVyLnJlYWRDaHVuayh4LCB6LCB0aGlzLnNlZWQsIHRoaXMuZ2VuZXJhdG9yLCB0aGlzLmNvbmZpZyk7XG4gICAgICAgIHRoaXMuY2h1bmtzLnNldChpbmRleCwgY2h1bmspO1xuXG4gICAgICAgIC8vIFRPRE86IGV2ZW50IGhlcmUsIGVnIG9uQ2h1bmtMb2FkXG4gICAgICAgIHJldHVybiBjaHVuaztcbiAgICB9XG5cbiAgICAvKipcbiAgICAgKiBTZW5kcyBhIHdvcmxkIGV2ZW50IHBhY2tldCB0byBhbGwgdGhlIHZpZXdlcnMgaW4gdGhlIHBvc2l0aW9uIGNodW5rLlxuICAgICAqIEBwYXJhbSB7VmVjdG9yM30gcG9zaXRpb24gLSB3b3JsZCBwb3NpdGlvbi5cbiAgICAgKiBAcGFyYW0ge251bWJlcn0gZXZlbnQgLSBldmVudCBpZGVudGlmaWVyLlxuICAgICAqIEBwYXJhbSB7bnVtYmVyfSBkYXRhIC0gZXZlbnQgZGF0YS5cbiAgICAgKi9cbiAgICBwdWJsaWMgYXN5bmMgc2VuZFdvcmxkRXZlbnQocG9zaXRpb246IFZlY3RvcjMgfCBudWxsLCBldmVudDogV29ybGRFdmVudCwgZGF0YTogbnVtYmVyKTogUHJvbWlzZTx2b2lkPiB7XG4gICAgICAgIGNvbnN0IHdvcmxkRXZlbnRQYWNrZXQgPSBuZXcgV29ybGRFdmVudFBhY2tldCgpO1xuICAgICAgICB3b3JsZEV2ZW50UGFja2V0LmV2ZW50SWQgPSBldmVudDtcbiAgICAgICAgLy93b3JsZEV2ZW50UGFja2V0LnBvc2l0aW9uID0gcG9zaXRpb247XG4gICAgICAgIHdvcmxkRXZlbnRQYWNrZXQuZGF0YSA9IGRhdGE7XG5cbiAgICAgICAgLy8gVE9ETzogTGltaXQgZGlzdGFuY2UuXG4gICAgICAgIGF3YWl0IFByb21pc2UuYWxsKHRoaXMuZ2V0UGxheWVycygpLm1hcCgocGxheWVyKSA9PiBwbGF5ZXIuZ2V0TmV0d29ya1Nlc3Npb24oKS5zZW5kKHdvcmxkRXZlbnRQYWNrZXQpKSk7XG4gICAgfVxuXG4gICAgLyoqXG4gICAgICogUmV0dXJucyBhIGNodW5rIGZyb20gYSBibG9jayBwb3NpdGlvbidzIHggYW5kIHogY29vcmRpbmF0ZXMuXG4gICAgICovXG4gICAgcHVibGljIGFzeW5jIGdldENodW5rQXQoeDogVmVjdG9yMyk6IFByb21pc2U8Q2h1bms+O1xuICAgIHB1YmxpYyBhc3luYyBnZXRDaHVua0F0KHg6IG51bWJlciwgejogbnVtYmVyKTogUHJvbWlzZTxDaHVuaz47XG4gICAgcHVibGljIGFzeW5jIGdldENodW5rQXQoeDogVmVjdG9yMyB8IG51bWJlciwgejogbnVtYmVyID0gMCk6IFByb21pc2U8Q2h1bms+IHtcbiAgICAgICAgaWYgKHggaW5zdGFuY2VvZiBWZWN0b3IzKSB7XG4gICAgICAgICAgICByZXR1cm4gdGhpcy5nZXRDaHVua0F0KHguZ2V0WCgpLCB4LmdldFooKSk7XG4gICAgICAgIH1cblxuICAgICAgICByZXR1cm4gdGhpcy5nZXRDaHVuayh4ID4+IDQsIHogPj4gNCk7XG4gICAgfVxuXG4gICAgLyoqXG4gICAgICogUmV0dXJucyB0aGUgd29ybGQgZGVmYXVsdCBzcGF3biBwb3NpdGlvbi5cbiAgICAgKi9cbiAgICBwdWJsaWMgYXN5bmMgZ2V0U3Bhd25Qb3NpdGlvbigpOiBQcm9taXNlPFZlY3RvcjM+IHtcbiAgICAgICAgaWYgKHRoaXMuc3Bhd24pIHJldHVybiB0aGlzLnNwYXduO1xuXG4gICAgICAgIGNvbnN0IHggPSAwO1xuICAgICAgICBjb25zdCB6ID0gMDsgLy8gVE9ETzogcmVwbGFjZSB3aXRoIGFjdHVhbCBkYXRhXG4gICAgICAgIGNvbnN0IGNodW5rID0gYXdhaXQgdGhpcy5nZXRDaHVua0F0KHgsIHopO1xuICAgICAgICBjb25zdCB5ID0gY2h1bmsuZ2V0SGlnaGVzdEJsb2NrQXQoeCwgeikgKyAxO1xuICAgICAgICByZXR1cm4gbmV3IFZlY3RvcjMoeiwgeSArIDIsIHopO1xuICAgIH1cblxuICAgIC8qKlxuICAgICAqIFNldCB0aGUgd29ybGQncyBzcGF3biBwb3NpdGlvbi5cbiAgICAgKiBAcGFyYW0ge1ZlY3RvcjN9IHBvcyAtIFRoZSBwb3NpdGlvbi5cbiAgICAgKi9cbiAgICBwdWJsaWMgc2V0U3Bhd25Qb3NpdGlvbihwb3M6IFZlY3RvcjMpIHtcbiAgICAgICAgdGhpcy5zcGF3biA9IHBvcztcbiAgICB9XG5cbiAgICAvLyBUT0RPOiBtb3ZlIHRoaXM/XG4gICAgcHVibGljIGFzeW5jIHVzZUl0ZW1PbihcbiAgICAgICAgaXRlbUluSGFuZDogSXRlbSB8IEJsb2NrIHwgbnVsbCxcbiAgICAgICAgYmxvY2tQb3NpdGlvbjogVmVjdG9yMyxcbiAgICAgICAgZmFjZTogbnVtYmVyLFxuICAgICAgICBjbGlja1Bvc2l0aW9uOiBWZWN0b3IzLFxuICAgICAgICBwbGF5ZXI6IFBsYXllclxuICAgICk6IFByb21pc2U8dm9pZD4ge1xuICAgICAgICBpZiAoaXRlbUluSGFuZCBpbnN0YW5jZW9mIEl0ZW0pIHJldHVybjsgLy8gVE9ET1xuXG4gICAgICAgIC8vIFRPRE86IGNoZWNrc1xuICAgICAgICAvLyBUT0RPOiBjYW5JbnRlcmFjdFxuXG4gICAgICAgIGNvbnN0IGJsb2NrID0gaXRlbUluSGFuZDsgLy8gVE9ETzogZ2V0IGJsb2NrIGZyb20gaXRlbUluSGFuZFxuICAgICAgICBjb25zdCBibG9ja0lkID0gKGF3YWl0IHRoaXMuZ2V0Q2h1bmtBdChibG9ja1Bvc2l0aW9uKSkuZ2V0QmxvY2soYmxvY2tQb3NpdGlvbik7XG5cbiAgICAgICAgY29uc3QgY2xpY2tlZEJsb2NrID0gdGhpcy5zZXJ2ZXIuZ2V0QmxvY2tNYW5hZ2VyKCkuZ2V0QmxvY2tCeUlkQW5kTWV0YShibG9ja0lkLmlkLCBibG9ja0lkLm1ldGEpO1xuXG4gICAgICAgIGlmICghYmxvY2sgfHwgIWNsaWNrZWRCbG9jaykgcmV0dXJuO1xuICAgICAgICBpZiAoY2xpY2tlZEJsb2NrLmdldE5hbWUoKSA9PT0gJ21pbmVjcmFmdDphaXInIHx8ICFibG9jay5jYW5CZVBsYWNlZCgpKSByZXR1cm47XG5cbiAgICAgICAgY29uc3QgcGxhY2VkUG9zaXRpb24gPSBuZXcgVmVjdG9yMyhibG9ja1Bvc2l0aW9uLmdldFgoKSwgYmxvY2tQb3NpdGlvbi5nZXRZKCksIGJsb2NrUG9zaXRpb24uZ2V0WigpKTtcblxuICAgICAgICAvLyBPbmx5IHNldCBjb3JyZWN0IGZhY2UgaWYgdGhlIGJsb2NrIGNhbid0IGJlIHJlcGxhY2VkXG4gICAgICAgIGlmICghY2xpY2tlZEJsb2NrLmNhbkJlUmVwbGFjZWQoKSlcbiAgICAgICAgICAgIHN3aXRjaCAoZmFjZSkge1xuICAgICAgICAgICAgICAgIGNhc2UgMDogLy8gQm90dG9tXG4gICAgICAgICAgICAgICAgICAgIHBsYWNlZFBvc2l0aW9uLnNldFkocGxhY2VkUG9zaXRpb24uZ2V0WSgpIC0gMSk7XG4gICAgICAgICAgICAgICAgICAgIGJyZWFrO1xuICAgICAgICAgICAgICAgIGNhc2UgMTogLy8gVG9wXG4gICAgICAgICAgICAgICAgICAgIHBsYWNlZFBvc2l0aW9uLnNldFkocGxhY2VkUG9zaXRpb24uZ2V0WSgpICsgMSk7XG4gICAgICAgICAgICAgICAgICAgIGJyZWFrO1xuICAgICAgICAgICAgICAgIGNhc2UgMjogLy8gRnJvbnRcbiAgICAgICAgICAgICAgICAgICAgcGxhY2VkUG9zaXRpb24uc2V0WihwbGFjZWRQb3NpdGlvbi5nZXRaKCkgLSAxKTtcbiAgICAgICAgICAgICAgICAgICAgYnJlYWs7XG4gICAgICAgICAgICAgICAgY2FzZSAzOiAvLyBCYWNrXG4gICAgICAgICAgICAgICAgICAgIHBsYWNlZFBvc2l0aW9uLnNldFoocGxhY2VkUG9zaXRpb24uZ2V0WigpICsgMSk7XG4gICAgICAgICAgICAgICAgICAgIGJyZWFrO1xuICAgICAgICAgICAgICAgIGNhc2UgNDogLy8gUmlnaHRcbiAgICAgICAgICAgICAgICAgICAgcGxhY2VkUG9zaXRpb24uc2V0WChwbGFjZWRQb3NpdGlvbi5nZXRYKCkgLSAxKTtcbiAgICAgICAgICAgICAgICAgICAgYnJlYWs7XG4gICAgICAgICAgICAgICAgY2FzZSA1OiAvLyBMZWZ0XG4gICAgICAgICAgICAgICAgICAgIHBsYWNlZFBvc2l0aW9uLnNldFgocGxhY2VkUG9zaXRpb24uZ2V0WCgpICsgMSk7XG4gICAgICAgICAgICAgICAgICAgIGJyZWFrO1xuICAgICAgICAgICAgICAgIGRlZmF1bHQ6XG4gICAgICAgICAgICAgICAgICAgIHRocm93IG5ldyBFcnJvcignSW52YWxpZCBGYWNlJyk7XG4gICAgICAgICAgICB9XG5cbiAgICAgICAgaWYgKGJsb2NrUG9zaXRpb24uZ2V0WSgpIDwgMCB8fCBibG9ja1Bvc2l0aW9uLmdldFkoKSA+IDI1NSkgcmV0dXJuO1xuXG4gICAgICAgIGNvbnN0IHN1Y2Nlc3M6IGJvb2xlYW4gPSBhd2FpdCBuZXcgUHJvbWlzZShhc3luYyAocmVzb2x2ZSkgPT4ge1xuICAgICAgICAgICAgdHJ5IHtcbiAgICAgICAgICAgICAgICBjb25zdCBjaHVuayA9IGF3YWl0IHRoaXMuZ2V0Q2h1bmtBdChwbGFjZWRQb3NpdGlvbi5nZXRYKCksIHBsYWNlZFBvc2l0aW9uLmdldFooKSk7XG5cbiAgICAgICAgICAgICAgICBjaHVuay5zZXRCbG9jayhwbGFjZWRQb3NpdGlvbi5nZXRYKCksIHBsYWNlZFBvc2l0aW9uLmdldFkoKSwgcGxhY2VkUG9zaXRpb24uZ2V0WigpLCBibG9jayk7XG4gICAgICAgICAgICAgICAgcmVzb2x2ZSh0cnVlKTtcbiAgICAgICAgICAgIH0gY2F0Y2ggKGVycm9yOiB1bmtub3duKSB7XG4gICAgICAgICAgICAgICAgcGxheWVyLmdldFNlcnZlcigpLmdldExvZ2dlcigpLndhcm4oYCR7cGxheWVyLmdldE5hbWUoKX0gZmFpbGVkIHRvIHBsYWNlIGJsb2NrIGR1ZSB0byAke2Vycm9yfWApO1xuICAgICAgICAgICAgICAgIGF3YWl0IHBsYXllci5zZW5kTWVzc2FnZSgoZXJyb3IgYXMgYW55KT8ubWVzc2FnZSk7XG5cbiAgICAgICAgICAgICAgICByZXNvbHZlKGZhbHNlKTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgfSk7XG5cbiAgICAgICAgaWYgKCFzdWNjZXNzKSB7XG4gICAgICAgICAgICBpZiAocGxhY2VkUG9zaXRpb24uZ2V0WSgpIDwgMCkgcmV0dXJuO1xuXG4gICAgICAgICAgICBjb25zdCBibG9ja1VwZGF0ZSA9IG5ldyBVcGRhdGVCbG9ja1BhY2tldCgpO1xuICAgICAgICAgICAgYmxvY2tVcGRhdGUueCA9IHBsYWNlZFBvc2l0aW9uLmdldFgoKTtcbiAgICAgICAgICAgIGJsb2NrVXBkYXRlLnkgPSBwbGFjZWRQb3NpdGlvbi5nZXRZKCk7XG4gICAgICAgICAgICBibG9ja1VwZGF0ZS56ID0gcGxhY2VkUG9zaXRpb24uZ2V0WigpO1xuICAgICAgICAgICAgYmxvY2tVcGRhdGUuYmxvY2tSdW50aW1lSWQgPSBCbG9ja01hcHBpbmdzLmdldFJ1bnRpbWVJZChjbGlja2VkQmxvY2suZ2V0TmFtZSgpKTtcbiAgICAgICAgICAgIHJldHVybjtcbiAgICAgICAgfVxuXG4gICAgICAgIGNvbnN0IHJ1bnRpbWVJZCA9IEJsb2NrTWFwcGluZ3MuZ2V0UnVudGltZUlkKGJsb2NrLmdldE5hbWUoKSk7XG5cbiAgICAgICAgY29uc3QgYmxvY2tVcGRhdGUgPSBuZXcgVXBkYXRlQmxvY2tQYWNrZXQoKTtcbiAgICAgICAgYmxvY2tVcGRhdGUueCA9IHBsYWNlZFBvc2l0aW9uLmdldFgoKTtcbiAgICAgICAgYmxvY2tVcGRhdGUueSA9IHBsYWNlZFBvc2l0aW9uLmdldFkoKTtcbiAgICAgICAgYmxvY2tVcGRhdGUueiA9IHBsYWNlZFBvc2l0aW9uLmdldFooKTtcbiAgICAgICAgYmxvY2tVcGRhdGUuYmxvY2tSdW50aW1lSWQgPSBydW50aW1lSWQ7XG5cbiAgICAgICAgYXdhaXQgUHJvbWlzZS5hbGwoXG4gICAgICAgICAgICB0aGlzLnNlcnZlclxuICAgICAgICAgICAgICAgIC5nZXRTZXNzaW9uTWFuYWdlcigpXG4gICAgICAgICAgICAgICAgLmdldEFsbFBsYXllcnMoKVxuICAgICAgICAgICAgICAgIC5tYXAoYXN5bmMgKG9ubGluZVBsYXllcikgPT5cbiAgICAgICAgICAgICAgICAgICAgb25saW5lUGxheWVyLmdldE5ldHdvcmtTZXNzaW9uKCkuZ2V0Q29ubmVjdGlvbigpLnNlbmREYXRhUGFja2V0KGJsb2NrVXBkYXRlKVxuICAgICAgICAgICAgICAgIClcbiAgICAgICAgKTtcblxuICAgICAgICBjb25zdCBwayA9IG5ldyBMZXZlbFNvdW5kRXZlbnRQYWNrZXQoKTtcbiAgICAgICAgcGsuc291bmQgPSA2OyAvLyBUT0RPOiBlbnVtXG5cbiAgICAgICAgcGsucG9zaXRpb25YID0gcGxhY2VkUG9zaXRpb24uZ2V0WCgpO1xuICAgICAgICBway5wb3NpdGlvblkgPSBwbGFjZWRQb3NpdGlvbi5nZXRZKCk7XG4gICAgICAgIHBrLnBvc2l0aW9uWiA9IHBsYWNlZFBvc2l0aW9uLmdldFooKTtcblxuICAgICAgICBway5leHRyYURhdGEgPSBydW50aW1lSWQ7IC8vIEluIHRoaXMgY2FzZSByZWZlcnMgdG8gYmxvY2sgcnVudGltZSBJZFxuICAgICAgICBway5kaXNhYmxlUmVsYXRpdmVWb2x1bWUgPSBmYWxzZTtcblxuICAgICAgICBhd2FpdCBQcm9taXNlLmFsbChcbiAgICAgICAgICAgIHBsYXllclxuICAgICAgICAgICAgICAgIC5nZXRXb3JsZCgpXG4gICAgICAgICAgICAgICAgLmdldFBsYXllcnMoKVxuICAgICAgICAgICAgICAgIC5tYXAoKHRhcmdldCkgPT4gdGFyZ2V0LmdldE5ldHdvcmtTZXNzaW9uKCkuc2VuZChwaykpXG4gICAgICAgICk7XG4gICAgfVxuXG4gICAgLyoqXG4gICAgICogU2VuZHMgdGhlIGN1cnJlbnQgdGltZSB0byBhbGwgcGxheWVycyBpbiB0aGUgd29ybGQuXG4gICAgICovXG4gICAgcHVibGljIGFzeW5jIHNlbmRUaW1lKCk6IFByb21pc2U8dm9pZD4ge1xuICAgICAgICAvLyBUcnkgdG8gc2VuZCBpdCBhdCB0aGUgc2FtZSB0aW1lIHRvIGFsbFxuICAgICAgICBhd2FpdCBQcm9taXNlLmFsbCh0aGlzLmdldFBsYXllcnMoKS5tYXAoKHBsYXllcikgPT4gcGxheWVyLmdldE5ldHdvcmtTZXNzaW9uKCkuc2VuZFRpbWUodGhpcy5nZXRUaWNrcygpKSkpO1xuICAgIH1cblxuICAgIC8qKlxuICAgICAqIEFkZHMgYW4gZW50aXR5IHRvIHRoZSBsZXZlbC5cbiAgICAgKiBAcGFyYW0ge0VudGl0eX0gZW50aXR5IC0gVGhlIGVudGl0eSB0byBhZGQuXG4gICAgICovXG4gICAgcHVibGljIGFzeW5jIGFkZEVudGl0eShlbnRpdHk6IEVudGl0eSk6IFByb21pc2U8dm9pZD4ge1xuICAgICAgICB0aGlzLmVudGl0aWVzLnNldChlbnRpdHkuZ2V0UnVudGltZUlkKCksIGVudGl0eSk7XG5cbiAgICAgICAgaWYgKCFlbnRpdHkuaXNQbGF5ZXIoKSkgYXdhaXQgZW50aXR5LnNlbmRTcGF3bigpO1xuICAgICAgICBlbHNlIGF3YWl0IFByb21pc2UuYWxsKHRoaXMuZ2V0RW50aXRpZXMoKS5tYXAoKGUpID0+IGUuc2VuZFNwYXduKGVudGl0eSBhcyBQbGF5ZXIpKSk7XG4gICAgfVxuXG4gICAgLyoqXG4gICAgICogUmVtb3ZlcyBhbiBlbnRpdHkgZnJvbSB0aGUgbGV2ZWwuXG4gICAgICogQHBhcmFtIHtFbnRpdHl9IGVudGl0eSAtIFRoZSBlbnRpdHkgdG8gcmVtb3ZlLlxuICAgICAqL1xuICAgIHB1YmxpYyBhc3luYyByZW1vdmVFbnRpdHkoZW50aXR5OiBFbnRpdHkpOiBQcm9taXNlPHZvaWQ+IHtcbiAgICAgICAgaWYgKCFlbnRpdHkuaXNQbGF5ZXIoKSkgYXdhaXQgZW50aXR5LnNlbmREZXNwYXduKCk7XG4gICAgICAgIGVsc2UgYXdhaXQgUHJvbWlzZS5hbGwodGhpcy5nZXRFbnRpdGllcygpLm1hcCgoZSkgPT4gZS5zZW5kRGVzcGF3bihlbnRpdHkgYXMgUGxheWVyKSkpO1xuXG4gICAgICAgIHRoaXMuZW50aXRpZXMuZGVsZXRlKGVudGl0eS5nZXRSdW50aW1lSWQoKSk7XG4gICAgfVxuXG4gICAgLyoqXG4gICAgICogR2V0IGFsbCBlbnRpdGllcyBpbiB0aGlzIHdvcmxkLlxuICAgICAqIEByZXR1cm5zIHtFbnRpdHlbXX0gdGhlIGVudGl0aWVzLlxuICAgICAqL1xuICAgIHB1YmxpYyBnZXRFbnRpdGllcygpOiBFbnRpdHlbXSB7XG4gICAgICAgIHJldHVybiBBcnJheS5mcm9tKHRoaXMuZW50aXRpZXMudmFsdWVzKCkpO1xuICAgIH1cbiAgICAvKipcbiAgICAgKiBHZXQgYWxsIHBsYXllcnMgaW4gdGhpcyB3b3JsZC5cbiAgICAgKiBAcmV0dXJucyB7UGxheWVyW119IHRoZSBwbGF5ZXJzLlxuICAgICAqL1xuICAgIHB1YmxpYyBnZXRQbGF5ZXJzKCk6IFBsYXllcltdIHtcbiAgICAgICAgcmV0dXJuICh0aGlzLmdldEVudGl0aWVzKCkuZmlsdGVyKChlKSA9PiBlLmlzUGxheWVyKCkpIGFzIFBsYXllcltdKS5maWx0ZXIoKHApID0+IHAuaXNPbmxpbmUoKSk7XG4gICAgfVxuXG4gICAgLyoqXG4gICAgICogU2F2ZXMgY2hhbmdlZCBjaHVua3MgaW50byBkaXNrLlxuICAgICAqL1xuICAgIHB1YmxpYyBhc3luYyBzYXZlQ2h1bmtzKCk6IFByb21pc2U8dm9pZD4ge1xuICAgICAgICBjb25zdCB0aW1lciA9IG5ldyBUaW1lcigpO1xuICAgICAgICB0aGlzLnNlcnZlci5nZXRMb2dnZXIoKS5pbmZvKGBTYXZpbmcgY2h1bmtzIGZvciBsZXZlbCAke3RoaXMuZ2V0Rm9ybWF0dGVkTmFtZSgpfWApO1xuXG4gICAgICAgIGF3YWl0IFByb21pc2UuYWxsKFxuICAgICAgICAgICAgQXJyYXkuZnJvbSh0aGlzLmNodW5rcy52YWx1ZXMoKSlcbiAgICAgICAgICAgICAgICAuZmlsdGVyKChjKSA9PiBjLmdldEhhc0NoYW5nZWQoKSlcbiAgICAgICAgICAgICAgICAubWFwKGFzeW5jIChjaHVuaykgPT4gdGhpcy5wcm92aWRlci53cml0ZUNodW5rKGNodW5rKSlcbiAgICAgICAgKTtcbiAgICAgICAgdGhpcy5zZXJ2ZXIuZ2V0TG9nZ2VyKCkudmVyYm9zZShgKHRvb2sgwqdlJHt0aW1lci5zdG9wKCl9IG1zwqdyKSFgKTtcbiAgICB9XG5cbiAgICBwdWJsaWMgYXN5bmMgc2F2ZSgpOiBQcm9taXNlPHZvaWQ+IHtcbiAgICAgICAgLy8gU2F2ZSBjaHVua3NcbiAgICAgICAgdGhpcy5nZXRQbGF5ZXJzKCkuZm9yRWFjaChhc3luYyAocGxheWVyKSA9PiB7XG4gICAgICAgICAgICBhd2FpdCB0aGlzLnNhdmVQbGF5ZXJEYXRhKHBsYXllcik7XG4gICAgICAgIH0pO1xuICAgICAgICBhd2FpdCB0aGlzLnNhdmVDaHVua3MoKTtcbiAgICAgICAgYXdhaXQgdGhpcy5zYXZlTGV2ZWxEYXRhKCk7XG4gICAgfVxuXG4gICAgcHVibGljIGdldEdhbWVydWxlTWFuYWdlcigpOiBHYW1lcnVsZU1hbmFnZXIge1xuICAgICAgICByZXR1cm4gdGhpcy5nYW1lcnVsZU1hbmFnZXI7XG4gICAgfVxuXG4gICAgcHVibGljIGdldFRpY2tzKCk6IG51bWJlciB7XG4gICAgICAgIHJldHVybiB0aGlzLmN1cnJlbnRUaWNrO1xuICAgIH1cblxuICAgIHB1YmxpYyBzZXRUaWNrcyh0aWNrOiBudW1iZXIpOiB2b2lkIHtcbiAgICAgICAgdGhpcy5jdXJyZW50VGljayA9IHRpY2s7XG4gICAgfVxuXG4gICAgcHVibGljIGdldFByb3ZpZGVyKCk6IGFueSB7XG4gICAgICAgIHJldHVybiB0aGlzLnByb3ZpZGVyO1xuICAgIH1cblxuICAgIC8vIFRoaXMgaXMgdXNlZCBmb3IgZXhhbXBsZSBpbiBzdGFydCBnYW1lIHBhY2tldFxuICAgIHB1YmxpYyBnZXRVVUlEKCk6IHN0cmluZyB7XG4gICAgICAgIHJldHVybiB0aGlzLnV1aWQ7XG4gICAgfVxuXG4gICAgcHVibGljIGdldE5hbWUoKTogc3RyaW5nIHtcbiAgICAgICAgcmV0dXJuIHRoaXMubmFtZTtcbiAgICB9XG4gICAgcHVibGljIGdldEZvcm1hdHRlZE5hbWUoKTogc3RyaW5nIHtcbiAgICAgICAgcmV0dXJuIGDCp2InJHt0aGlzLm5hbWV9Jy8ke3RoaXMuZ2VuZXJhdG9yLmNvbnN0cnVjdG9yLm5hbWV9wqdyYDtcbiAgICB9XG5cbiAgICBwdWJsaWMgZ2V0U2VlZCgpOiBudW1iZXIge1xuICAgICAgICByZXR1cm4gdGhpcy5zZWVkO1xuICAgIH1cblxuICAgIHByaXZhdGUgYXN5bmMgZ2V0TGV2ZWxEYXRhKCkge1xuICAgICAgICBjb25zdCBwYXRoID0gd2l0aEN3ZChXT1JMRFNfRk9MREVSX05BTUUsIHRoaXMubmFtZSwgTEVWRUxfREFUQV9GSUxFX05BTUUpO1xuICAgICAgICBpZiAoIWZzLmV4aXN0c1N5bmMocGF0aCkpIHJldHVybiB7fTtcblxuICAgICAgICB0cnkge1xuICAgICAgICAgICAgY29uc3QgcmF3ID0gYXdhaXQgZnMucHJvbWlzZXMucmVhZEZpbGUocGF0aCwgJ3V0Zi04Jyk7XG4gICAgICAgICAgICByZXR1cm4gcGFyc2VKU09ONShyYXcudG9TdHJpbmcoKSkgYXMgUGFydGlhbDxMZXZlbERhdGE+O1xuICAgICAgICB9IGNhdGNoIChlcnJvcjogYW55KSB7XG4gICAgICAgICAgICAvLyBTb21ldGhpbmcgd2VudCB3cm9uZyB3aGlsZSByZWFkaW5nIG9yIHBhcnNpbmcgdGhlIGxldmVsIGRhdGEuXG4gICAgICAgICAgICB0aGlzLnNlcnZlci5nZXRMb2dnZXIoKS5lcnJvcihlcnJvcik7XG4gICAgICAgIH1cblxuICAgICAgICByZXR1cm4ge307XG4gICAgfVxuICAgIHB1YmxpYyBhc3luYyBzYXZlTGV2ZWxEYXRhKCk6IFByb21pc2U8dm9pZD4ge1xuICAgICAgICBjb25zdCBkYXRhID0ge1xuICAgICAgICAgICAgc3Bhd246IGF3YWl0IHRoaXMuZ2V0U3Bhd25Qb3NpdGlvbigpLFxuICAgICAgICAgICAgZ2FtZXJ1bGVzOiBBcnJheS5mcm9tKHRoaXMuZ2V0R2FtZXJ1bGVNYW5hZ2VyKCkuZ2V0R2FtZXJ1bGVzKCkpLFxuICAgICAgICAgICAgZW50aXRpZXM6IHRoaXMuZ2V0RW50aXRpZXMoKVxuICAgICAgICAgICAgICAgIC5maWx0ZXIoKGVudGl0eSkgPT4gIWVudGl0eS5pc1BsYXllcigpICYmICFlbnRpdHkuaXNDb25zb2xlKCkpXG4gICAgICAgICAgICAgICAgLm1hcCgoZW50aXR5KSA9PiAoe1xuICAgICAgICAgICAgICAgICAgICB1dWlkOiBlbnRpdHkuZ2V0VVVJRCgpLFxuICAgICAgICAgICAgICAgICAgICB0eXBlOiBlbnRpdHkuZ2V0VHlwZSgpLFxuICAgICAgICAgICAgICAgICAgICBwb3NpdGlvbjoge1xuICAgICAgICAgICAgICAgICAgICAgICAgeDogZW50aXR5LmdldFgoKSxcbiAgICAgICAgICAgICAgICAgICAgICAgIHk6IGVudGl0eS5nZXRZKCksXG4gICAgICAgICAgICAgICAgICAgICAgICB6OiBlbnRpdHkuZ2V0WigpLFxuICAgICAgICAgICAgICAgICAgICAgICAgcGl0Y2g6IGVudGl0eS5waXRjaCxcbiAgICAgICAgICAgICAgICAgICAgICAgIHlhdzogZW50aXR5LnlhdyxcbiAgICAgICAgICAgICAgICAgICAgICAgIGhlYWRZYXc6IGVudGl0eS5oZWFkWWF3XG4gICAgICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICB9KSlcbiAgICAgICAgfTtcblxuICAgICAgICB0cnkge1xuICAgICAgICAgICAgYXdhaXQgZnMucHJvbWlzZXMud3JpdGVGaWxlKFxuICAgICAgICAgICAgICAgIC8vIEZJWE1FOiBUaGlzIG92ZXJ3cml0ZXMgY29tbWVudHMgaW4gdGhlIGZpbGUuXG4gICAgICAgICAgICAgICAgd2l0aEN3ZChXT1JMRFNfRk9MREVSX05BTUUsIHRoaXMubmFtZSwgTEVWRUxfREFUQV9GSUxFX05BTUUpLFxuICAgICAgICAgICAgICAgIEpTT04uc3RyaW5naWZ5KGRhdGEsIG51bGwsIDQpXG4gICAgICAgICAgICApO1xuICAgICAgICB9IGNhdGNoIChlcnJvcjogdW5rbm93bikge1xuICAgICAgICAgICAgdGhpcy5zZXJ2ZXIuZ2V0TG9nZ2VyKCkuZXJyb3IoYEZhaWxlZCB0byBzYXZlIGxldmVsIGRhdGFgKTtcbiAgICAgICAgICAgIHRoaXMuc2VydmVyLmdldExvZ2dlcigpLmVycm9yKGVycm9yKTtcbiAgICAgICAgfVxuICAgIH1cblxuICAgIC8qKlxuICAgICAqIEdldCB0aGUgcGxheWVyIGRhdGEgZm9yIGEgcGxheWVyLlxuICAgICAqIEBwYXJhbSB7UGxheWVyfSBwbGF5ZXIgLSBUaGUgcGxheWVyIHRvIGdldCB0aGUgZGF0YSBmb3IuXG4gICAgICogQHJldHVybnMge1Byb21pc2U8V29ybGRQbGF5ZXJEYXRhPn0gVGhlIHBsYXllciBkYXRhLlxuICAgICAqL1xuICAgIHB1YmxpYyBhc3luYyBnZXRQbGF5ZXJEYXRhKHBsYXllcjogUGxheWVyKTogUHJvbWlzZTxQYXJ0aWFsPFdvcmxkUGxheWVyRGF0YT4+IHtcbiAgICAgICAgdHJ5IHtcbiAgICAgICAgICAgIGNvbnN0IGZpbGVOYW1lID0gcGxheWVyLmdldFhVSUQoKTtcbiAgICAgICAgICAgIGlmICghZmlsZU5hbWUpIHtcbiAgICAgICAgICAgICAgICB0aHJvdyBuZXcgRXJyb3IoJ1BsYXllciBoYXMgbm8gWFVJRCcpO1xuICAgICAgICAgICAgfVxuXG4gICAgICAgICAgICBjb25zdCByYXcgPSBhd2FpdCBmcy5wcm9taXNlcy5yZWFkRmlsZShcbiAgICAgICAgICAgICAgICB3aXRoQ3dkKFdPUkxEU19GT0xERVJfTkFNRSwgdGhpcy5uYW1lLCAncGxheWVyZGF0YScsIGAke3BsYXllci5nZXRYVUlEKCkgfHwgcGxheWVyLmdldE5hbWUoKX0uanNvbmApLFxuICAgICAgICAgICAgICAgIHsgZmxhZzogJ3InLCBlbmNvZGluZzogJ3V0Zi04JyB9XG4gICAgICAgICAgICApO1xuICAgICAgICAgICAgcmV0dXJuIHBhcnNlSlNPTjUocmF3LnRvU3RyaW5nKCkpIGFzIFBhcnRpYWw8V29ybGRQbGF5ZXJEYXRhPjtcbiAgICAgICAgfSBjYXRjaCAoZXJyb3I6IHVua25vd24pIHtcbiAgICAgICAgICAgIHRoaXMuc2VydmVyLmdldExvZ2dlcigpLmRlYnVnKGBQbGF5ZXJEYXRhIGlzIG1pc3NpbmcgZm9yIHBsYXllciAke3BsYXllci5nZXRYVUlEKCl9YCk7XG4gICAgICAgICAgICB0aGlzLnNlcnZlci5nZXRMb2dnZXIoKS5lcnJvcihlcnJvcik7XG5cbiAgICAgICAgICAgIGNvbnN0IHNwYXduID0gYXdhaXQgdGhpcy5nZXRTcGF3blBvc2l0aW9uKCk7XG4gICAgICAgICAgICByZXR1cm4ge1xuICAgICAgICAgICAgICAgIGdhbWVtb2RlOiB0aGlzLnNlcnZlci5nZXRDb25maWcoKS5nZXRHYW1lbW9kZSgpLFxuICAgICAgICAgICAgICAgIHBvc2l0aW9uOiB7XG4gICAgICAgICAgICAgICAgICAgIHg6IHNwYXduLmdldFgoKSxcbiAgICAgICAgICAgICAgICAgICAgeTogc3Bhd24uZ2V0WSgpLFxuICAgICAgICAgICAgICAgICAgICB6OiBzcGF3bi5nZXRaKCksXG4gICAgICAgICAgICAgICAgICAgIHBpdGNoOiAwLFxuICAgICAgICAgICAgICAgICAgICB5YXc6IDAsXG4gICAgICAgICAgICAgICAgICAgIGhlYWRZYXc6IDBcbiAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICB9O1xuICAgICAgICB9XG4gICAgfVxuICAgIHB1YmxpYyBhc3luYyBzYXZlUGxheWVyRGF0YShwbGF5ZXI6IFBsYXllcik6IFByb21pc2U8dm9pZD4ge1xuICAgICAgICBjb25zdCBkYXRhID0ge1xuICAgICAgICAgICAgdXVpZDogcGxheWVyLmdldFVVSUQoKSxcbiAgICAgICAgICAgIHVzZXJuYW1lOiBwbGF5ZXIuZ2V0TmFtZSgpLFxuICAgICAgICAgICAgZ2FtZW1vZGU6IGdldEdhbWV0eXBlTmFtZShwbGF5ZXIuZ2FtZW1vZGUpLFxuICAgICAgICAgICAgcG9zaXRpb246IHtcbiAgICAgICAgICAgICAgICB4OiBwbGF5ZXIuZ2V0WCgpLFxuICAgICAgICAgICAgICAgIHk6IHBsYXllci5nZXRZKCksXG4gICAgICAgICAgICAgICAgejogcGxheWVyLmdldFooKSxcbiAgICAgICAgICAgICAgICBwaXRjaDogcGxheWVyLnBpdGNoLFxuICAgICAgICAgICAgICAgIHlhdzogcGxheWVyLnlhdyxcbiAgICAgICAgICAgICAgICBoZWFkWWF3OiBwbGF5ZXIuaGVhZFlhd1xuICAgICAgICAgICAgfVxuICAgICAgICB9IGFzIFdvcmxkUGxheWVyRGF0YTtcblxuICAgICAgICB0cnkge1xuICAgICAgICAgICAgYXdhaXQgZnMucHJvbWlzZXMud3JpdGVGaWxlKFxuICAgICAgICAgICAgICAgIC8vIEZJWE1FOiBUaGlzIG92ZXJ3cml0ZXMgY29tbWVudHMgaW4gdGhlIGZpbGUuXG4gICAgICAgICAgICAgICAgd2l0aEN3ZChXT1JMRFNfRk9MREVSX05BTUUsIHRoaXMubmFtZSwgJ3BsYXllcmRhdGEnLCBgJHtwbGF5ZXIuZ2V0WFVJRCgpIHx8IHBsYXllci5nZXROYW1lKCl9Lmpzb25gKSxcbiAgICAgICAgICAgICAgICBKU09OLnN0cmluZ2lmeShkYXRhLCBudWxsLCA0KSxcbiAgICAgICAgICAgICAgICB7IGZsYWc6ICd3KycsIGVuY29kaW5nOiAndXRmLTgnLCBmbHVzaDogdHJ1ZSB9XG4gICAgICAgICAgICApO1xuICAgICAgICB9IGNhdGNoIChlcnJvcjogdW5rbm93bikge1xuICAgICAgICAgICAgdGhpcy5zZXJ2ZXIuZ2V0TG9nZ2VyKCkuZXJyb3IoYEZhaWxlZCB0byBzYXZlIHBsYXllciBkYXRhYCk7XG4gICAgICAgICAgICB0aGlzLnNlcnZlci5nZXRMb2dnZXIoKS5lcnJvcihlcnJvcik7XG4gICAgICAgIH1cbiAgICB9XG5cbiAgICAvKipcbiAgICAgKiBAcmV0dXJucyB7U2VydmVyfSBUaGUgc2VydmVyIGluc3RhbmNlLlxuICAgICAqL1xuICAgIHB1YmxpYyBnZXRTZXJ2ZXIoKTogU2VydmVyIHtcbiAgICAgICAgcmV0dXJuIHRoaXMuc2VydmVyO1xuICAgIH1cbn1cbiJdLCJuYW1lcyI6WyJVVUlEIiwiR2FtZXJ1bGVNYW5hZ2VyIiwiR2FtZVJ1bGVzIiwid2l0aEN3ZCIsImZzIiwiVmVjdG9yMyIsIkVudGl0aWVzIiwiVGltZXIiLCJDaHVuayIsIldvcmxkRXZlbnRQYWNrZXQiLCJJdGVtIiwiYmxvY2tVcGRhdGUiLCJVcGRhdGVCbG9ja1BhY2tldCIsIkJsb2NrTWFwcGluZ3MiLCJMZXZlbFNvdW5kRXZlbnRQYWNrZXQiLCJwYXJzZUpTT041IiwiZ2V0R2FtZXR5cGVOYW1lIl0sIm1hcHBpbmdzIjoiOzs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7O0FBdUJBLE1BQU0sb0JBQXVCLEdBQUEsWUFBQTtBQUM3QixNQUFNLGtCQUFxQixHQUFBLFFBQUE7QUFxQ3BCLE1BQU0sS0FBeUIsQ0FBQTtBQUFBLEVBQ2pCLElBQUEsR0FBZUEsbUJBQUssWUFBYSxFQUFBO0FBQUEsRUFDMUMsSUFBQTtBQUFBLEVBRVMsUUFBQSx1QkFBb0MsR0FBSSxFQUFBO0FBQUEsRUFDeEMsTUFBQSx1QkFBaUMsR0FBSSxFQUFBO0FBQUEsRUFDckMsZUFBQTtBQUFBLEVBQ1QsV0FBYyxHQUFBLENBQUE7QUFBQSxFQUNMLFFBQUE7QUFBQSxFQUNBLE1BQUE7QUFBQSxFQUNBLElBQUE7QUFBQSxFQUNBLFNBQUE7QUFBQSxFQUNBLE1BQUE7QUFBQSxFQUNULEtBQXdCLEdBQUEsSUFBQTtBQUFBLEVBRXpCLFdBQUEsQ0FBWSxFQUFFLElBQU0sRUFBQSxNQUFBLEVBQVEsVUFBVSxJQUFNLEVBQUEsU0FBQSxFQUFXLFFBQXFCLEVBQUE7QUFDL0UsSUFBQSxJQUFBLENBQUssSUFBTyxHQUFBLElBQUE7QUFDWixJQUFBLElBQUEsQ0FBSyxNQUFTLEdBQUEsTUFBQTtBQUNkLElBQUEsSUFBQSxDQUFLLFFBQVcsR0FBQSxRQUFBO0FBQ2hCLElBQUssSUFBQSxDQUFBLGVBQUEsR0FBa0IsSUFBSUMsNkJBQUEsQ0FBZ0IsTUFBTSxDQUFBO0FBQ2pELElBQUEsSUFBQSxDQUFLLElBQU8sR0FBQSxJQUFBO0FBQ1osSUFBQSxJQ