@jsprismarine/prismarine
Version:
Dedicated Minecraft Bedrock Edition server written in TypeScript
116 lines (115 loc) • 12.9 kB
JavaScript
import Timer from "../utils/Timer.es.js";
import Identifiers from "./Identifiers.es.js";
import { Packets_exports } from "./Packets.es.js";
import { Handlers_exports } from "./Handlers.es.js";
//#region src/network/PacketRegistry.ts
var PacketRegistry = class {
server;
packets = /* @__PURE__ */ new Map();
handlers = /* @__PURE__ */ new Map();
constructor(server) {
this.server = server;
}
/**
* On enable hook.
* @group Lifecycle
*/
async enable() {
await this.registerPackets();
await this.registerHandlers();
}
/**
* On disable hook.
* @group Lifecycle
*/
async disable() {
this.handlers.clear();
this.packets.clear();
}
/**
* Register a packet.
* @param packet - the packet.
*/
registerPacket(packet) {
if (this.packets.has(packet.NetID)) throw new Error(`Packet ${packet.name} is trying to use id ${packet.NetID.toString(16)} which already exists!`);
this.packets.set(packet.NetID, packet);
this.server.getLogger().debug(`Packet with id §b${packet.name}§r registered`);
}
/**
* Get a packet by it's network ID.
* @param id - the NetID.
*/
getPacket(id) {
if (!this.packets.has(id)) throw new Error(`Invalid packet with id ${id}!`);
return this.packets.get(id);
}
/**
* Remove a packet from the registry.
* @param id - the NetID.
*/
removePacket(id) {
this.packets.delete(id);
}
registerHandler(id, handler) {
if (this.handlers.has(id)) throw new Error(`Handler with id ${id} already exists!`);
this.handlers.set(id, handler);
this.server.getLogger().debug(`Handler with id §b${handler.constructor.name}§r registered`, "PacketRegistry/registerHandler");
}
getHandler(id) {
if (!this.handlers.has(id)) throw new Error(`Invalid handler with id ${id.toString(16)}!`);
return this.handlers.get(id);
}
/**
* Merge two handlers.
* This is useful if you want to extend a handler without actually replacing it.
*
* @param handler - the first handler, executed first.
* @param handler2 - the second handler.
*/
appendHandler(handler, handler2) {
return new class Handler {
async handle(packet, server, session) {
await handler.handle(packet, server, session);
await handler2.handle(packet, server, session);
}
}();
}
/**
* Remove a handler from the registry.
* @param id - the handler id.
*/
removeHandler(id) {
this.handlers.delete(id);
}
/**
* Dynamically register all packets exported by './Protocol'.
*/
async registerPackets() {
const timer = new Timer();
Object.entries(Packets_exports).filter(([, value]) => value.name !== "DataPacket" && value.name !== "BatchPacket").map(([, value]) => this.registerPacket(value));
this.server.getLogger().verbose(`Registered §b${this.packets.size}§r of §b${Array.from(Object.keys(Identifiers)).length - 2}§r packet(s) (took §e${timer.stop()} ms§r)!`);
}
/**
* Dynamically register all handlers exported by './Handlers'.
*/
async registerHandlers() {
const timer = new Timer();
Object.entries(Handlers_exports).map(([, value]) => this.registerHandler(value.NetID, new value()));
this.server.getLogger().verbose(`Registered §b${this.handlers.size}§r packet handler(s) (took §e${timer.stop()} ms§r)!`);
}
/**
* Get all packets from the registry.
*/
getPackets() {
return this.packets;
}
/**
* Get all handlers from the registry.
*/
getHandlers() {
return this.handlers;
}
};
//#endregion
export { PacketRegistry as default };
//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiUGFja2V0UmVnaXN0cnkuZXMuanMiLCJuYW1lcyI6W10sInNvdXJjZXMiOlsiLi4vLi4vc3JjL25ldHdvcmsvUGFja2V0UmVnaXN0cnkudHMiXSwic291cmNlc0NvbnRlbnQiOlsiaW1wb3J0ICogYXMgSGFuZGxlcnMgZnJvbSAnLi9IYW5kbGVycyc7XG5pbXBvcnQgKiBhcyBQYWNrZXRzIGZyb20gJy4vUGFja2V0cyc7XG5cbmltcG9ydCB0eXBlIHsgUGxheWVyU2Vzc2lvbiB9IGZyb20gJy4uLyc7XG5pbXBvcnQgdHlwZSBTZXJ2ZXIgZnJvbSAnLi4vU2VydmVyJztcbmltcG9ydCBUaW1lciBmcm9tICcuLi91dGlscy9UaW1lcic7XG5pbXBvcnQgSWRlbnRpZmllcnMgZnJvbSAnLi9JZGVudGlmaWVycyc7XG5pbXBvcnQgdHlwZSBQYWNrZXRIYW5kbGVyIGZyb20gJy4vaGFuZGxlci9QYWNrZXRIYW5kbGVyJztcbmltcG9ydCB0eXBlIFByZUxvZ2luUGFja2V0SGFuZGxlciBmcm9tICcuL2hhbmRsZXIvUHJlTG9naW5QYWNrZXRIYW5kbGVyJztcblxuZXhwb3J0IGRlZmF1bHQgY2xhc3MgUGFja2V0UmVnaXN0cnkge1xuICAgIHByaXZhdGUgc2VydmVyOiBTZXJ2ZXI7XG4gICAgcHJpdmF0ZSByZWFkb25seSBwYWNrZXRzOiBNYXA8bnVtYmVyLCB0eXBlb2YgUGFja2V0cy5EYXRhUGFja2V0PiA9IG5ldyBNYXAoKTtcbiAgICBwcml2YXRlIHJlYWRvbmx5IGhhbmRsZXJzOiBNYXA8bnVtYmVyLCBQYWNrZXRIYW5kbGVyPGFueT4+ID0gbmV3IE1hcCgpO1xuXG4gICAgcHVibGljIGNvbnN0cnVjdG9yKHNlcnZlcjogU2VydmVyKSB7XG4gICAgICAgIHRoaXMuc2VydmVyID0gc2VydmVyO1xuICAgIH1cblxuICAgIC8qKlxuICAgICAqIE9uIGVuYWJsZSBob29rLlxuICAgICAqIEBncm91cCBMaWZlY3ljbGVcbiAgICAgKi9cbiAgICBwdWJsaWMgYXN5bmMgZW5hYmxlKCk6IFByb21pc2U8dm9pZD4ge1xuICAgICAgICBhd2FpdCB0aGlzLnJlZ2lzdGVyUGFja2V0cygpO1xuICAgICAgICBhd2FpdCB0aGlzLnJlZ2lzdGVySGFuZGxlcnMoKTtcbiAgICB9XG5cbiAgICAvKipcbiAgICAgKiBPbiBkaXNhYmxlIGhvb2suXG4gICAgICogQGdyb3VwIExpZmVjeWNsZVxuICAgICAqL1xuICAgIHB1YmxpYyBhc3luYyBkaXNhYmxlKCk6IFByb21pc2U8dm9pZD4ge1xuICAgICAgICB0aGlzLmhhbmRsZXJzLmNsZWFyKCk7XG4gICAgICAgIHRoaXMucGFja2V0cy5jbGVhcigpO1xuICAgIH1cblxuICAgIC8qKlxuICAgICAqIFJlZ2lzdGVyIGEgcGFja2V0LlxuICAgICAqIEBwYXJhbSBwYWNrZXQgLSB0aGUgcGFja2V0LlxuICAgICAqL1xuICAgIHB1YmxpYyByZWdpc3RlclBhY2tldChwYWNrZXQ6IHR5cGVvZiBQYWNrZXRzLkRhdGFQYWNrZXQpOiB2b2lkIHtcbiAgICAgICAgaWYgKHRoaXMucGFja2V0cy5oYXMocGFja2V0Lk5ldElEKSlcbiAgICAgICAgICAgIHRocm93IG5ldyBFcnJvcihcbiAgICAgICAgICAgICAgICBgUGFja2V0ICR7cGFja2V0Lm5hbWV9IGlzIHRyeWluZyB0byB1c2UgaWQgJHtwYWNrZXQuTmV0SUQudG9TdHJpbmcoMTYpfSB3aGljaCBhbHJlYWR5IGV4aXN0cyFgXG4gICAgICAgICAgICApO1xuXG4gICAgICAgIHRoaXMucGFja2V0cy5zZXQocGFja2V0Lk5ldElELCBwYWNrZXQpO1xuICAgICAgICB0aGlzLnNlcnZlci5nZXRMb2dnZXIoKS5kZWJ1ZyhgUGFja2V0IHdpdGggaWQgwqdiJHtwYWNrZXQubmFtZX3Cp3IgcmVnaXN0ZXJlZGApO1xuICAgIH1cblxuICAgIC8qKlxuICAgICAqIEdldCBhIHBhY2tldCBieSBpdCdzIG5ldHdvcmsgSUQuXG4gICAgICogQHBhcmFtIGlkIC0gdGhlIE5ldElELlxuICAgICAqL1xuICAgIHB1YmxpYyBnZXRQYWNrZXQoaWQ6IG51bWJlcik6IGFueSB7XG4gICAgICAgIGlmICghdGhpcy5wYWNrZXRzLmhhcyhpZCkpIHRocm93IG5ldyBFcnJvcihgSW52YWxpZCBwYWNrZXQgd2l0aCBpZCAke2lkfSFgKTtcblxuICAgICAgICByZXR1cm4gdGhpcy5wYWNrZXRzLmdldChpZCkhO1xuICAgIH1cblxuICAgIC8qKlxuICAgICAqIFJlbW92ZSBhIHBhY2tldCBmcm9tIHRoZSByZWdpc3RyeS5cbiAgICAgKiBAcGFyYW0gaWQgLSB0aGUgTmV0SUQuXG4gICAgICovXG4gICAgcHVibGljIHJlbW92ZVBhY2tldChpZDogbnVtYmVyKTogdm9pZCB7XG4gICAgICAgIHRoaXMucGFja2V0cy5kZWxldGUoaWQpO1xuICAgIH1cblxuICAgIHB1YmxpYyByZWdpc3RlckhhbmRsZXIoaWQ6IG51bWJlciwgaGFuZGxlcjogUGFja2V0SGFuZGxlcjxhbnk+KTogdm9pZCB7XG4gICAgICAgIGlmICh0aGlzLmhhbmRsZXJzLmhhcyhpZCkpIHRocm93IG5ldyBFcnJvcihgSGFuZGxlciB3aXRoIGlkICR7aWR9IGFscmVhZHkgZXhpc3RzIWApO1xuXG4gICAgICAgIHRoaXMuaGFuZGxlcnMuc2V0KGlkLCBoYW5kbGVyKTtcbiAgICAgICAgdGhpcy5zZXJ2ZXJcbiAgICAgICAgICAgIC5nZXRMb2dnZXIoKVxuICAgICAgICAgICAgLmRlYnVnKGBIYW5kbGVyIHdpdGggaWQgwqdiJHtoYW5kbGVyLmNvbnN0cnVjdG9yLm5hbWV9wqdyIHJlZ2lzdGVyZWRgLCAnUGFja2V0UmVnaXN0cnkvcmVnaXN0ZXJIYW5kbGVyJyk7XG4gICAgfVxuXG4gICAgcHVibGljIGdldEhhbmRsZXIoaWQ6IG51bWJlcik6IFBhY2tldEhhbmRsZXI8YW55PiB8IFByZUxvZ2luUGFja2V0SGFuZGxlcjxhbnk+IHtcbiAgICAgICAgaWYgKCF0aGlzLmhhbmRsZXJzLmhhcyhpZCkpIHRocm93IG5ldyBFcnJvcihgSW52YWxpZCBoYW5kbGVyIHdpdGggaWQgJHtpZC50b1N0cmluZygxNil9IWApO1xuXG4gICAgICAgIHJldHVybiB0aGlzLmhhbmRsZXJzLmdldChpZCkhO1xuICAgIH1cblxuICAgIC8qKlxuICAgICAqIE1lcmdlIHR3byBoYW5kbGVycy5cbiAgICAgKiBUaGlzIGlzIHVzZWZ1bCBpZiB5b3Ugd2FudCB0byBleHRlbmQgYSBoYW5kbGVyIHdpdGhvdXQgYWN0dWFsbHkgcmVwbGFjaW5nIGl0LlxuICAgICAqXG4gICAgICogQHBhcmFtIGhhbmRsZXIgLSB0aGUgZmlyc3QgaGFuZGxlciwgZXhlY3V0ZWQgZmlyc3QuXG4gICAgICogQHBhcmFtIGhhbmRsZXIyIC0gdGhlIHNlY29uZCBoYW5kbGVyLlxuICAgICAqL1xuICAgIHB1YmxpYyBhcHBlbmRIYW5kbGVyKGhhbmRsZXI6IFBhY2tldEhhbmRsZXI8YW55PiwgaGFuZGxlcjI6IFBhY2tldEhhbmRsZXI8YW55Pik6IFBhY2tldEhhbmRsZXI8YW55PiB7XG4gICAgICAgIGNvbnN0IHJlcyA9IG5ldyAoY2xhc3MgSGFuZGxlciB7XG4gICAgICAgICAgICBwdWJsaWMgYXN5bmMgaGFuZGxlKHBhY2tldDogYW55LCBzZXJ2ZXI6IFNlcnZlciwgc2Vzc2lvbjogUGxheWVyU2Vzc2lvbikge1xuICAgICAgICAgICAgICAgIGF3YWl0IGhhbmRsZXIuaGFuZGxlKHBhY2tldCwgc2VydmVyLCBzZXNzaW9uKTtcbiAgICAgICAgICAgICAgICBhd2FpdCBoYW5kbGVyMi5oYW5kbGUocGFja2V0LCBzZXJ2ZXIsIHNlc3Npb24pO1xuICAgICAgICAgICAgfVxuICAgICAgICB9KSgpO1xuXG4gICAgICAgIHJldHVybiByZXMgYXMgUGFja2V0SGFuZGxlcjxhbnk+O1xuICAgIH1cblxuICAgIC8qKlxuICAgICAqIFJlbW92ZSBhIGhhbmRsZXIgZnJvbSB0aGUgcmVnaXN0cnkuXG4gICAgICogQHBhcmFtIGlkIC0gdGhlIGhhbmRsZXIgaWQuXG4gICAgICovXG4gICAgcHVibGljIHJlbW92ZUhhbmRsZXIoaWQ6IG51bWJlcik6IHZvaWQge1xuICAgICAgICB0aGlzLmhhbmRsZXJzLmRlbGV0ZShpZCk7XG4gICAgfVxuXG4gICAgLyoqXG4gICAgICogRHluYW1pY2FsbHkgcmVnaXN0ZXIgYWxsIHBhY2tldHMgZXhwb3J0ZWQgYnkgJy4vUHJvdG9jb2wnLlxuICAgICAqL1xuICAgIHByaXZhdGUgYXN5bmMgcmVnaXN0ZXJQYWNrZXRzKCk6IFByb21pc2U8dm9pZD4ge1xuICAgICAgICBjb25zdCB0aW1lciA9IG5ldyBUaW1lcigpO1xuXG4gICAgICAgIC8vIER5bmFtaWNhbGx5IHJlZ2lzdGVyIHBhY2tldHNcbiAgICAgICAgLy8gV2UgbmVlZCB0byBtYW51YWxseSBpZ25vcmUgRGF0YVBhY2tldCAmIEJhdGNoUGFja2V0XG4gICAgICAgIE9iamVjdC5lbnRyaWVzKFBhY2tldHMpXG4gICAgICAgICAgICAuZmlsdGVyKChbLCB2YWx1ZV0pID0+IHZhbHVlLm5hbWUgIT09ICdEYXRhUGFja2V0JyAmJiB2YWx1ZS5uYW1lICE9PSAnQmF0Y2hQYWNrZXQnKVxuICAgICAgICAgICAgLm1hcCgoWywgdmFsdWVdKSA9PiB0aGlzLnJlZ2lzdGVyUGFja2V0KHZhbHVlKSk7XG5cbiAgICAgICAgdGhpcy5zZXJ2ZXJcbiAgICAgICAgICAgIC5nZXRMb2dnZXIoKVxuICAgICAgICAgICAgLnZlcmJvc2UoXG4gICAgICAgICAgICAgICAgYFJlZ2lzdGVyZWQgwqdiJHt0aGlzLnBhY2tldHMuc2l6ZX3Cp3Igb2YgwqdiJHtcbiAgICAgICAgICAgICAgICAgICAgQXJyYXkuZnJvbShPYmplY3Qua2V5cyhJZGVudGlmaWVycykpLmxlbmd0aCAtIDJcbiAgICAgICAgICAgICAgICB9wqdyIHBhY2tldChzKSAodG9vayDCp2Uke3RpbWVyLnN0b3AoKX0gbXPCp3IpIWBcbiAgICAgICAgICAgICk7XG4gICAgfVxuXG4gICAgLyoqXG4gICAgICogRHluYW1pY2FsbHkgcmVnaXN0ZXIgYWxsIGhhbmRsZXJzIGV4cG9ydGVkIGJ5ICcuL0hhbmRsZXJzJy5cbiAgICAgKi9cbiAgICBwcml2YXRlIGFzeW5jIHJlZ2lzdGVySGFuZGxlcnMoKTogUHJvbWlzZTx2b2lkPiB7XG4gICAgICAgIGNvbnN0IHRpbWVyID0gbmV3IFRpbWVyKCk7XG5cbiAgICAgICAgLy8gRHluYW1pY2FsbHkgcmVnaXN0ZXIgaGFuZGxlcnNcbiAgICAgICAgT2JqZWN0LmVudHJpZXMoSGFuZGxlcnMpLm1hcCgoWywgdmFsdWVdKSA9PiB0aGlzLnJlZ2lzdGVySGFuZGxlcih2YWx1ZS5OZXRJRCEsIG5ldyAodmFsdWUgYXMgYW55KSgpKSk7XG5cbiAgICAgICAgdGhpcy5zZXJ2ZXJcbiAgICAgICAgICAgIC5nZXRMb2dnZXIoKVxuICAgICAgICAgICAgLnZlcmJvc2UoYFJlZ2lzdGVyZWQgwqdiJHt0aGlzLmhhbmRsZXJzLnNpemV9wqdyIHBhY2tldCBoYW5kbGVyKHMpICh0b29rIMKnZSR7dGltZXIuc3RvcCgpfSBtc8KncikhYCk7XG4gICAgfVxuXG4gICAgLyoqXG4gICAgICogR2V0IGFsbCBwYWNrZXRzIGZyb20gdGhlIHJlZ2lzdHJ5LlxuICAgICAqL1xuICAgIHB1YmxpYyBnZXRQYWNrZXRzKCkge1xuICAgICAgICByZXR1cm4gdGhpcy5wYWNrZXRzO1xuICAgIH1cblxuICAgIC8qKlxuICAgICAqIEdldCBhbGwgaGFuZGxlcnMgZnJvbSB0aGUgcmVnaXN0cnkuXG4gICAgICovXG4gICAgcHVibGljIGdldEhhbmRsZXJzKCkge1xuICAgICAgICByZXR1cm4gdGhpcy5oYW5kbGVycztcbiAgICB9XG59XG4iXSwibWFwcGluZ3MiOiI7Ozs7O0FBVUEsSUFBcUIsaUJBQXJCLE1BQW9DO0NBQ2hDO0NBQ0EsMEJBQW1FLElBQUksSUFBSTtDQUMzRSwyQkFBNkQsSUFBSSxJQUFJO0NBRXJFLFlBQW1CLFFBQWdCO0VBQy9CLEtBQUssU0FBUztDQUNsQjs7Ozs7Q0FNQSxNQUFhLFNBQXdCO0VBQ2pDLE1BQU0sS0FBSyxnQkFBZ0I7RUFDM0IsTUFBTSxLQUFLLGlCQUFpQjtDQUNo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