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