UNPKG

@jsprismarine/prismarine

Version:

Dedicated Minecraft Bedrock Edition server written in TypeScript

326 lines (325 loc) 41 kB
import ChatEvent from "./events/chat/ChatEvent.es.js"; import Timer from "./utils/Timer.es.js"; import TextType from "./network/type/TextType.es.js"; import ChangeDimensionPacket from "./network/packet/ChangeDimensionPacket.es.js"; import MovementType from "./network/type/MovementType.es.js"; import Human from "./entity/Human.es.js"; import { Chat, ChatType } from "./chat/Chat.es.js"; import PlayerSetGamemodeEvent from "./events/player/PlayerSetGamemodeEvent.es.js"; import PlayerToggleFlightEvent from "./events/player/PlayerToggleFlightEvent.es.js"; import PlayerToggleSprintEvent from "./events/player/PlayerToggleSprintEvent.es.js"; import PlayStatusType from "./network/type/PlayStatusType.es.js"; import CoordinateUtils from "./world/CoordinateUtils.es.js"; import PlayerSession from "./network/PlayerSession.es.js"; import { Vector3 } from "@jsprismarine/math"; import { Gametype, getGametypeId } from "@jsprismarine/minecraft"; //#region src/Player.ts /** * Default spawn view distance used in vanilla */ var VANILLA_DEFAULT_SPAWN_RADIUS = 4; var Player = class extends Human { address; networkSession; permissions; /** * Timer used for various metrics. */ timer; connected = false; xuid = ""; randomId = 0; locale = ""; skin = null; viewDistance = 0; gamemode = Gametype.WORLD_DEFAULT; onGround = false; flying = false; sneaking = false; platformChatId = ""; device = null; chunkSendQueue = /* @__PURE__ */ new Set(); /** * Player's constructor. */ constructor({ connection, world, server, uuid }) { super({ world, server, uuid }); this.timer = new Timer(); this.address = connection.getRakNetSession().getAddress(); this.networkSession = new PlayerSession(server, connection, this); this.permissions = []; this.server.on("chat", this.chatHandler.bind(this)); } /** * On enable hook. * @group Lifecycle */ async enable() { const playerData = await this.getWorld().getPlayerData(this); this.permissions = await this.server.getPermissionManager().getPermissions(this); this.gamemode = getGametypeId(playerData.gamemode || this.server.getConfig().getGamemode()); this.setPosition({ position: playerData.position ? Vector3.fromObject(playerData.position) : await this.getWorld().getSpawnPosition(), pitch: playerData.position?.pitch || 0, yaw: playerData.position?.yaw || 0, headYaw: playerData.position?.headYaw || 0, type: MovementType.Reset }); await this.sendPosition(); await this.sendSettings(); await Promise.all(this.getWorld().getPlayers().map((target) => this.getNetworkSession().broadcastMove(target, MovementType.Reset))); this.server.getLogger().debug(`(Complete player creation took ${this.timer.stop()} ms)`); this.connected = true; } /** * On disable hook. * @group Lifecycle */ async disable() { if (this.connected && this.xuid) await this.getWorld().savePlayerData(this); await this.getWorld().removeEntity(this); await this.getNetworkSession().removeFromPlayerList(); for (const onlinePlayer of this.server.getSessionManager().getAllPlayers()) await this.getNetworkSession().sendDespawn(onlinePlayer); const event = new ChatEvent(new Chat({ sender: this.server.getConsole(), message: `§e%multiplayer.player.left`, parameters: [this.getName()], needsTranslation: true, type: ChatType.TRANSLATION })); await this.server.emit("chat", event); this.connected = false; this.server.removeListener("chat", this.chatHandler); } async chatHandler(evt) { if (evt.isCancelled()) return; if (evt.getChat().getChannel() === "*.everyone" || evt.getChat().getChannel() === "*.ops" && this.isOp() || evt.getChat().getChannel() === `*.player.${this.getName()}`) await this.sendMessage(evt.getChat().getMessage(), evt.getChat().getType(), evt.getChat().getParameters(), evt.getChat().isNeedsTranslation()); } /** * Used to match vanilla behavior, will send chunks * with an initial view radius of VANILLA_DEFAULT_SPAWN_RADIUS. */ async sendInitialSpawnChunks() { const minX = CoordinateUtils.fromBlockToChunk(this.x) - 4; const minZ = CoordinateUtils.fromBlockToChunk(this.z) - 4; const maxX = CoordinateUtils.fromBlockToChunk(this.x) + 4; const maxZ = CoordinateUtils.fromBlockToChunk(this.z) + 4; const savedChunks = []; const sendQueue = []; for (let chunkX = minX; chunkX <= maxX; ++chunkX) for (let chunkZ = minZ; chunkZ <= maxZ; ++chunkZ) { savedChunks.push({ x: chunkX, z: chunkZ }); sendQueue.push(this.getWorld().getChunk(chunkX, chunkZ)); } await this.networkSession.sendNetworkChunkPublisher(4, savedChunks); const getUniqueChunks = (sendList) => { const xSet = /* @__PURE__ */ new Set(); const zSet = /* @__PURE__ */ new Set(); for (const coord of sendList) { xSet.add(coord.x); zSet.add(coord.z); } return Math.floor((xSet.size + zSet.size) / 2); }; for (let i = 0; i < getUniqueChunks(savedChunks); ++i) await this.networkSession.sendNetworkChunkPublisher(4, []); for await (const chunk of sendQueue) await this.networkSession.sendChunk(chunk); } /** * Change the player's current world. * @param {World} world - the new world */ async setWorld(world) { const dim0 = new ChangeDimensionPacket(); dim0.dimension = 0; dim0.position = this.getPosition(); dim0.respawn = false; const dim1 = new ChangeDimensionPacket(); dim1.dimension = 1; dim1.position = this.getPosition(); dim1.respawn = false; await this.sendDespawn(); await this.getWorld().removeEntity(this); await super.setWorld(world); await world.addEntity(this); await this.networkSession.send(dim0); await this.networkSession.sendPlayStatus(PlayStatusType.PlayerSpawn); await this.networkSession.send(dim1); await this.networkSession.sendPlayStatus(PlayStatusType.PlayerSpawn); await this.sendInitialSpawnChunks(); await this.networkSession.send(dim1); await this.networkSession.sendPlayStatus(PlayStatusType.PlayerSpawn); await this.networkSession.send(dim0); await this.networkSession.sendPlayStatus(PlayStatusType.PlayerSpawn); await this.networkSession.clearChunks(); await this.networkSession.needNewChunks(); await this.networkSession.sendPlayStatus(PlayStatusType.PlayerSpawn); } isOnline() { return this.connected; } async update(tick) { await super.update(tick); await this.networkSession.update(tick); if (tick % (6e3 * 1) === 0) await this.networkSession.sendTime(tick); for (const chunk of this.chunkSendQueue) { await this.networkSession.sendNetworkChunkPublisher(this.viewDistance || 4, []); await this.networkSession.sendChunk(chunk); this.chunkSendQueue.delete(chunk); } } async kick(reason = "unknown reason") { await this.disable(); await this.networkSession.kick(reason); this.server.getLogger().verbose(`Player with id §b${this.getRuntimeId()}§r was kicked: ${reason}`); } async sendSettings() { await this.getNetworkSession().sendGamemode(); await Promise.all(this.server.getSessionManager().getAllPlayers().map(async (target) => { await target.getNetworkSession().sendAbilities(this); await target.getNetworkSession().sendSettings(this); })); } /** * Player spawning logic. */ async sendSpawn() { await this.sendPosition(); await this.setGamemode(); await this.getNetworkSession().sendInventory(); await this.sendSettings(); } async sendDespawn() {} /** * Send a chat message to the client. * @param message - the message */ async sendMessage(message, type = TextType.Raw, parameters = [], needsTranslation = false) { await this.networkSession.sendMessage({ message, parameters, needsTranslation, type }); } async setGamemode(mode) { mode ??= this.gamemode; const event = new PlayerSetGamemodeEvent(this, mode); this.server.post(["playerSetGamemode", event]); if (event.isCancelled()) return; this.gamemode = event.getGamemode(); await this.networkSession.sendGamemode(); if (this.gamemode === Gametype.CREATIVE || this.gamemode === Gametype.SPECTATOR) this.metadata.setCanFly(true); else { this.metadata.setCanFly(false); await this.setFlying(false); } await this.sendSettings(); await this.networkSession.sendGamemode(); await this.networkSession.sendMetadata(); } getNetworkSession() { return this.networkSession; } getAddress() { return this.address; } getName() { return this.metadata.nameTag; } getFormattedUsername() { return `<${this.getName()}>`; } getPermissions() { return this.permissions; } /** * Get the XUID of the player. * @returns {string | null} XUID of the player or null if not available */ getXUID() { return this.xuid || null; } isPlayer() { return true; } /** * Check if the `Player` is an operator. * @returns `true` if this player is an operator otherwise `false`. */ isOp() { return this.server.getPermissionManager().isOp(this.getName()); } async setSprinting(sprinting) { if (sprinting === this.metadata.sprinting) return; const event = new PlayerToggleSprintEvent(this, sprinting); this.server.post(["playerToggleSprint", event]); if (event.isCancelled()) return; this.metadata.setSprinting(event.getIsSprinting()); await this.networkSession.sendMetadata(); } isFlying() { return this.flying; } async setFlying(flying) { if (flying === this.isFlying()) return; if (!this.metadata.canFly) { this.flying = false; await this.sendSettings(); return; } const event = new PlayerToggleFlightEvent(this, flying); this.server.post(["playerToggleFlight", event]); if (event.isCancelled()) return; this.flying = event.getIsFlying(); await this.sendSettings(); } isSneaking() { return this.sneaking; } async setSneaking(val) { if (val === this.sneaking) return; this.sneaking = val; } isOnGround() { return this.onGround; } async setOnGround(val) { if (val === this.onGround) return; this.onGround = val; } /** * Set the position. * @param {object} options - The options to set the position. * @param {Vector3} options.position - The new position. * @param {MovementType} [options.type=MovementType.Normal] - The movement type. * @param {number} [options.pitch=this.pitch] - The new pitch. * @param {number} [options.yaw=this.yaw] - The new yaw. * @param {number} [options.headYaw=this.headYaw] - The new head yaw. * @param {boolean} [broadcast=true] - Whether to broadcast the position change. * @remarks This will notify the player's client about the position change. */ async setPosition({ position, type = MovementType.Normal, pitch = this.pitch, yaw = this.yaw, headYaw = this.headYaw }, broadcast = true) { await super.setPosition({ position }); this.pitch = pitch; this.yaw = yaw; this.headYaw = headYaw; if (!broadcast) return; await this.networkSession.broadcastMove(this, type); } /** * Send the position to all the players in the same world. * @returns {Promise<void>} A promise that resolves when the position is sent. */ async sendPosition() { await super.sendPosition(); } }; //#endregion export { VANILLA_DEFAULT_SPAWN_RADIUS, Player as default }; //# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiUGxheWVyLmVzLmpzIiwibmFtZXMiOltdLCJzb3VyY2VzIjpbIi4uL3NyYy9QbGF5ZXIudHMiXSwic291cmNlc0NvbnRlbnQiOlsiaW1wb3J0IHsgVmVjdG9yMyB9IGZyb20gJ0Bqc3ByaXNtYXJpbmUvbWF0aCc7XG5pbXBvcnQgeyBHYW1ldHlwZSwgZ2V0R2FtZXR5cGVJZCB9IGZyb20gJ0Bqc3ByaXNtYXJpbmUvbWluZWNyYWZ0JztcbmltcG9ydCB0eXBlIHsgSW5ldEFkZHJlc3MgfSBmcm9tICdAanNwcmlzbWFyaW5lL3Jha25ldCc7XG5pbXBvcnQgdHlwZSBTZXJ2ZXIgZnJvbSAnLi9TZXJ2ZXInO1xuaW1wb3J0IHsgQ2hhdCwgQ2hhdFR5cGUgfSBmcm9tICcuL2NoYXQvQ2hhdCc7XG5pbXBvcnQgSHVtYW4gZnJvbSAnLi9lbnRpdHkvSHVtYW4nO1xuaW1wb3J0IENoYXRFdmVudCBmcm9tICcuL2V2ZW50cy9jaGF0L0NoYXRFdmVudCc7XG5pbXBvcnQgUGxheWVyU2V0R2FtZW1vZGVFdmVudCBmcm9tICcuL2V2ZW50cy9wbGF5ZXIvUGxheWVyU2V0R2FtZW1vZGVFdmVudCc7XG5pbXBvcnQgUGxheWVyVG9nZ2xlRmxpZ2h0RXZlbnQgZnJvbSAnLi9ldmVudHMvcGxheWVyL1BsYXllclRvZ2dsZUZsaWdodEV2ZW50JztcbmltcG9ydCBQbGF5ZXJUb2dnbGVTcHJpbnRFdmVudCBmcm9tICcuL2V2ZW50cy9wbGF5ZXIvUGxheWVyVG9nZ2xlU3ByaW50RXZlbnQnO1xuaW1wb3J0IHR5cGUgQ2xpZW50Q29ubmVjdGlvbiBmcm9tICcuL25ldHdvcmsvQ2xpZW50Q29ubmVjdGlvbic7XG5pbXBvcnQgeyBDaGFuZ2VEaW1lbnNpb25QYWNrZXQgfSBmcm9tICcuL25ldHdvcmsvUGFja2V0cyc7XG5pbXBvcnQgUGxheWVyU2Vzc2lvbiBmcm9tICcuL25ldHdvcmsvUGxheWVyU2Vzc2lvbic7XG5pbXBvcnQgdHlwZSB7IENodW5rQ29vcmQgfSBmcm9tICcuL25ldHdvcmsvcGFja2V0L05ldHdvcmtDaHVua1B1Ymxpc2hlclVwZGF0ZVBhY2tldCc7XG5pbXBvcnQgTW92ZW1lbnRUeXBlIGZyb20gJy4vbmV0d29yay90eXBlL01vdmVtZW50VHlwZSc7XG5pbXBvcnQgUGxheVN0YXR1c1R5cGUgZnJvbSAnLi9uZXR3b3JrL3R5cGUvUGxheVN0YXR1c1R5cGUnO1xuaW1wb3J0IFRleHRUeXBlIGZyb20gJy4vbmV0d29yay90eXBlL1RleHRUeXBlJztcbmltcG9ydCB0eXBlIERldmljZSBmcm9tICcuL3V0aWxzL0RldmljZSc7XG5pbXBvcnQgVGltZXIgZnJvbSAnLi91dGlscy9UaW1lcic7XG5pbXBvcnQgdHlwZSBTa2luIGZyb20gJy4vdXRpbHMvc2tpbi9Ta2luJztcbmltcG9ydCB0eXBlIHsgV29ybGQgfSBmcm9tICcuL3dvcmxkLyc7XG5pbXBvcnQgQ29vcmRpbmF0ZVV0aWxzIGZyb20gJy4vd29ybGQvQ29vcmRpbmF0ZVV0aWxzJztcbmltcG9ydCB0eXBlIENodW5rIGZyb20gJy4vd29ybGQvY2h1bmsvQ2h1bmsnO1xuXG4vKipcbiAqIERlZmF1bHQgc3Bhd24gdmlldyBkaXN0YW5jZSB1c2VkIGluIHZhbmlsbGFcbiAqL1xuZXhwb3J0IGNvbnN0IFZBTklMTEFfREVGQVVMVF9TUEFXTl9SQURJVVMgPSA0O1xuXG5leHBvcnQgZGVmYXVsdCBjbGFzcyBQbGF5ZXIgZXh0ZW5kcyBIdW1hbiB7XG4gICAgcHJpdmF0ZSByZWFkb25seSBhZGRyZXNzOiBJbmV0QWRkcmVzcztcbiAgICBwcml2YXRlIHJlYWRvbmx5IG5ldHdvcmtTZXNzaW9uOiBQbGF5ZXJTZXNzaW9uO1xuICAgIHByaXZhdGUgcGVybWlzc2lvbnM6IHN0cmluZ1tdO1xuXG4gICAgLyoqXG4gICAgICogVGltZXIgdXNlZCBmb3IgdmFyaW91cyBtZXRyaWNzLlxuICAgICAqL1xuICAgIHByaXZhdGUgdGltZXI6IFRpbWVyO1xuICAgIHByaXZhdGUgY29ubmVjdGVkID0gZmFsc2U7XG5cbiAgICBwdWJsaWMgeHVpZCA9ICcnO1xuICAgIHB1YmxpYyByYW5kb21JZCA9IDA7XG5cbiAgICBwdWJsaWMgbG9jYWxlID0gJyc7XG4gICAgcHVibGljIHNraW46IFNraW4gfCBudWxsID0gbnVsbDtcblxuICAgIHB1YmxpYyB2aWV3RGlzdGFuY2UgPSAwO1xuICAgIHB1YmxpYyBnYW1lbW9kZTogR2FtZXR5cGUgPSBHYW1ldHlwZS5XT1JMRF9ERUZBVUxUO1xuXG4gICAgcHJpdmF0ZSBvbkdyb3VuZCA9IGZhbHNlO1xuICAgIHByaXZhdGUgZmx5aW5nID0gZmFsc2U7XG4gICAgcHJpdmF0ZSBzbmVha2luZyA9IGZhbHNlO1xuXG4gICAgcHVibGljIHBsYXRmb3JtQ2hhdElkID0gJyc7IC8vIFRPRE86IHJlYWQgdGhpcyB2YWx1ZSBmcm9tIExvZ2luXG4gICAgcHVibGljIGRldmljZTogRGV2aWNlIHwgbnVsbCA9IG51bGw7XG5cbiAgICBwdWJsaWMgcmVhZG9ubHkgY2h1bmtTZW5kUXVldWUgPSBuZXcgU2V0PENodW5rPigpO1xuXG4gICAgLyoqXG4gICAgICogUGxheWVyJ3MgY29uc3RydWN0b3IuXG4gICAgICovXG4gICAgcHVibGljIGNvbnN0cnVjdG9yKHtcbiAgICAgICAgY29ubmVjdGlvbixcbiAgICAgICAgd29ybGQsXG4gICAgICAgIHNlcnZlcixcbiAgICAgICAgdXVpZFxuICAgIH06IHtcbiAgICAgICAgY29ubmVjdGlvbjogQ2xpZW50Q29ubmVjdGlvbjtcbiAgICAgICAgd29ybGQ6IFdvcmxkO1xuICAgICAgICBzZXJ2ZXI6IFNlcnZlcjtcbiAgICAgICAgdXVpZD86IHN0cmluZztcbiAgICB9KSB7XG4gICAgICAgIHN1cGVyKHtcbiAgICAgICAgICAgIHdvcmxkLFxuICAgICAgICAgICAgc2VydmVyLFxuICAgICAgICAgICAgdXVpZFxuICAgICAgICB9KTtcblxuICAgICAgICB0aGlzLnRpbWVyID0gbmV3IFRpbWVyKCk7XG5cbiAgICAgICAgdGhpcy5hZGRyZXNzID0gY29ubmVjdGlvbi5nZXRSYWtOZXRTZXNzaW9uKCkuZ2V0QWRkcmVzcygpO1xuICAgICAgICB0aGlzLm5ldHdvcmtTZXNzaW9uID0gbmV3IFBsYXllclNlc3Npb24oc2VydmVyLCBjb25uZWN0aW9uLCB0aGlzKTtcbiAgICAgICAgdGhpcy5wZXJtaXNzaW9ucyA9IFtdO1xuXG4gICAgICAgIHRoaXMuc2VydmVyLm9uKCdjaGF0JywgdGhpcy5jaGF0SGFuZGxlci5iaW5kKHRoaXMpKTtcbiAgICB9XG5cbiAgICAvKipcbiAgICAgKiBPbiBlbmFibGUgaG9vay5cbiAgICAgKiBAZ3JvdXAgTGlmZWN5Y2xlXG4gICAgICovXG4gICAgcHVibGljIGFzeW5jIGVuYWJsZSgpOiBQcm9taXNlPHZvaWQ+IHtcbiAgICAgICAgY29uc3QgcGxheWVyRGF0YSA9IGF3YWl0IHRoaXMuZ2V0V29ybGQoKS5nZXRQbGF5ZXJEYXRhKHRoaXMpO1xuXG4gICAgICAgIHRoaXMucGVybWlzc2lvbnMgPSBhd2FpdCB0aGlzLnNlcnZlci5nZXRQZXJtaXNzaW9uTWFuYWdlcigpLmdldFBlcm1pc3Npb25zKHRoaXMpO1xuICAgICAgICB0aGlzLmdhbWVtb2RlID0gZ2V0R2FtZXR5cGVJZChwbGF5ZXJEYXRhLmdhbWVtb2RlIHx8IHRoaXMuc2VydmVyLmdldENvbmZpZygpLmdldEdhbWVtb2RlKCkpO1xuXG4gICAgICAgIHRoaXMuc2V0UG9zaXRpb24oe1xuICAgICAgICAgICAgcG9zaXRpb246IHBsYXllckRhdGEucG9zaXRpb25cbiAgICAgICAgICAgICAgICA/IFZlY3RvcjMuZnJvbU9iamVjdChwbGF5ZXJEYXRhLnBvc2l0aW9uKVxuICAgICAgICAgICAgICAgIDogYXdhaXQgdGhpcy5nZXRXb3JsZCgpLmdldFNwYXduUG9zaXRpb24oKSxcbiAgICAgICAgICAgIHBpdGNoOiBwbGF5ZXJEYXRhLnBvc2l0aW9uPy5waXRjaCB8fCAwLFxuICAgICAgICAgICAgeWF3OiBwbGF5ZXJEYXRhLnBvc2l0aW9uPy55YXcgfHwgMCxcbiAgICAgICAgICAgIGhlYWRZYXc6IHBsYXllckRhdGEucG9zaXRpb24/LmhlYWRZYXcgfHwgMCxcbiAgICAgICAgICAgIHR5cGU6IE1vdmVtZW50VHlwZS5SZXNldFxuICAgICAgICB9KTtcbiAgICAgICAgYXdhaXQgdGhpcy5zZW5kUG9zaXRpb24oKTtcblxuICAgICAgICBhd2FpdCB0aGlzLnNlbmRTZXR0aW5ncygpO1xuXG4gICAgICAgIC8vIFVwZGF0ZSBwb3NpdGlvbiBvZiBhbGwgdGhlIHBsYXllcnMgaW4gdGhlIHNhbWUgd29ybGQuXG4gICAgICAgIGF3YWl0IFByb21pc2UuYWxsKFxuICAgICAgICAgICAgdGhpcy5nZXRXb3JsZCgpXG4gICAgICAgICAgICAgICAgLmdldFBsYXllcnMoKVxuICAgICAgICAgICAgICAgIC5tYXAoKHRhcmdldCkgPT4gdGhpcy5nZXROZXR3b3JrU2Vzc2lvbigpLmJyb2FkY2FzdE1vdmUodGFyZ2V0LCBNb3ZlbWVudFR5cGUuUmVzZXQpKVxuICAgICAgICApO1xuXG4gICAgICAgIC8vIEZpbmFsbHkgbWFyayB0aGUgcGxheWVyIGFzIGNvbm5lY3RlZC5cbiAgICAgICAgdGhpcy5zZXJ2ZXIuZ2V0TG9nZ2VyKCkuZGVidWcoYChDb21wbGV0ZSBwbGF5ZXIgY3JlYXRpb24gdG9vayAke3RoaXMudGltZXIuc3RvcCgpfSBtcylgKTtcbiAgICAgICAgdGhpcy5jb25uZWN0ZWQgPSB0cnVlO1xuICAgIH1cblxuICAgIC8qKlxuICAgICAqIE9uIGRpc2FibGUgaG9vay5cbiAgICAgKiBAZ3JvdXAgTGlmZWN5Y2xlXG4gICAgICovXG4gICAgcHVibGljIGFzeW5jIGRpc2FibGUoKTogUHJvbWlzZTx2b2lkPiB7XG4gICAgICAgIGlmICh0aGlzLmNvbm5lY3RlZCAmJiB0aGlzLnh1aWQpIGF3YWl0IHRoaXMuZ2V0V29ybGQoKS5zYXZlUGxheWVyRGF0YSh0aGlzKTtcbiAgICAgICAgYXdhaXQgdGhpcy5nZXRXb3JsZCgpLnJlbW92ZUVudGl0eSh0aGlzKTtcblxuICAgICAgICAvLyBEZS1zcGF3biB0aGUgcGxheWVyIHRvIGFsbCBvbmxpbmUgcGxheWVyc1xuICAgICAgICBhd2FpdCB0aGlzLmdldE5ldHdvcmtTZXNzaW9uKCkucmVtb3ZlRnJvbVBsYXllckxpc3QoKTtcbiAgICAgICAgZm9yIChjb25zdCBvbmxpbmVQbGF5ZXIgb2YgdGhpcy5zZXJ2ZXIuZ2V0U2Vzc2lvbk1hbmFnZXIoKS5nZXRBbGxQbGF5ZXJzKCkpIHtcbiAgICAgICAgICAgIGF3YWl0IHRoaXMuZ2V0TmV0d29ya1Nlc3Npb24oKS5zZW5kRGVzcGF3bihvbmxpbmVQbGF5ZXIpO1xuICAgICAgICB9XG5cbiAgICAgICAgLy8gQW5ub3VuY2UgZGlzY29ubmVjdGlvblxuICAgICAgICBjb25zdCBldmVudCA9IG5ldyBDaGF0RXZlbnQoXG4gICAgICAgICAgICBuZXcgQ2hhdCh7XG4gICAgICAgICAgICAgICAgc2VuZGVyOiB0aGlzLnNlcnZlci5nZXRDb25zb2xlKCkhLFxuICAgICAgICAgICAgICAgIG1lc3NhZ2U6IGDCp2UlbXVsdGlwbGF5ZXIucGxheWVyLmxlZnRgLFxuICAgICAgICAgICAgICAgIHBhcmFtZXRlcnM6IFt0aGlzLmdldE5hbWUoKV0sXG4gICAgICAgICAgICAgICAgbmVlZHNUcmFuc2xhdGlvbjogdHJ1ZSxcbiAgICAgICAgICAgICAgICB0eXBlOiBDaGF0VHlwZS5UUkFOU0xBVElPTlxuICAgICAgICAgICAgfSlcbiAgICAgICAgKTtcbiAgICAgICAgYXdhaXQgdGhpcy5zZXJ2ZXIuZW1pdCgnY2hhdCcsIGV2ZW50KTtcblxuICAgICAgICB0aGlzLmNvbm5lY3RlZCA9IGZhbHNlO1xuICAgICAgICB0aGlzLnNlcnZlci5yZW1vdmVMaXN0ZW5lcignY2hhdCcsIHRoaXMuY2hhdEhhbmRsZXIpO1xuICAgIH1cblxuICAgIHByaXZhdGUgYXN5bmMgY2hhdEhhbmRsZXIoZXZ0OiBDaGF0RXZlbnQpIHtcbiAgICAgICAgaWYgKGV2dC5pc0NhbmNlbGxlZCgpKSByZXR1cm47XG5cbiAgICAgICAgLy8gVE9ETzogcHJvcGVyIGNoYW5uZWwgc3lzdGVtXG4gICAgICAgIGlmIChcbiAgICAgICAgICAgIGV2dC5nZXRDaGF0KCkuZ2V0Q2hhbm5lbCgpID09PSAnKi5ldmVyeW9uZScgfHxcbiAgICAgICAgICAgIChldnQuZ2V0Q2hhdCgpLmdldENoYW5uZWwoKSA9PT0gJyoub3BzJyAmJiB0aGlzLmlzT3AoKSkgfHxcbiAgICAgICAgICAgIGV2dC5nZXRDaGF0KCkuZ2V0Q2hhbm5lbCgpID09PSBgKi5wbGF5ZXIuJHt0aGlzLmdldE5hbWUoKX1gXG4gICAgICAgIClcbiAgICAgICAgICAgIGF3YWl0IHRoaXMuc2VuZE1lc3NhZ2UoXG4gICAgICAgICAgICAgICAgZXZ0LmdldENoYXQoKS5nZXRNZXNzYWdlKCksXG4gICAgICAgICAgICAgICAgZXZ0LmdldENoYXQoKS5nZXRUeXBlKCkgYXMgbnVtYmVyIGFzIFRleHRUeXBlLFxuICAgICAgICAgICAgICAgIGV2dC5nZXRDaGF0KCkuZ2V0UGFyYW1ldGVycygpLFxuICAgICAgICAgICAgICAgIGV2dC5nZXRDaGF0KCkuaXNOZWVkc1RyYW5zbGF0aW9uKClcbiAgICAgICAgICAgICk7XG4gICAgfVxuXG4gICAgLyoqXG4gICAgICogVXNlZCB0byBtYXRjaCB2YW5pbGxhIGJlaGF2aW9yLCB3aWxsIHNlbmQgY2h1bmtzXG4gICAgICogd2l0aCBhbiBpbml0aWFsIHZpZXcgcmFkaXVzIG9mIFZBTklMTEFfREVGQVVMVF9TUEFXTl9SQURJVVMuXG4gICAgICovXG4gICAgcHVibGljIGFzeW5jIHNlbmRJbml0aWFsU3Bhd25DaHVua3MoKTogUHJvbWlzZTx2b2lkPiB7XG4gICAgICAgIGNvbnN0IG1pblggPSBDb29yZGluYXRlVXRpbHMuZnJvbUJsb2NrVG9DaHVuayh0aGlzLngpIC0gVkFOSUxMQV9ERUZBVUxUX1NQQVdOX1JBRElVUztcbiAgICAgICAgY29uc3QgbWluWiA9IENvb3JkaW5hdGVVdGlscy5mcm9tQmxvY2tUb0NodW5rKHRoaXMueikgLSBWQU5JTExBX0RFRkFVTFRfU1BBV05fUkFESVVTO1xuICAgICAgICBjb25zdCBtYXhYID0gQ29vcmRpbmF0ZVV0aWxzLmZyb21CbG9ja1RvQ2h1bmsodGhpcy54KSArIFZBTklMTEFfREVGQVVMVF9TUEFXTl9SQURJVVM7XG4gICAgICAgIGNvbnN0IG1heFogPSBDb29yZGluYXRlVXRpbHMuZnJvbUJsb2NrVG9DaHVuayh0aGlzLnopICsgVkFOSUxMQV9ERUZBVUxUX1NQQVdOX1JBRElVUztcblxuICAgICAgICBjb25zdCBzYXZlZENodW5rczogQ2h1bmtDb29yZFtdID0gW107XG4gICAgICAgIGNvbnN0IHNlbmRRdWV1ZTogQXJyYXk8UHJvbWlzZTxDaHVuaz4+ID0gW107XG4gICAgICAgIGZvciAobGV0IGNodW5rWCA9IG1pblg7IGNodW5rWCA8PSBtYXhYOyArK2NodW5rWCkge1xuICAgICAgICAgICAgZm9yIChsZXQgY2h1bmtaID0gbWluWjsgY2h1bmtaIDw9IG1heFo7ICsrY2h1bmtaKSB7XG4gICAgICAgICAgICAgICAgLy8gVE9ETzogdmFuaWxsYSBkb2VzIG5vdCBzZW5kIGFsbCBvZiB0aGVtLCBidXQgaW4gYSByYW5nZVxuICAgICAgICAgICAgICAgIC8vIGZvciBleGFtcGxlIGl0IGRvZXMgc2VuZCB0aGVtIGZyb20geCA9PiBbLTM7IDNdIGFuZCB6ID0+IFstMzsgMl1cbiAgICAgICAgICAgICAgICBzYXZlZENodW5rcy5wdXNoKHsgeDogY2h1bmtYLCB6OiBjaHVua1ogfSk7XG4gICAgICAgICAgICAgICAgc2VuZFF1ZXVlLnB1c2godGhpcy5nZXRXb3JsZCgpLmdldENodW5rKGNodW5rWCwgY2h1bmtaKSk7XG4gICAgICAgICAgICB9XG4gICAgICAgIH1cblxuICAgICAgICBhd2FpdCB0aGlzLm5ldHdvcmtTZXNzaW9uLnNlbmROZXR3b3JrQ2h1bmtQdWJsaXNoZXIoVkFOSUxMQV9ERUZBVUxUX1NQQVdOX1JBRElVUywgc2F2ZWRDaHVua3MpO1xuXG4gICAgICAgIGNvbnN0IGdldFVuaXF1ZUNodW5rcyA9IChzZW5kTGlzdDogQ2h1bmtDb29yZFtdKSA9PiB7XG4gICAgICAgICAgICBjb25zdCB4U2V0ID0gbmV3IFNldDxudW1iZXI+KCk7XG4gICAgICAgICAgICBjb25zdCB6U2V0ID0gbmV3IFNldDxudW1iZXI+KCk7XG5cbiAgICAgICAgICAgIGZvciAoY29uc3QgY29vcmQgb2Ygc2VuZExpc3QpIHtcbiAgICAgICAgICAgICAgICB4U2V0LmFkZChjb29yZC54KTtcbiAgICAgICAgICAgICAgICB6U2V0LmFkZChjb29yZC56KTtcbiAgICAgICAgICAgIH1cblxuICAgICAgICAgICAgcmV0dXJuIE1hdGguZmxvb3IoKHhTZXQuc2l6ZSArIHpTZXQuc2l6ZSkgLyAyKTtcbiAgICAgICAgfTtcblxuICAgICAgICBmb3IgKGxldCBpID0gMDsgaSA8IGdldFVuaXF1ZUNodW5rcyhzYXZlZENodW5rcyk7ICsraSkge1xuICAgICAgICAgICAgYXdhaXQgdGhpcy5uZXR3b3JrU2Vzc2lvbi5zZW5kTmV0d29ya0NodW5rUHVibGlzaGVyKFZBTklMTEFfREVGQVVMVF9TUEFXTl9SQURJVVMsIFtdKTtcbiAgICAgICAgfVxuXG4gICAgICAgIGZvciBhd2FpdCAoY29uc3QgY2h1bmsgb2Ygc2VuZFF1ZXVlKSB7XG4gICAgICAgICAgICBhd2FpdCB0aGlzLm5ldHdvcmtTZXNzaW9uLnNlbmRDaHVuayhjaHVuayk7XG4gICAgICAgIH1cbiAgICB9XG5cbiAgICAvKipcbiAgICAgKiBDaGFuZ2UgdGhlIHBsYXllcidzIGN1cnJlbnQgd29ybGQuXG4gICAgICogQHBhcmFtIHtXb3JsZH0gd29ybGQgLSB0aGUgbmV3IHdvcmxkXG4gICAgICovXG4gICAgcHVibGljIGFzeW5jIHNldFdvcmxkKHdvcmxkOiBXb3JsZCkge1xuICAgICAgICBjb25zdCBkaW0wID0gbmV3IENoYW5nZURpbWVuc2lvblBhY2tldCgpO1xuICAgICAgICBkaW0wLmRpbWVuc2lvbiA9IDA7XG4gICAgICAgIGRpbTAucG9zaXRpb24gPSB0aGlzLmdldFBvc2l0aW9uKCk7XG4gICAgICAgIGRpbTAucmVzcGF3biA9IGZhbHNlO1xuXG4gICAgICAgIGNvbnN0IGRpbTEgPSBuZXcgQ2hhbmdlRGltZW5zaW9uUGFja2V0KCk7XG4gICAgICAgIGRpbTEuZGltZW5zaW9uID0gMTtcbiAgICAgICAgZGltMS5wb3NpdGlvbiA9IHRoaXMuZ2V0UG9zaXRpb24oKTtcbiAgICAgICAgZGltMS5yZXNwYXduID0gZmFsc2U7XG5cbiAgICAgICAgYXdhaXQgdGhpcy5zZW5kRGVzcGF3bigpO1xuICAgICAgICBhd2FpdCB0aGlzLmdldFdvcmxkKCkucmVtb3ZlRW50aXR5KHRoaXMpO1xuXG4gICAgICAgIGF3YWl0IHN1cGVyLnNldFdvcmxkKHdvcmxkKTtcbiAgICAgICAgYXdhaXQgd29ybGQuYWRkRW50aXR5KHRoaXMpO1xuXG4gICAgICAgIGF3YWl0IHRoaXMubmV0d29ya1Nlc3Npb24uc2VuZChkaW0wKTtcbiAgICAgICAgYXdhaXQgdGhpcy5uZXR3b3JrU2Vzc2lvbi5zZW5kUGxheVN0YXR1cyhQbGF5U3RhdHVzVHlwZS5QbGF5ZXJTcGF3bik7XG4gICAgICAgIGF3YWl0IHRoaXMubmV0d29ya1Nlc3Npb24uc2VuZChkaW0xKTtcbiAgICAgICAgYXdhaXQgdGhpcy5uZXR3b3JrU2Vzc2lvbi5zZW5kUGxheVN0YXR1cyhQbGF5U3RhdHVzVHlwZS5QbGF5ZXJTcGF3bik7XG5cbiAgICAgICAgYXdhaXQgdGhpcy5zZW5kSW5pdGlhbFNwYXduQ2h1bmtzKCk7XG5cbiAgICAgICAgYXdhaXQgdGhpcy5uZXR3b3JrU2Vzc2lvbi5zZW5kKGRpbTEpO1xuICAgICAgICBhd2FpdCB0aGlzLm5ldHdvcmtTZXNzaW9uLnNlbmRQbGF5U3RhdHVzKFBsYXlTdGF0dXNUeXBlLlBsYXllclNwYXduKTtcbiAgICAgICAgYXdhaXQgdGhpcy5uZXR3b3JrU2Vzc2lvbi5zZW5kKGRpbTApO1xuICAgICAgICBhd2FpdCB0aGlzLm5ldHdvcmtTZXNzaW9uLnNlbmRQbGF5U3RhdHVzKFBsYXlTdGF0dXNUeXBlLlBsYXllclNwYXduKTtcblxuICAgICAgICBhd2FpdCB0aGlzLm5ldHdvcmtTZXNzaW9uLmNsZWFyQ2h1bmtzKCk7XG4gICAgICAgIGF3YWl0IHRoaXMubmV0d29ya1Nlc3Npb24ubmVlZE5ld0NodW5rcygpO1xuICAgICAgICBhd2FpdCB0aGlzLm5ldHdvcmtTZXNzaW9uLnNlbmRQbGF5U3RhdHVzKFBsYXlTdGF0dXNUeXBlLlBsYXllclNwYXduKTtcbiAgICB9XG5cbiAgICBwdWJsaWMgaXNPbmxpbmUoKSB7XG4gICAgICAgIHJldHVybiB0aGlzLmNvbm5lY3RlZDtcbiAgICB9XG5cbiAgICBwdWJsaWMgYXN5bmMgdXBkYXRlKHRpY2s6IG51bWJlcik6IFByb21pc2U8dm9pZD4ge1xuICAgICAgICBhd2FpdCBzdXBlci51cGRhdGUodGljayk7XG4gICAgICAgIGF3YWl0IHRoaXMubmV0d29ya1Nlc3Npb24udXBkYXRlKHRpY2spO1xuXG4gICAgICAgIC8vIFRPRE86IGdldCBkb2N1bWVudGF0aW9uIGFib3V0IHRpbWluZ3MgZnJvbSB2YW5pbGxhXG4gICAgICAgIC8vIDEgc2Vjb25kIC8gMjAgPSAxIHRpY2ssIDIwICogNSA9IDEgc2Vjb25kXG4gICAgICAgIC8vIDEgc2Vjb25kICogNjAgPSAxIG1pbnV0ZVxuICAgICAgICBpZiAodGljayAlICgyMCAqIDUgKiA2MCAqIDEpID09PSAwKSB7XG4gICAgICAgICAgICBhd2FpdCB0aGlzLm5ldHdvcmtTZXNzaW9uLnNlbmRUaW1lKHRpY2spO1xuICAgICAgICB9XG5cbiAgICAgICAgZm9yIChjb25zdCBjaHVuayBvZiB0aGlzLmNodW5rU2VuZFF1ZXVlKSB7XG4gICAgICAgICAgICBhd2FpdCB0aGlzLm5ldHdvcmtTZXNzaW9uLnNlbmROZXR3b3JrQ2h1bmtQdWJsaXNoZXIodGhpcy52aWV3RGlzdGFuY2UgfHwgVkFOSUxMQV9ERUZBVUxUX1NQQVdOX1JBRElVUywgW10pO1xuICAgICAgICAgICAgYXdhaXQgdGhpcy5uZXR3b3JrU2Vzc2lvbi5zZW5kQ2h1bmsoY2h1bmspO1xuICAgICAgICAgICAgdGhpcy5jaHVua1NlbmRRdWV1ZS5kZWxldGUoY2h1bmspO1xuICAgICAgICB9XG4gICAgfVxuXG4gICAgcHVibGljIGFzeW5jIGtpY2socmVhc29uID0gJ3Vua25vd24gcmVhc29uJyk6IFByb21pc2U8dm9pZD4ge1xuICAgICAgICBhd2FpdCB0aGlzLmRpc2FibGUoKTtcbiAgICAgICAgYXdhaXQgdGhpcy5uZXR3b3JrU2Vzc2lvbi5raWNrKHJlYXNvbik7XG4gICAgICAgIHRoaXMuc2VydmVyLmdldExvZ2dlcigpLnZlcmJvc2UoYFBsYXllciB3aXRoIGlkIMKnYiR7dGhpcy5nZXRSdW50aW1lSWQoKX3Cp3Igd2FzIGtpY2tlZDogJHtyZWFzb259YCk7XG4gICAgfVxuXG4gICAgcHVibGljIGFzeW5jIHNlbmRTZXR0aW5ncygpOiBQcm9taXNlPHZvaWQ+IHtcbiAgICAgICAgYXdhaXQgdGhpcy5nZXROZXR3b3JrU2Vzc2lvbigpLnNlbmRHYW1lbW9kZSgpO1xuXG4gICAgICAgIGF3YWl0IFByb21pc2UuYWxsKFxuICAgICAgICAgICAgdGhpcy5zZXJ2ZXJcbiAgICAgICAgICAgICAgICAuZ2V0U2Vzc2lvbk1hbmFnZXIoKVxuICAgICAgICAgICAgICAgIC5nZXRBbGxQbGF5ZXJzKClcbiAgICAgICAgICAgICAgICAubWFwKGFzeW5jICh0YXJnZXQpID0+IHtcbiAgICAgICAgICAgICAgICAgICAgYXdhaXQgdGFyZ2V0LmdldE5ldHdvcmtTZXNzaW9uKCkuc2VuZEFiaWxpdGllcyh0aGlzKTtcbiAgICAgICAgICAgICAgICAgICAgYXdhaXQgdGFyZ2V0LmdldE5ldHdvcmtTZXNzaW9uKCkuc2VuZFNldHRpbmdzKHRoaXMpO1xuICAgICAgICAgICAgICAgIH0pXG4gICAgICAgICk7XG4gICAgfVxuXG4gICAgLyoqXG4gICAgICogUGxheWVyIHNwYXduaW5nIGxvZ2ljLlxuICAgICAqL1xuICAgIHB1YmxpYyBhc3luYyBzZW5kU3Bhd24oKSB7XG4gICAgICAgIGF3YWl0IHRoaXMuc2VuZFBvc2l0aW9uKCk7XG4gICAgICAgIGF3YWl0IHRoaXMuc2V0R2FtZW1vZGUoKTtcbiAgICAgICAgYXdhaXQgdGhpcy5nZXROZXR3b3JrU2Vzc2lvbigpLnNlbmRJbnZlbnRvcnkoKTtcbiAgICAgICAgYXdhaXQgdGhpcy5zZW5kU2V0dGluZ3MoKTtcbiAgICB9XG4gICAgcHVibGljIGFzeW5jIHNlbmREZXNwYXduKCkge31cblxuICAgIC8qKlxuICAgICAqIFNlbmQgYSBjaGF0IG1lc3NhZ2UgdG8gdGhlIGNsaWVudC5cbiAgICAgKiBAcGFyYW0gbWVzc2FnZSAtIHRoZSBtZXNzYWdlXG4gICAgICovXG4gICAgcHVibGljIGFzeW5jIHNlbmRNZXNzYWdlKFxuICAgICAgICBtZXNzYWdlOiBzdHJpbmcsXG4gICAgICAgIHR5cGU6IFRleHRUeXBlID0gVGV4dFR5cGUuUmF3LFxuICAgICAgICBwYXJhbWV0ZXJzOiBzdHJpbmdbXSA9IFtdLFxuICAgICAgICBuZWVkc1RyYW5zbGF0aW9uID0gZmFsc2VcbiAgICApOiBQcm9taXNlPHZvaWQ+IHtcbiAgICAgICAgLy8gVE9ETzogRG8gdGhpcyBwcm9wZXJseSBsaWtlIGphdmEgZWRpdGlvbixcbiAgICAgICAgLy8gaW4gb3RoZXIgd29yZHMsIHRoZSBtZXNzYWdlIHNob3VsZCBiZSBKU09OIGZvcm1hdHRlZC5cbiAgICAgICAgYXdhaXQgdGhpcy5uZXR3b3JrU2Vzc2lvbi5zZW5kTWVzc2FnZSh7XG4gICAgICAgICAgICBtZXNzYWdlLFxuICAgICAgICAgICAgcGFyYW1ldGVycyxcbiAgICAgICAgICAgIG5lZWRzVHJhbnNsYXRpb24sXG4gICAgICAgICAgICB0eXBlXG4gICAgICAgIH0pO1xuICAgIH1cblxuICAgIHB1YmxpYyBhc3luYyBzZXRHYW1lbW9kZShtb2RlPzogR2FtZXR5cGUpOiBQcm9taXNlPHZvaWQ+IHtcbiAgICAgICAgbW9kZSA/Pz0gdGhpcy5nYW1lbW9kZTtcblxuICAgICAgICBjb25zdCBldmVudCA9IG5ldyBQbGF5ZXJTZXRHYW1lbW9kZUV2ZW50KHRoaXMsIG1vZGUpO1xuICAgICAgICB0aGlzLnNlcnZlci5wb3N0KFsncGxheWVyU2V0R2FtZW1vZGUnLCBldmVudF0pO1xuICAgICAgICBpZiAoZXZlbnQuaXNDYW5jZWxsZWQoKSkgcmV0dXJuO1xuXG4gICAgICAgIHRoaXMuZ2FtZW1vZGUgPSBldmVudC5nZXRHYW1lbW9kZSgpO1xuICAgICAgICBhd2FpdCB0aGlzLm5ldHdvcmtTZXNzaW9uLnNlbmRHYW1lbW9kZSgpO1xuXG4gICAgICAgIGlmICh0aGlzLmdhbWVtb2RlID09PSBHYW1ldHlwZS5DUkVBVElWRSB8fCB0aGlzLmdhbWVtb2RlID09PSBHYW1ldHlwZS5TUEVDVEFUT1IpIHtcbiAgICAgICAgICAgIHRoaXMubWV0YWRhdGEuc2V0Q2FuRmx5KHRydWUpO1xuICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgdGhpcy5tZXRhZGF0YS5zZXRDYW5GbHkoZmFsc2UpO1xuICAgICAgICAgICAgYXdhaXQgdGhpcy5zZXRGbHlpbmcoZmFsc2UpO1xuICAgICAgICB9XG5cbiAgICAgICAgYXdhaXQgdGhpcy5zZW5kU2V0dGluZ3MoKTtcbiAgICAgICAgYXdhaXQgdGhpcy5uZXR3b3JrU2Vzc2lvbi5zZW5kR2FtZW1vZGUoKTtcbiAgICAgICAgYXdhaXQgdGhpcy5uZXR3b3JrU2Vzc2lvbi5zZW5kTWV0YWRhdGEoKTtcbiAgICB9XG5cbiAgICBwdWJsaWMgZ2V0TmV0d29ya1Nlc3Npb24oKTogUGxheWVyU2Vzc2lvbiB7XG4gICAgICAgIHJldHVybiB0aGlzLm5ldHdvcmtTZXNzaW9uO1xuICAgIH1cblxuICAgIHB1YmxpYyBnZXRBZGRyZXNzKCkge1xuICAgICAgICByZXR1cm4gdGhpcy5hZGRyZXNzO1xuICAgIH1cblxuICAgIHB1YmxpYyBnZXROYW1lKCk6IHN0cmluZyB7XG4gICAgICAgIHJldHVybiB0aGlzLm1ldGFkYXRhLm5hbWVUYWc7XG4gICAgfVxuXG4gICAgcHVibGljIGdldEZvcm1hdHRlZFVzZXJuYW1lKCk6IHN0cmluZyB7XG4gICAgICAgIHJldHVybiBgPCR7dGhpcy5nZXROYW1lKCl9PmA7XG4gICAgfVxuXG4gICAgcHVibGljIGdldFBlcm1pc3Npb25zKCk6IHN0cmluZ1tdIHtcbiAgICAgICAgcmV0dXJuIHRoaXMucGVybWlzc2lvbnM7XG4gICAgfVxuXG4gICAgLyoqXG4gICAgICogR2V0IHRoZSBYVUlEIG9mIHRoZSBwbGF5ZXIuXG4gICAgICogQHJldHVybnMge3N0cmluZyB8IG51bGx9IFhVSUQgb2YgdGhlIHBsYXllciBvciBudWxsIGlmIG5vdCBhdmFpbGFibGVcbiAgICAgKi9cbiAgICBwdWJsaWMgZ2V0WFVJRCgpOiBzdHJpbmcgfCBudWxsIHtcbiAgICAgICAgcmV0dXJuIHRoaXMueHVpZCB8fCBudWxsO1xuICAgIH1cblxuICAgIHB1YmxpYyBpc1BsYXllcigpOiBib29sZWFuIHtcbiAgICAgICAgcmV0dXJuIHRydWU7XG4gICAgfVxuXG4gICAgLyoqXG4gICAgICogQ2hlY2sgaWYgdGhlIGBQbGF5ZXJgIGlzIGFuIG9wZXJhdG9yLlxuICAgICAqIEByZXR1cm5zIGB0cnVlYCBpZiB0aGlzIHBsYXllciBpcyBhbiBvcGVyYXRvciBvdGhlcndpc2UgYGZhbHNlYC5cbiAgICAgKi9cbiAgICBwdWJsaWMgaXNPcCgpOiBib29sZWFuIHtcbiAgICAgICAgcmV0dXJuIHRoaXMuc2VydmVyLmdldFBlcm1pc3Npb25NYW5hZ2VyKCkuaXNPcCh0aGlzLmdldE5hbWUoKSk7XG4gICAgfVxuXG4gICAgcHVibGljIGFzeW5jIHNldFNwcmludGluZyhzcHJpbnRpbmc6IGJvb2xlYW4pIHtcbiAgICAgICAgaWYgKHNwcmludGluZyA9PT0gdGhpcy5tZXRhZGF0YS5zcHJpbnRpbmcpIHJldHVybjtcblxuICAgICAgICBjb25zdCBldmVudCA9IG5ldyBQbGF5ZXJUb2dnbGVTcHJpbnRFdmVudCh0aGlzLCBzcHJpbnRpbmcpO1xuICAgICAgICB0aGlzLnNlcnZlci5wb3N0KFsncGxheWVyVG9nZ2xlU3ByaW50JywgZXZlbnRdKTtcbiAgICAgICAgaWYgKGV2ZW50LmlzQ2FuY2VsbGVkKCkpIHJldHVybjtcblxuICAgICAgICB0aGlzLm1ldGFkYXRhLnNldFNwcmludGluZyhldmVudC5nZXRJc1NwcmludGluZygpKTtcbiAgICAgICAgYXdhaXQgdGhpcy5uZXR3b3JrU2Vzc2lvbi5zZW5kTWV0YWRhdGEoKTtcbiAgICB9XG5cbiAgICBwdWJsaWMgaXNGbHlpbmcoKSB7XG4gICAgICAgIHJldHVybiB0aGlzLmZseWluZztcbiAgICB9XG4gICAgcHVibGljIGFzeW5jIHNldEZseWluZyhmbHlpbmc6IGJvb2xlYW4pIHtcbiAgICAgICAgaWYgKGZseWluZyA9PT0gdGhpcy5pc0ZseWluZygpKSByZXR1cm47XG5cbiAgICAgICAgaWYgKCF0aGlzLm1ldGFkYXRhLmNhbkZseSkge1xuICAgICAgICAgICAgdGhpcy5mbHlpbmcgPSBmYWxzZTtcbiAgICAgICAgICAgIGF3YWl0IHRoaXMuc2VuZFNldHRpbmdzKCk7XG4gICAgICAgICAgICByZXR1cm47XG4gICAgICAgIH1cblxuICAgICAgICBjb25zdCBldmVudCA9IG5ldyBQbGF5ZXJUb2dnbGVGbGlnaHRFdmVudCh0aGlzLCBmbHlpbmcpO1xuICAgICAgICB0aGlzLnNlcnZlci5wb3N0KFsncGxheWVyVG9nZ2xlRmxpZ2h0JywgZXZlbnRdKTtcbiAgICAgICAgaWYgKGV2ZW50LmlzQ2FuY2VsbGVkKCkpIHJldHVybjtcblxuICAgICAgICB0aGlzLmZseWluZyA9IGV2ZW50LmdldElzRmx5aW5nKCk7XG4gICAgICAgIGF3YWl0IHRoaXMuc2VuZFNldHRpbmdzKCk7XG4gICAgfVxuXG4gICAgcHVibGljIGlzU25lYWtpbmcoKSB7XG4gICAgICAgIHJldHVybiB0aGlzLnNuZWFraW5nO1xuICAgIH1cbiAgICBwdWJsaWMgYXN5bmMgc2V0U25lYWtpbmcodmFsOiBib29sZWFuKSB7XG4gICAgICAgIGlmICh2YWwgPT09IHRoaXMuc25lYWtpbmcpIHJldHVybjtcbiAgICAgICAgdGhpcy5zbmVha2luZyA9IHZhbDtcbiAgICB9XG5cbiAgICBwdWJsaWMgaXNPbkdyb3VuZCgpIHtcbiAgICAgICAgcmV0dXJuIHRoaXMub25Hcm91bmQ7XG4gICAgfVxuICAgIHB1YmxpYyBhc3luYyBzZXRPbkdyb3VuZCh2YWw6IGJvb2xlYW4pIHtcbiAgICAgICAgaWYgKHZhbCA9PT0gdGhpcy5vbkdyb3VuZCkgcmV0dXJuO1xuICAgICAgICB0aGlzLm9uR3JvdW5kID0gdmFsO1xuICAgIH1cblxuICAgIC8qKlxuICAgICAqIFNldCB0aGUgcG9zaXRpb24uXG4gICAgICogQHBhcmFtIHtvYmplY3R9IG9wdGlvbnMgLSBUaGUgb3B0aW9ucyB0byBzZXQgdGhlIHBvc2l0aW9uLlxuICAgICAqIEBwYXJhbSB7VmVjdG9yM30gb3B0aW9ucy5wb3NpdGlvbiAtIFRoZSBuZXcgcG9zaXRpb24uXG4gICAgICogQHBhcmFtIHtNb3ZlbWVudFR5cGV9IFtvcHRpb25zLnR5cGU9TW92ZW1lbnRUeXBlLk5vcm1hbF0gLSBUaGUgbW92ZW1lbnQgdHlwZS5cbiAgICAgKiBAcGFyYW0ge251bWJlcn0gW29wdGlvbnMucGl0Y2g9dGhpcy5waXRjaF0gLSBUaGUgbmV3IHBpdGNoLlxuICAgICAqIEBwYXJhbSB7bnVtYmVyfSBbb3B0aW9ucy55YXc9dGhpcy55YXddIC0gVGhlIG5ldyB5YXcuXG4gICAgICogQHBhcmFtIHtudW1iZXJ9IFtvcHRpb25zLmhlYWRZYXc9dGhpcy5oZWFkWWF3XSAtIFRoZSBuZXcgaGVhZCB5YXcuXG4gICAgICogQHBhcmFtIHtib29sZWFufSBbYnJvYWRjYXN0PXRydWVdIC0gV2hldGhlciB0byBicm9hZGNhc3QgdGhlIHBvc2l0aW9uIGNoYW5nZS5cbiAgICAgKiBAcmVtYXJrcyBUaGlzIHdpbGwgbm90aWZ5IHRoZSBwbGF5ZXIncyBjbGllbnQgYWJvdXQgdGhlIHBvc2l0aW9uIGNoYW5nZS5cbiAgICAgKi9cbiAgICBwdWJsaWMgYXN5bmMgc2V0UG9zaXRpb24oXG4gICAgICAgIHtcbiAgICAgICAgICAgIHBvc2l0aW9uLFxuICAgICAgICAgICAgdHlwZSA9IE1vdmVtZW50VHlwZS5Ob3JtYWwsXG4gICAgICAgICAgICBwaXRjaCA9IHRoaXMucGl0Y2gsXG4gICAgICAgICAgICB5YXcgPSB0aGlzLnlhdyxcbiAgICAgICAgICAgIGhlYWRZYXcgPSB0aGlzLmhlYWRZYXdcbiAgICAgICAgfToge1xuICAgICAgICAgICAgcG9zaXRpb246IFZlY3RvcjM7XG4gICAgICAgICAgICB0eXBlPzogTW92ZW1lbnRUeXBlO1xuICAgICAgICAgICAgcGl0Y2g/OiBudW1iZXI7XG4gICAgICAgICAgICB5YXc/OiBudW1iZXI7XG4gICAgICAgICAgICBoZWFkWWF3PzogbnVtYmVyO1xuICAgICAgICB9LFxuICAgICAgICBicm9hZGNhc3QgPSB0cnVlXG4gICAgKSB7XG4gICAgICAgIGF3YWl0IHN1cGVyLnNldFBvc2l0aW9uKHsgcG9zaXRpb24gfSk7XG4gICAgICAgIHRoaXMucGl0Y2ggPSBwaXRjaDtcbiAgICAgICAgdGhpcy55YXcgPSB5YXc7XG4gICAgICAgIHRoaXMuaGVhZFlhdyA9IGhlYWRZYXc7XG5cbiAgICAgICAgaWYgKCFicm9hZGNhc3QpIHJldHVybjtcbiAgICAgICAgYXdhaXQgdGhpcy5uZXR3b3JrU2Vzc2lvbi5icm9hZGNhc3RNb3ZlKHRoaXMsIHR5cGUpO1xuICAgIH1cbiAgICAvKipcbiAgICAgKiBTZW5kIHRoZSBwb3NpdGlvbiB0byBhbGwgdGhlIHBsYXllcnMgaW4gdGhlIHNhbWUgd29ybGQuXG4gICAgICogQHJldHVybnMge1Byb21pc2U8dm9pZD59IEEgcHJvbWlzZSB0aGF0IHJlc29sdmVzIHdoZW4gdGhlIHBvc2l0aW9uIGlzIHNlbnQuXG4gICAgICovXG4gICAgcHVibGljIGFzeW5jIHNlbmRQb3NpdGlvbigpOiBQcm9taXNlPHZvaWQ+IHtcbiAgICAgICAgYXdhaXQgc3VwZXIuc2VuZFBvc2l0aW9uKCk7XG4gICAgfVxufVxuIl0sIm1h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