@jsprismarine/client
Version:
Minecraft: Bedrock Edition RakNet client
166 lines (165 loc) • 20.8 kB
JavaScript
import { import_clear_interval_async, import_set_interval_async } from "./node_modules/.pnpm/set-interval-async@3.0.3/node_modules/set-interval-async/dist/dynamic/set-interval-async2.es.js";
import { Logger } from "@jsprismarine/logger";
import { Protocol } from "@jsprismarine/prismarine";
import { ConnectionPriority, InetAddress, MessageIdentifiers, Protocol as Protocol$1, RakNetSession } from "@jsprismarine/raknet";
import Crypto, { randomBytes } from "node:crypto";
import Dgram from "node:dgram";
import { EventEmitter } from "node:events";
//#region src/index.ts
var getRandomInt = (min, max) => {
min = Math.ceil(min);
max = Math.floor(max);
return Math.floor(Math.random() * (max - min + 1)) + min;
};
var PROTOCOL = 10;
var DEF_MTU_SIZE = 1455;
var RAKNET_TICK_LENGTH = 1 / 100;
var Client = class extends EventEmitter {
clientGUID = Crypto.randomBytes(8).readBigInt64BE();
logger = new Logger();
address;
targetAddress;
connection = null;
socket = Dgram.createSocket({ type: "udp4" });
get closed() {
return false;
}
connecting = false;
connected = false;
offlineHandled = false;
loginHandled = false;
constructor() {
super();
this.address = new InetAddress("0.0.0.0", getRandomInt(46e3, 49999));
this.socket.bind(this.address.getPort(), this.address.getAddress());
}
/**
* Creates a packet listener on given address and port.
* @param {string} address - The address to listen on.
* @param {number} port - The port to listen on.
* @returns {Promise<Client>} The client instance.
* @public
*/
async connect(address = "0.0.0.0", port = 19132) {
this.targetAddress = new InetAddress(address, port);
this.socket.on("message", (buffer) => {
this.handle(buffer);
});
if (this.connection) throw new Error("Already connected/connecting to server.");
this.logger.info("JSPrismarine client is now attempting to connect...");
const timer = (0, import_set_interval_async.setIntervalAsync)(async () => {
if (this.closed) {
await (0, import_clear_interval_async.clearIntervalAsync)(timer);
return;
}
if (!this.connecting) {
const pk = new Protocol$1.UnconnectedPing();
pk.timestamp = BigInt(Date.now());
pk.encode();
await this.sendBuffer(pk.getBuffer());
}
if (this.connected && !this.loginHandled) {
const pk = new Protocol.Packets.LoginPacket();
pk.encode();
const sendPk = new Protocol$1.Frame();
sendPk.reliability = 0;
sendPk.content = pk.getBuffer();
this.connection.sendFrame(sendPk, ConnectionPriority.NORMAL);
this.loginHandled = true;
}
this.connection?.update(Date.now());
}, RAKNET_TICK_LENGTH * 1e3);
return this;
}
/**
* Handles the incoming buffer.
* @param {Buffer} buffer - The buffer to handle.
* @private
* @internal
*/
async handle(buffer) {
const header = buffer.readUInt8();
if (this.connection && this.offlineHandled) {
this.connection.handle(buffer);
return;
}
let buf;
switch (header) {
case MessageIdentifiers.UNCONNECTED_PONG:
buf = this.handleUnconnectedPong(buffer);
await this.sendBuffer(buf);
break;
case MessageIdentifiers.OPEN_CONNECTION_REPLY_1:
buf = this.handleOpenConnectionReply1(buffer);
await this.sendBuffer(buf);
break;
case MessageIdentifiers.OPEN_CONNECTION_REPLY_2:
this.handleOpenConnectionReply2(buffer);
break;
default: this.logger.warn(`Unhandled offline packet ID: ${header}`, "Client/handle");
}
}
handleUnconnectedPong(buffer) {
const decodedPacket = new Protocol$1.UnconnectedPong(buffer);
decodedPacket.decode();
if (!decodedPacket.isValid()) throw new Error("Received an invalid offline message");
const packet = new Protocol$1.OpenConnectionRequest1();
packet.protocol = PROTOCOL;
packet.mtuSize = DEF_MTU_SIZE;
packet.encode();
return packet.getBuffer();
}
handleOpenConnectionReply1(buffer) {
const decodedPacket = new Protocol$1.OpenConnectionReply1(buffer);
decodedPacket.decode();
if (!decodedPacket.isValid()) throw new Error("Received an invalid offline message");
const packet = new Protocol$1.OpenConnectionRequest2();
packet.serverAddress = this.targetAddress;
packet.mtuSize = DEF_MTU_SIZE;
packet.clientGUID = this.clientGUID;
packet.encode();
this.connecting = true;
this.connection = new RakNetSession(this, DEF_MTU_SIZE, {
address: this.address.getAddress(),
port: this.address.getPort(),
family: "IPv4",
size: 0
}, randomBytes(8).readBigInt64BE());
return packet.getBuffer();
}
handleOpenConnectionReply2(buffer) {
const decodedPacket = new Protocol$1.OpenConnectionReply2(buffer);
decodedPacket.decode();
if (!decodedPacket.isValid()) throw new Error("Received an invalid offline message");
const packet = new Protocol$1.ConnectionRequest();
packet.clientGUID = this.clientGUID;
packet.requestTimestamp = BigInt(Date.now());
packet.encode();
const sendPacket = new Protocol$1.Frame();
sendPacket.reliability = 0;
sendPacket.content = packet.getBuffer();
this.connection?.sendFrame(sendPacket, ConnectionPriority.NORMAL);
this.offlineHandled = true;
this.connected = true;
}
/**
* Sends the buffer to the server
*
* @param buffer
*/
async sendBuffer(buffer) {
this.socket.send(buffer, 0, buffer.byteLength, this.targetAddress.getPort(), this.targetAddress.getAddress());
}
getSocket() {
return this.socket;
}
getAddress() {
return this.address;
}
async removeConnection(_connection, _reason) {
throw new Error("Method not implemented.");
}
};
//#endregion
export { Client as default };
//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5kZXguZXMuanMiLCJuYW1lcyI6W10sInNvdXJjZXMiOlsiLi4vc3JjL2luZGV4LnRzIl0sInNvdXJjZXNDb250ZW50IjpbImltcG9ydCB7IExvZ2dlciB9IGZyb20gJ0Bqc3ByaXNtYXJpbmUvbG9nZ2VyJztcbmltcG9ydCB7IFByb3RvY29sIGFzIEpTUFByb3RvY29sIH0gZnJvbSAnQGpzcHJpc21hcmluZS9wcmlzbWFyaW5lJztcbmltcG9ydCB7IENvbm5lY3Rpb25Qcmlvcml0eSwgSW5ldEFkZHJlc3MsIE1lc3NhZ2VJZGVudGlmaWVycywgUHJvdG9jb2wsIFJha05ldFNlc3Npb24gfSBmcm9tICdAanNwcmlzbWFyaW5lL3Jha25ldCc7XG5pbXBvcnQgQ3J5cHRvLCB7IHJhbmRvbUJ5dGVzIH0gZnJvbSAnbm9kZTpjcnlwdG8nO1xuaW1wb3J0IERncmFtLCB7IHR5cGUgU29ja2V0IH0gZnJvbSAnbm9kZTpkZ3JhbSc7XG5pbXBvcnQgeyBFdmVudEVtaXR0ZXIgfSBmcm9tICdub2RlOmV2ZW50cyc7XG5pbXBvcnQgeyBjbGVhckludGVydmFsQXN5bmMsIHNldEludGVydmFsQXN5bmMgfSBmcm9tICdzZXQtaW50ZXJ2YWwtYXN5bmMvZHluYW1pYyc7XG5cbi8vIGh0dHBzOi8vc3RhY2tvdmVyZmxvdy5jb20vYS8xNTI3ODIwLzMxNDI1NTNcbmNvbnN0IGdldFJhbmRvbUludCA9IChtaW46IG51bWJlciwgbWF4OiBudW1iZXIpID0+IHtcbiAgICBtaW4gPSBNYXRoLmNlaWwobWluKTtcbiAgICBtYXggPSBNYXRoLmZsb29yKG1heCk7XG4gICAgcmV0dXJuIE1hdGguZmxvb3IoTWF0aC5yYW5kb20oKSAqIChtYXggLSBtaW4gKyAxKSkgKyBtaW47XG59O1xuXG4vLyBNaW5lY3JhZnQgcmVsYXRlZCBwcm90b2NvbFxuY29uc3QgUFJPVE9DT0wgPSAxMDtcblxuLy8gTWF4IG5ldCB0cmFuc2ZlciB1bml0XG5jb25zdCBERUZfTVRVX1NJWkUgPSAxNDU1O1xuXG4vLyBSYWtuZXQgdGlja3NcbmNvbnN0IFJBS05FVF9UUFMgPSAxMDA7XG5jb25zdCBSQUtORVRfVElDS19MRU5HVEggPSAxIC8gUkFLTkVUX1RQUztcblxuZXhwb3J0IGRlZmF1bHQgY2xhc3MgQ2xpZW50IGV4dGVuZHMgRXZlbnRFbWl0dGVyIHtcbiAgICBwcml2YXRlIGNsaWVudEdVSUQgPSBDcnlwdG8ucmFuZG9tQnl0ZXMoOCkucmVhZEJpZ0ludDY0QkUoKTtcbiAgICBwcml2YXRlIHJlYWRvbmx5IGxvZ2dlciA9IG5ldyBMb2dnZXIoKTtcbiAgICBwcml2YXRlIHJlYWRvbmx5IGFkZHJlc3M6IEluZXRBZGRyZXNzO1xuICAgIHByaXZhdGUgdGFyZ2V0QWRkcmVzcyE6IEluZXRBZGRyZXNzO1xuICAgIHByaXZhdGUgY29ubmVjdGlvbjogUmFrTmV0U2Vzc2lvbiB8IG51bGwgPSBudWxsO1xuICAgIHByaXZhdGUgcmVhZG9ubHkgc29ja2V0ID0gRGdyYW0uY3JlYXRlU29ja2V0KHsgdHlwZTogJ3VkcDQnIH0pO1xuICAgIHByaXZhdGUgZ2V0IGNsb3NlZCgpIHtcbiAgICAgICAgcmV0dXJuIGZhbHNlO1xuICAgIH1cblxuICAgIHByaXZhdGUgY29ubmVjdGluZyA9IGZhbHNlO1xuICAgIHByaXZhdGUgY29ubmVjdGVkID0gZmFsc2U7XG4gICAgcHJpdmF0ZSBvZmZsaW5lSGFuZGxlZCA9IGZhbHNlO1xuICAgIHByaXZhdGUgbG9naW5IYW5kbGVkID0gZmFsc2U7XG5cbiAgICBwdWJsaWMgY29uc3RydWN0b3IoKSB7XG4gICAgICAgIHN1cGVyKCk7XG4gICAgICAgIHRoaXMuYWRkcmVzcyA9IG5ldyBJbmV0QWRkcmVzcygnMC4wLjAuMCcsIGdldFJhbmRvbUludCg0NjAwMCwgNDk5OTkpKTtcbiAgICAgICAgdGhpcy5zb2NrZXQuYmluZCh0aGlzLmFkZHJlc3MuZ2V0UG9ydCgpLCB0aGlzLmFkZHJlc3MuZ2V0QWRkcmVzcygpKTtcbiAgICB9XG5cbiAgICAvKipcbiAgICAgKiBDcmVhdGVzIGEgcGFja2V0IGxpc3RlbmVyIG9uIGdpdmVuIGFkZHJlc3MgYW5kIHBvcnQuXG4gICAgICogQHBhcmFtIHtzdHJpbmd9IGFkZHJlc3MgLSBUaGUgYWRkcmVzcyB0byBsaXN0ZW4gb24uXG4gICAgICogQHBhcmFtIHtudW1iZXJ9IHBvcnQgLSBUaGUgcG9ydCB0byBsaXN0ZW4gb24uXG4gICAgICogQHJldHVybnMge1Byb21pc2U8Q2xpZW50Pn0gVGhlIGNsaWVudCBpbnN0YW5jZS5cbiAgICAgKiBAcHVibGljXG4gICAgICovXG4gICAgcHVibGljIGFzeW5jIGNvbm5lY3QoYWRkcmVzcyA9ICcwLjAuMC4wJywgcG9ydCA9IDE5MTMyKTogUHJvbWlzZTxDbGllbnQ+IHtcbiAgICAgICAgdGhpcy50YXJnZXRBZGRyZXNzID0gbmV3IEluZXRBZGRyZXNzKGFkZHJlc3MsIHBvcnQpO1xuXG4gICAgICAgIHRoaXMuc29ja2V0Lm9uKCdtZXNzYWdlJywgKGJ1ZmZlcjogQnVmZmVyKSA9PiB7XG4gICAgICAgICAgICB2b2lkIHRoaXMuaGFuZGxlKGJ1ZmZlcik7XG4gICAgICAgIH0pO1xuXG4gICAgICAgIGlmICh0aGlzLmNvbm5lY3Rpb24pIHRocm93IG5ldyBFcnJvcignQWxyZWFkeSBjb25uZWN0ZWQvY29ubmVjdGluZyB0byBzZXJ2ZXIuJyk7XG5cbiAgICAgICAgdGhpcy5sb2dnZXIuaW5mbygnSlNQcmlzbWFyaW5lIGNsaWVudCBpcyBub3cgYXR0ZW1wdGluZyB0byBjb25uZWN0Li4uJyk7XG5cbiAgICAgICAgY29uc3QgdGltZXIgPSBzZXRJbnRlcnZhbEFzeW5jKGFzeW5jICgpID0+IHtcbiAgICAgICAgICAgIGlmICh0aGlzLmNsb3NlZCkge1xuICAgICAgICAgICAgICAgIGF3YWl0IGNsZWFySW50ZXJ2YWxBc3luYyh0aW1lcik7XG4gICAgICAgICAgICAgICAgcmV0dXJuO1xuICAgICAgICAgICAgfVxuXG4gICAgICAgICAgICAvLyBTZW5kIGEgY2xpZW50IHBhY2tldCB0byB0aGUgc2VydmVyXG4gICAgICAgICAgICAvLyBzbyB0aGUgc2VydmVyIGdvZXMgaW4gdGFyZ2V0IG1vZGVcbiAgICAgICAgICAgIC8vIGFuZCB0aGUgbG9naW4gcHJvY2VzcyBzdGFydHNcbiAgICAgICAgICAgIGlmICghdGhpcy5jb25uZWN0aW5nKSB7XG4gICAgICAgICAgICAgICAgY29uc3QgcGsgPSBuZXcgUHJvdG9jb2wuVW5jb25uZWN0ZWRQaW5nKCk7XG4gICAgICAgICAgICAgICAgcGsudGltZXN0YW1wID0gQmlnSW50KERhdGUubm93KCkpO1xuICAgICAgICAgICAgICAgIC8vIFRPRE86IGNhbiBiZSBvbWl0dGVkLi4uIHBrLiA9IHRoaXMuY2xpZW50R1VJRDtcbiAgICAgICAgICAgICAgICBway5lbmNvZGUoKTtcbiAgICAgICAgICAgICAgICBhd2FpdCB0aGlzLnNlbmRCdWZmZXIocGsuZ2V0QnVmZmVyKCkpO1xuICAgICAgICAgICAgfVxuXG4gICAgICAgICAgICBpZiAodGhpcy5jb25uZWN0ZWQgJiYgIXRoaXMubG9naW5IYW5kbGVkKSB7XG4gICAgICAgICAgICAgICAgY29uc3QgcGsgPSBuZXcgSlNQUHJvdG9jb2wuUGFja2V0cy5Mb2dpblBhY2tldCgpO1xuICAgICAgICAgICAgICAgIHBrLmVuY29kZSgpO1xuXG4gICAgICAgICAgICAgICAgY29uc3Qgc2VuZFBrID0gbmV3IFByb3RvY29sLkZyYW1lKCk7XG4gICAgICAgICAgICAgICAgc2VuZFBrLnJlbGlhYmlsaXR5ID0gMDtcbiAgICAgICAgICAgICAgICBzZW5kUGsuY29udGVudCA9IHBrLmdldEJ1ZmZlcigpO1xuXG4gICAgICAgICAgICAgICAgdGhpcy5jb25uZWN0aW9uIS5zZW5kRnJhbWUoc2VuZFBrLCBDb25uZWN0aW9uUHJpb3JpdHkuTk9STUFMKTsgLy8gUGFja2V0IG5lZWRzIHRvIGJlIHNwbGl0dGVkXG4gICAgICAgICAgICAgICAgdGhpcy5sb2dpbkhhbmRsZWQgPSB0cnVlO1xuICAgICAgICAgICAgfVxuXG4gICAgICAgICAgICB0aGlzLmNvbm5lY3Rpb24/LnVwZGF0ZShEYXRlLm5vdygpKTtcbiAgICAgICAgfSwgUkFLTkVUX1RJQ0tfTEVOR1RIICogMTAwMCk7XG4gICAgICAgIHJldHVybiB0aGlzO1xuICAgIH1cblxuICAgIC8qKlxuICAgICAqIEhhbmRsZXMgdGhlIGluY29taW5nIGJ1ZmZlci5cbiAgICAgKiBAcGFyYW0ge0J1ZmZlcn0gYnVmZmVyIC0gVGhlIGJ1ZmZlciB0byBoYW5kbGUuXG4gICAgICogQHByaXZhdGVcbiAgICAgKiBAaW50ZXJuYWxcbiAgICAgKi9cbiAgICBwcml2YXRlIGFzeW5jIGhhbmRsZShidWZmZXI6IEJ1ZmZlcik6IFByb21pc2U8dm9pZD4ge1xuICAgICAgICBjb25zdCBoZWFkZXIgPSBidWZmZXIucmVhZFVJbnQ4KCk7IC8vIFJlYWQgcGFja2V0IGhlYWRlclxuXG4gICAgICAgIGlmICh0aGlzLmNvbm5lY3Rpb24gJiYgdGhpcy5vZmZsaW5lSGFuZGxlZCkge1xuICAgICAgICAgICAgdGhpcy5jb25uZWN0aW9uLmhhbmRsZShidWZmZXIpO1xuICAgICAgICAgICAgcmV0dXJuO1xuICAgICAgICB9XG5cbiAgICAgICAgbGV0IGJ1ZjtcbiAgICAgICAgc3dpdGNoIChoZWFkZXIpIHtcbiAgICAgICAgICAgIGNhc2UgTWVzc2FnZUlkZW50aWZpZXJzLlVOQ09OTkVDVEVEX1BPTkc6XG4gICAgICAgICAgICAgICAgYnVmID0gdGhpcy5oYW5kbGVVbmNvbm5lY3RlZFBvbmcoYnVmZmVyKTtcbiAgICAgICAgICAgICAgICBhd2FpdCB0aGlzLnNlbmRCdWZmZXIoYnVmKTtcbiAgICAgICAgICAgICAgICBicmVhaztcbiAgICAgICAgICAgIGNhc2UgTWVzc2FnZUlkZW50aWZpZXJzLk9QRU5fQ09OTkVDVElPTl9SRVBMWV8xOlxuICAgICAgICAgICAgICAgIGJ1ZiA9IHRoaXMuaGFuZGxlT3BlbkNvbm5lY3Rpb25SZXBseTEoYnVmZmVyKTtcbiAgICAgICAgICAgICAgICBhd2FpdCB0aGlzLnNlbmRCdWZmZXIoYnVmKTtcbiAgICAgICAgICAgICAgICBicmVhaztcbiAgICAgICAgICAgIGNhc2UgTWVzc2FnZUlkZW50aWZpZXJzLk9QRU5fQ09OTkVDVElPTl9SRVBMWV8yOlxuICAgICAgICAgICAgICAgIHRoaXMuaGFuZGxlT3BlbkNvbm5lY3Rpb25SZXBseTIoYnVmZmVyKTtcbiAgICAgICAgICAgICAgICBicmVhaztcbiAgICAgICAgICAgIGRlZmF1bHQ6XG4gICAgICAgICAgICAgICAgdGhpcy5sb2dnZXIud2FybihgVW5oYW5kbGVkIG9mZmxpbmUgcGFja2V0IElEOiAke2hlYWRlcn1gLCAnQ2xpZW50L2hhbmRsZScpO1xuICAgICAgICB9XG4gICAgfVxuXG4gICAgcHVibGljIGhhbmRsZVVuY29ubmVjdGVkUG9uZyhidWZmZXI6IEJ1ZmZlcik6IEJ1ZmZlciB7XG4gICAgICAgIC8vIERlY29kZSBzZXJ2ZXIgcGFja2V0XG4gICAgICAgIGNvbnN0IGRlY29kZWRQYWNrZXQgPSBuZXcgUHJvdG9jb2wuVW5jb25uZWN0ZWRQb25nKGJ1ZmZlcik7XG4gICAgICAgIGRlY29kZWRQYWNrZXQuZGVjb2RlKCk7XG5cbiAgICAgICAgLy8gQ2hlY2sgcGFja2V0IHZhbGlkaXR5XG4gICAgICAgIC8vIFRvIHJlZmFjdG9yXG4gICAgICAgIGlmICghZGVjb2RlZFBhY2tldC5pc1ZhbGlkKCkpIHtcbiAgICAgICAgICAgIHRocm93IG5ldyBFcnJvcignUmVjZWl2ZWQgYW4gaW52YWxpZCBvZmZsaW5lIG1lc3NhZ2UnKTtcbiAgICAgICAgfVxuXG4gICAgICAgIC8vIEVuY29kZSByZXNwb25zZVxuICAgICAgICBjb25zdCBwYWNrZXQgPSBuZXcgUHJvdG9jb2wuT3BlbkNvbm5lY3Rpb25SZXF1ZXN0MSgpO1xuICAgICAgICBwYWNrZXQucHJvdG9jb2wgPSBQUk9UT0NPTDtcbiAgICAgICAgcGFja2V0Lm10dVNpemUgPSBERUZfTVRVX1NJWkU7XG4gICAgICAgIHBhY2tldC5lbmNvZGUoKTtcblxuICAgICAgICAvLyBVcGRhdGUgc2Vzc2lvbiBzdGF0dXNcbiAgICAgICAgLy8gdGhpcy5zdGF0dXMgPSBDb25uZWN0aW9uU3RhdHVzLlRhcmdldHRlZDtcblxuICAgICAgICByZXR1cm4gcGFja2V0LmdldEJ1ZmZlcigpO1xuICAgIH1cblxuICAgIHB1YmxpYyBoYW5kbGVPcGVuQ29ubmVjdGlvblJlcGx5MShidWZmZXI6IEJ1ZmZlcik6IEJ1ZmZlciB7XG4gICAgICAgIC8vIERlY29kZSBzZXJ2ZXIgcGFja2V0XG4gICAgICAgIGNvbnN0IGRlY29kZWRQYWNrZXQgPSBuZXcgUHJvdG9jb2wuT3BlbkNvbm5lY3Rpb25SZXBseTEoYnVmZmVyKTtcbiAgICAgICAgZGVjb2RlZFBhY2tldC5kZWNvZGUoKTtcblxuICAgICAgICAvLyBDaGVjayBwYWNrZXQgdmFsaWRpdHlcbiAgICAgICAgLy8gVG8gcmVmYWN0b3JcbiAgICAgICAgaWYgKCFkZWNvZGVkUGFja2V0LmlzVmFsaWQoKSkge1xuICAgICAgICAgICAgdGhyb3cgbmV3IEVycm9yKCdSZWNlaXZlZCBhbiBpbnZhbGlkIG9mZmxpbmUgbWVzc2FnZScpO1xuICAgICAgICB9XG5cbiAgICAgICAgLy8gRW5jb2RlIHJlc3BvbnNlXG4gICAgICAgIGNvbnN0IHBhY2tldCA9IG5ldyBQcm90b2NvbC5PcGVuQ29ubmVjdGlvblJlcXVlc3QyKCk7XG4gICAgICAgIHBhY2tldC5zZXJ2ZXJBZGRyZXNzID0gdGhpcy50YXJnZXRBZGRyZXNzO1xuICAgICAgICBwYWNrZXQubXR1U2l6ZSA9IERFRl9NVFVfU0laRTtcbiAgICAgICAgcGFja2V0LmNsaWVudEdVSUQgPSB0aGlzLmNsaWVudEdVSUQ7XG4gICAgICAgIHBhY2tldC5lbmNvZGUoKTtcblxuICAgICAgICAvLyBVcGRhdGUgc2Vzc2lvbiBzdGF0dXNcbiAgICAgICAgdGhpcy5jb25uZWN0aW5nID0gdHJ1ZTtcbiAgICAgICAgLy8gVGhpcy5zdGF0dXMgPSBDb25uZWN0aW9uU3RhdHVzLkNvbm5lY3RlZDtcbiAgICAgICAgdGhpcy5jb25uZWN0aW9uID0gbmV3IFJha05ldFNlc3Npb24oXG4gICAgICAgICAgICB0aGlzIGFzIGFueSxcbiAgICAgICAgICAgIERFRl9NVFVfU0laRSxcbiAgICAgICAgICAgIHtcbiAgICAgICAgICAgICAgICBhZGRyZXNzOiB0aGlzLmFkZHJlc3MuZ2V0QWRkcmVzcygpLFxuICAgICAgICAgICAgICAgIHBvcnQ6IHRoaXMuYWRkcmVzcy5nZXRQb3J0KCksXG4gICAgICAgICAgICAgICAgZmFtaWx5OiAnSVB2NCcsXG4gICAgICAgICAgICAgICAgc2l6ZTogMFxuICAgICAgICAgICAgfSxcbiAgICAgICAgICAgIHJhbmRvbUJ5dGVzKDgpLnJlYWRCaWdJbnQ2NEJFKClcbiAgICAgICAgKTtcblxuICAgICAgICByZXR1cm4gcGFja2V0LmdldEJ1ZmZlcigpO1xuICAgIH1cblxuICAgIHB1YmxpYyBoYW5kbGVPcGVuQ29ubmVjdGlvblJlcGx5MihidWZmZXI6IEJ1ZmZlcik6IHZvaWQge1xuICAgICAgICAvLyBEZWNvZGUgc2VydmVyIHBhY2tldFxuICAgICAgICBjb25zdCBkZWNvZGVkUGFja2V0ID0gbmV3IFByb3RvY29sLk9wZW5Db25uZWN0aW9uUmVwbHkyKGJ1ZmZlcik7XG4gICAgICAgIGRlY29kZWRQYWNrZXQuZGVjb2RlKCk7XG5cbiAgICAgICAgLy8gQ2hlY2sgcGFja2V0IHZhbGlkaXR5XG4gICAgICAgIC8vIFRvIHJlZmFjdG9yXG4gICAgICAgIGlmICghZGVjb2RlZFBhY2tldC5pc1ZhbGlkKCkpIHtcbiAgICAgICAgICAgIHRocm93IG5ldyBFcnJvcignUmVjZWl2ZWQgYW4gaW52YWxpZCBvZmZsaW5lIG1lc3NhZ2UnKTtcbiAgICAgICAgfVxuXG4gICAgICAgIC8vIEVuY29kZSByZXNwb25zZSAoZW5jYXBzdWxhdGVkKVxuICAgICAgICBjb25zdCBwYWNrZXQgPSBuZXcgUHJvdG9jb2wuQ29ubmVjdGlvblJlcXVlc3QoKTtcbiAgICAgICAgcGFja2V0LmNsaWVudEdVSUQgPSB0aGlzLmNsaWVudEdVSUQ7XG4gICAgICAgIHBhY2tldC5yZXF1ZXN0VGltZXN0YW1wID0gQmlnSW50KERhdGUubm93KCkpO1xuICAgICAgICBwYWNrZXQuZW5jb2RlKCk7XG5cbiAgICAgICAgY29uc3Qgc2VuZFBhY2tldCA9IG5ldyBQcm90b2NvbC5GcmFtZSgpO1xuICAgICAgICBzZW5kUGFja2V0LnJlbGlhYmlsaXR5ID0gMDtcbiAgICAgICAgc2VuZFBhY2tldC5jb250ZW50ID0gcGFja2V0LmdldEJ1ZmZlcigpO1xuXG4gICAgICAgIHRoaXMuY29ubmVjdGlvbj8uc2VuZEZyYW1lKHNlbmRQYWNrZXQsIENvbm5lY3Rpb25Qcmlvcml0eS5OT1JNQUwpO1xuXG4gICAgICAgIHRoaXMub2ZmbGluZUhhbmRsZWQgPSB0cnVlO1xuICAgICAgICB0aGlzLmNvbm5lY3RlZCA9IHRydWU7IC8vIFNob3VsZCBiZS4uLiB3ZSBjYW4ndCByZWx5IG9uIGl0XG4gICAgfVxuXG4gICAgLyoqXG4gICAgICogU2VuZHMgdGhlIGJ1ZmZlciB0byB0aGUgc2VydmVyXG4gICAgICpcbiAgICAgKiBAcGFyYW0gYnVmZmVyXG4gICAgICovXG4gICAgcHVibGljIGFzeW5jIHNlbmRCdWZmZXIoYnVmZmVyOiBCdWZmZXIpOiBQcm9taXNlPHZvaWQ+IHtcbiAgICAgICAgdGhpcy5zb2NrZXQuc2VuZChcbiAgICAgICAgICAgIGJ1ZmZlciBhcyBhbnksXG4gICAgICAgICAgICAwLFxuICAgICAgICAgICAgYnVmZmVyLmJ5dGVMZW5ndGgsXG4gICAgICAgICAgICB0aGlzLnRhcmdldEFkZHJlc3MuZ2V0UG9ydCgpLFxuICAgICAgICAgICAgdGhpcy50YXJnZXRBZGRyZXNzLmdldEFkZHJlc3MoKVxuICAgICAgICApO1xuICAgIH1cblxuICAgIHB1YmxpYyBnZXRTb2NrZXQoKTogU29ja2V0IHtcbiAgICAgICAgcmV0dXJuIHRoaXMuc29ja2V0O1xuICAgIH1cblxuICAgIHB1YmxpYyBnZXRBZGRyZXNzKCk6IEluZXRBZGRyZXNzIHtcbiAgICAgICAgcmV0dXJuIHRoaXMuYWRkcmVzcztcbiAgICB9XG5cbiAgICBwdWJsaWMgYXN5bmMgcmVtb3ZlQ29ubmVjdGlvbihfY29ubmVjdGlvbjogUmFrTmV0U2Vzc2lvbiwgX3JlYXNvbj86IHN0cmluZyk6IFByb21pc2U8dm9pZD4ge1xuICAgICAgICB0aHJvdyBuZXcgRXJyb3IoJ01ldGhvZCBub3QgaW1wbGVtZW50ZWQuJyk7XG4gICAgfVxufVxuIl0sIm1hcHBpbmdzIjoiOzs7Ozs7OztBQVNBLElBQU0sZ0JBQWdCLEtBQWEsUUFBZ0I7Q0FDL0MsTUFBTSxLQUFLLEtBQUssR0FBRztDQUNuQixNQUFNLEtBQUssTUFBTSxHQUFHO0NBQ3BCLE9BQU8sS0FBSyxNQUFNLEtBQUssT0FBTyxLQUFLLE1BQU0sTUFBTSxFQUFFLElBQUk7QUFDekQ7QUFHQSxJQUFNLFdBQVc7QUFHakIsSUFBTSxlQUFlO0FBSXJCLElBQU0scUJBQXFCLElBQUk7QUFFL0IsSUFBcUIsU0FBckIsY0FBb0MsYUFBYTtDQUM3QyxhQUFxQixPQUFPLFlBQVksQ0FBQyxFQUFFLGVBQWU7Q0FDMUQsU0FBMEIsSUFBSSxPQUFPO0NBQ3JDO0NBQ0E7Q0FDQSxhQUEyQztDQUMzQyxTQUEwQixNQUFNLGFBQWEsRUFBRSxNQUFNLE9BQU8sQ0FBQztDQUM3RCxJQUFZLFNBQVM7RUFDakIsT0FBTztDQUNYO0NBRUEsYUFBcUI7Q0FDckIsWUFBb0I7Q0FDcEIsaUJBQXlCO0NBQ3pCLGVBQXVCO0NBRXZCLGNBQXFCO0VBQ2pCLE1BQU07RUFDTixLQUFLLFVBQVUsSUFBSSxZQUFZLFdBQVcsYUFBYSxNQUFPLEtBQUssQ0FBQztFQUNwRSxLQUFLLE9BQU8sS0FBSyxLQUFLLFFBQVEsUUFBUSxHQUFHLEtBQUssUUFBUSxXQUFXLENBQUM7Q0FDdEU7Ozs7Ozs7O0NBU0EsTUFBYSxRQUFRLFVBQVUsV0FBVyxPQUFPLE9BQXdCO0VBQ3JFLEtBQUssZ0JBQWdCLElBQUksWUFBWSxTQUFTLElBQUk7RUFFbEQsS0FBSyxPQUFPLEdBQUcsWUFBWSxX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