UNPKG

mc-server-management

Version:

A library for the Minecraft server management protocol added in 25w35a

1,414 lines (1,391 loc) 67.4 kB
//#region rolldown:runtime var __create = Object.create; var __defProp = Object.defineProperty; var __getOwnPropDesc = Object.getOwnPropertyDescriptor; var __getOwnPropNames = Object.getOwnPropertyNames; var __getProtoOf = Object.getPrototypeOf; var __hasOwnProp = Object.prototype.hasOwnProperty; var __copyProps = (to, from, except, desc) => { if (from && typeof from === "object" || typeof from === "function") for (var keys = __getOwnPropNames(from), i = 0, n = keys.length, key; i < n; i++) { key = keys[i]; if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, { get: ((k) => from[k]).bind(null, key), enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); } return to; }; var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target, mod)); //#endregion let eventemitter3 = require("eventemitter3"); eventemitter3 = __toESM(eventemitter3); let rpc_websockets = require("rpc-websockets"); rpc_websockets = __toESM(rpc_websockets); //#region src/error/InvalidResponseError.ts var InvalidResponseError = class extends Error { /** * The full response received from the server. */ response; /** * The path in the response that was invalid. */ path; constructor(message, response, path) { super("Invalid response from server at '" + path.join(".") + "'. " + message); this.response = response; this.path = path; } }; //#endregion //#region src/error/IncorrectTypeError.ts var IncorrectTypeError = class extends InvalidResponseError { /** * The expected type of the response. */ expectedType; /** * The actual type of the response. */ foundType; /** * Creates a new InvalidResponseError. * @param path The path in the response that was invalid. * @param expectedType The expected type of the response. * @param foundType The actual type of the response. * @param response The full response received from the server. * @internal */ constructor(expectedType, foundType, response, ...path) { super("Expected " + expectedType + ", received " + foundType + ".", response, path); this.expectedType = expectedType; this.foundType = foundType; } }; //#endregion //#region src/server/ServerSettings.ts /** * An API wrapper for the settings of the Minecraft server. */ var ServerSettings = class { #connection; /** * Create an API wrapper for the server settings. Use {@link MinecraftServer.settings}. * @param connection * @internal */ constructor(connection) { this.#connection = connection; } /** * Get whether the server automatically saves the world periodically. * @returns {Promise<boolean>} True if the server automatically saves the world, false otherwise. */ async getAutoSave() { return this.#assertBoolean(await this.#connection.call("minecraft:serversettings/autosave", [])); } /** * Set whether the server automatically saves the world periodically. * @param value True to enable auto-saving, false to disable it. */ async setAutoSave(value) { this.#assertBoolean(await this.#connection.call("minecraft:serversettings/autosave/set", [value])); } /** * Get the current difficulty level of the server. * @returns {Promise<Difficulty>} The current difficulty level. */ async getDifficulty() { return this.#assertString(await this.#connection.call("minecraft:serversettings/difficulty", []), "Difficulty"); } /** * Set the difficulty level of the server. * @param value The difficulty level to set. */ async setDifficulty(value) { this.#assertString(await this.#connection.call("minecraft:serversettings/difficulty/set", [value])); } /** * Get whether the server immediately kicks players when they are removed from the allowlist. * @returns {Promise<boolean>} If true, players are kicked when removed from the allowlist. */ async getEnforceAllowList() { return this.#assertBoolean(await this.#connection.call("minecraft:serversettings/enforce_allowlist", [])); } /** * Set whether the server immediately kicks players when they are removed from the allowlist. * @param value True to enable enforcement, false to disable it. */ async setEnforceAllowList(value) { this.#assertBoolean(await this.#connection.call("minecraft:serversettings/enforce_allowlist/set", [value])); } /** * Get whether the server uses the allow list. * @returns {Promise<boolean>} True if the server uses the allow list, false otherwise. */ async getUseAllowList() { return this.#assertBoolean(await this.#connection.call("minecraft:serversettings/use_allowlist", [])); } /** * Set whether the server uses the allow list. * @param value True to enable the allow list, false to disable it. */ async setUseAllowList(value) { this.#assertBoolean(await this.#connection.call("minecraft:serversettings/use_allowlist/set", [value])); } /** * Get the maximum number of players that can join the server. * @returns {Promise<number>} The maximum number of players. */ async getMaxPlayers() { return this.#assertNumber(await this.#connection.call("minecraft:serversettings/max_players", [])); } /** * Set the maximum number of players that can join the server. * @param value The maximum number of players. */ async setMaxPlayers(value) { this.#assertNumber(await this.#connection.call("minecraft:serversettings/max_players/set", [value])); } /** * Get the number of seconds the server waits before pausing when no players are online. * A non-positive value means the server never pauses. * @returns {Promise<number>} The number of seconds before pausing when empty. */ async getPauseWhenEmptySeconds() { return this.#assertNumber(await this.#connection.call("minecraft:serversettings/pause_when_empty_seconds", [])); } /** * Set the number of seconds the server waits before pausing when no players are online. * A non-positive value means the server never pauses. * @param value The number of seconds before pausing when empty. */ async setPauseWhenEmptySeconds(value) { this.#assertNumber(await this.#connection.call("minecraft:serversettings/pause_when_empty_seconds/set", [value])); } /** * Get the number of minutes a player can be idle before being kicked. * Value 0 means players are never kicked for idling. * @returns {Promise<number>} The number of minutes before kicking idle players. */ async getPlayerIdleTimeout() { return this.#assertNumber(await this.#connection.call("minecraft:serversettings/player_idle_timeout", [])); } /** * Set the number of minutes a player can be idle before being kicked. * Value 0 means players are never kicked for idling. * @param value The number of minutes before kicking idle players. */ async setPlayerIdleTimeout(value) { this.#assertNumber(await this.#connection.call("minecraft:serversettings/player_idle_timeout/set", [value])); } /** * Get whether players are allowed to fly on the server. This only changes the flight detection, it does not * enable flight for players in survival mode without a hacked client. * @returns {Promise<boolean>} True if players are allowed to fly, false otherwise. */ async getAllowFlight() { return this.#assertBoolean(await this.#connection.call("minecraft:serversettings/allow_flight", [])); } /** * Set whether players are allowed to fly on the server. This only changes the flight detection, it does not * enable flight for players in survival mode without a hacked client. * @param value True to allow flying, false to disable it. */ async setAllowFlight(value) { this.#assertBoolean(await this.#connection.call("minecraft:serversettings/allow_flight/set", [value])); } /** * Get the message of the day (MOTD) of the server. * @returns {Promise<string>} The MOTD of the server. */ async getMOTD() { return this.#assertString(await this.#connection.call("minecraft:serversettings/motd", [])); } /** * Set the message of the day (MOTD) of the server. * @param value The MOTD to set. */ async setMOTD(value) { this.#assertString(await this.#connection.call("minecraft:serversettings/motd/set", [value])); } /** * Get the radius around the world spawn point that is protected from non-operator players. * @returns {Promise<number>} The spawn protection radius. */ async getSpawnProtectionRadius() { return this.#assertNumber(await this.#connection.call("minecraft:serversettings/spawn_protection_radius", [])); } /** * Set the radius around the world spawn point that is protected from non-operator players. * @param value The spawn protection radius. */ async setSpawnProtectionRadius(value) { this.#assertNumber(await this.#connection.call("minecraft:serversettings/spawn_protection_radius/set", [value])); } /** * Get whether players are forced to use the server's game mode when they join. * @returns {Promise<boolean>} True if players are forced to use the server's game mode, false otherwise. */ async getForceGameMode() { return this.#assertBoolean(await this.#connection.call("minecraft:serversettings/force_game_mode", [])); } /** * Set whether players are forced to use the server's game mode when they join. * @param value True to force the server's game mode, false to allow players to use their own. */ async setForceGameMode(value) { this.#assertBoolean(await this.#connection.call("minecraft:serversettings/force_game_mode/set", [value])); } /** * Get the default game mode for players when they join the server for the first time. If force game mode is * enabled the game mode will be applied to all players when they join, not just new ones. * @returns {Promise<GameMode>} The default game mode. */ async getGameMode() { return this.#assertString(await this.#connection.call("minecraft:serversettings/game_mode", []), "GameMode"); } /** * Set the default game mode for players when they join the server for the first time. If force game mode is * enabled the game mode will be applied to all players when they join, not just new ones. * @param value The default game mode. */ async setGameMode(value) { this.#assertString(await this.#connection.call("minecraft:serversettings/game_mode/set", [value])); } /** * Get the view distance of the server, in chunks. * This is the distance in chunks that the server sends to players around them. * @returns {Promise<number>} The view distance in chunks. */ async getViewDistance() { return this.#assertNumber(await this.#connection.call("minecraft:serversettings/view_distance", [])); } /** * Set the view distance of the server, in chunks. * This is the distance in chunks that the server sends to players around them. * @param value The view distance in chunks. */ async setViewDistance(value) { this.#assertNumber(await this.#connection.call("minecraft:serversettings/view_distance/set", [value])); } /** * Get the simulation distance of the server, in chunks. * This is the distance in chunks that the server simulates around each player. * @returns {Promise<number>} The simulation distance in chunks. */ async getSimulationDistance() { return this.#assertNumber(await this.#connection.call("minecraft:serversettings/simulation_distance", [])); } /** * Set the simulation distance of the server, in chunks. * This is the distance in chunks that the server simulates around each player. * @param value The simulation distance in chunks. */ async setSimulationDistance(value) { this.#assertNumber(await this.#connection.call("minecraft:serversettings/simulation_distance/set", [value])); } /** * Get whether the server accepts players transferred from other servers. * @returns {Promise<boolean>} True if the server accepts transferred players, false otherwise. */ async getAcceptTransfers() { return this.#assertBoolean(await this.#connection.call("minecraft:serversettings/accept_transfers", [])); } /** * Set whether the server accepts players transferred from other servers. * @param value True to accept transferred players, false to reject them. */ async setAcceptTransfers(value) { this.#assertBoolean(await this.#connection.call("minecraft:serversettings/accept_transfers/set", [value])); } /** * Get the interval in seconds between status heartbeats sent to server management clients. * A value of 0 means no heartbeats are sent. * @returns {Promise<number>} The status heartbeat interval in seconds. */ async getStatusHeartbeatInterval() { return this.#assertNumber(await this.#connection.call("minecraft:serversettings/status_heartbeat_interval", [])); } /** * Set the interval in seconds between status heartbeats sent to server management clients. * A value of 0 means no heartbeats are sent. * @param value The status heartbeat interval in seconds. */ async setStatusHeartbeatInterval(value) { this.#assertNumber(await this.#connection.call("minecraft:serversettings/status_heartbeat_interval/set", [value])); } /** * Get the permission level granted to new operators. * Levels are from 1 to 4, with 4 being the highest. * @returns {Promise<number>} The operator user permission level. */ async getOperatorUserPermissionLevel() { return this.#assertNumber(await this.#connection.call("minecraft:serversettings/operator_user_permission_level", [])); } /** * Set the permission level granted to new operators. * Levels are from 1 to 4, with 4 being the highest. * @param value The operator user permission level. */ async setOperatorUserPermissionLevel(value) { this.#assertNumber(await this.#connection.call("minecraft:serversettings/operator_user_permission_level/set", [value])); } /** * Get whether the server hides the list of online players from the server list. * @returns {Promise<boolean>} True if the server hides the list of online players, false otherwise. */ async getHideOnlinePlayers() { return this.#assertBoolean(await this.#connection.call("minecraft:serversettings/hide_online_players", [])); } /** * Set whether the server hides the list of online players from the server list. * @param value True to hide the list of online players, false to show it. */ async setHideOnlinePlayers(value) { this.#assertBoolean(await this.#connection.call("minecraft:serversettings/hide_online_players/set", [value])); } /** * Get whether the server responds to status requests in the multiplayer server list. * @returns {Promise<boolean>} True if the server responds to status requests, false otherwise. */ async getStatusReplies() { return this.#assertBoolean(await this.#connection.call("minecraft:serversettings/status_replies", [])); } /** * Set whether the server responds to status requests in the multiplayer server list. * @param value True to respond to status requests, false to ignore them. */ async setStatusReplies(value) { this.#assertBoolean(await this.#connection.call("minecraft:serversettings/status_replies/set", [value])); } /** * Get the range in chunks around each player in which entities are updated to the player. This is a percentage * value (100 => 100%) of the default value. Min: 10%, Max: 1000% * @returns {Promise<number>} The entity broadcast range percentage. */ async getEntityBroadcastRange() { return this.#assertNumber(await this.#connection.call("minecraft:serversettings/entity_broadcast_range", [])); } /** * Set the range in chunks around each player in which entities are updated to the player. This is a percentage * value (100 => 100%) of the default value. Min: 10%, Max: 1000% * @param value The entity broadcast range percentage. */ async setEntityBroadcastRange(value) { this.#assertNumber(await this.#connection.call("minecraft:serversettings/entity_broadcast_range/set", [value])); } #assertBoolean(value) { if (typeof value !== "boolean") throw new IncorrectTypeError("boolean", typeof value, value); return value; } #assertString(value, expected) { if (typeof value !== "string") throw new IncorrectTypeError(expected ?? "string", typeof value, value); return value; } #assertNumber(value) { if (typeof value !== "number") throw new IncorrectTypeError("number", typeof value, value); return value; } }; //#endregion //#region src/error/MissingPropertyError.ts var MissingPropertyError = class extends InvalidResponseError { /** * Name of the missing property. */ property; /** * Creates a new MissingPropertyError. * @param property Name of the missing property. * @param response The full response received from the server. * @param path The path in the response that was invalid. * @internal */ constructor(property, response = null, ...path) { super(`Missing required property '${property}'.`, response, path.concat(property)); this.property = property; } }; //#endregion //#region src/schemas/player/Player.ts var Player = class Player { /** * Unique identifier of the player (UUID format). */ id; /** * Username of the player. */ name; /** * Creates a Player instance with the specified UUID. * @param id */ static withId(id) { return new Player().setId(id); } /** * Creates a Player instance with the specified username. * @param name */ static withName(name) { return new Player().setName(name); } /** * Parse a Player instance from a PlayerInput. * @param input * @internal */ static fromInput(input) { if (typeof input === "string") return Player.withName(input); return input; } /** * Parse a list of Player instances from a raw array. * @param data * @param response * @param path * @internal */ static parseList(data, response = data, ...path) { if (!Array.isArray(data)) throw new IncorrectTypeError("array", typeof data, response, ...path); const players = []; for (const [index, entry] of data.entries()) players.push(Player.parse(entry, response, ...path, index.toString())); return players; } /** * Parse a Player instance from a raw object. * @param data Raw object to parse. * @param response Full response received from the server, used for errors. * @param path Path to the data in the original response, used for errors. * @returns Parsed Player instance. * @throws {IncorrectTypeError} If the data is not a valid Player object. * @internal */ static parse(data, response = data, ...path) { if (typeof data !== "object" || data === null) throw new IncorrectTypeError("object", typeof data, response, ...path); if (!("id" in data)) throw new MissingPropertyError("id", response, ...path); if (!("name" in data)) throw new MissingPropertyError("name", response, ...path); if (typeof data.id !== "string") throw new IncorrectTypeError("string", typeof data.id, response, ...path, "id"); if (typeof data.name !== "string") throw new IncorrectTypeError("string", typeof data.name, response, ...path, "name"); return new Player(data.id, data.name); } /** * @param id Unique identifier of the player (UUID format). * @param name Username of the player. * @internal Use static methods {@link withId} or {@link withName} to create instances. */ constructor(id, name) { this.id = id; this.name = name; } /** * Sets the unique identifier of the player. * @param id */ setId(id) { this.id = id; return this; } /** * Sets the username of the player. * @param name */ setName(name) { this.name = name; return this; } }; //#endregion //#region src/player-list/PlayerList.ts var PlayerList = class { #connection; #items; /** * Create a player list API wrapper. Use {@link MinecraftServer.allowlist} or similar methods instead. * @param connection * @internal */ constructor(connection) { this.#connection = connection; } /** * Get the items on the list. If the list is already fetched and force is false, the cached list is returned. * @param force Always request the list from the server, even if it was already fetched. */ async get(force = false) { if (!this.#items || force) await this.callAndParse(); if (!this.#items) throw new Error("Invalid player list."); return this.#items; } /** * Clear the list. * This method updates the cached with the resulting list from the server. Use `get()` to retrieve it. */ async clear() { return this.callAndParse("clear", []); } /** * Add an item to the cached list. Does not update the server. * @param item * @internal */ addItem(item) { this.#items?.push(item); return this; } /** * Remove all items matching this callback from the cached list. Does not update the server. * If the cached list is not available, this method does nothing. * @param filter * @internal */ removeMatching(filter) { if (!this.#items) return this; this.#items = this.#items.filter((item) => !filter(item)); return this; } /** * Call a method on the player list and return the raw response. * @param action Method to call (e.g., 'set', 'add', 'remove', 'clear'). * @param params Parameters to pass to the method. * @returns This PlayerList instance. * @protected */ async call(action, params = []) { let method = this.getName(); if (action) method += "/" + action; return await this.#connection.call(method, params); } /** * Call a method on the player list. Updates the cached list with the resulting list from the server and returns this. * @param action Method to call (e.g., 'set', 'add', 'remove', 'clear'). * @param params Parameters to pass to the method. * @returns This PlayerList instance. * @protected */ async callAndParse(action, params = []) { const result = await this.call(action, params); if (!Array.isArray(result)) throw new IncorrectTypeError("array", typeof result, result); this.#items = this.parseResult(result); return this; } }; //#endregion //#region src/util.ts /** * Parse an optional value. If the input is `undefined` or `null`, `undefined` is returned. * Otherwise, the input is parsed using the provided function. * @param parse * @param input * @internal */ function optional(parse, input) { if (input === void 0 || input === null) return; return parse(input); } /** * Ensure the input is an array. If the input is already an array, it is returned as-is. * @param item * @internal */ function fromItemOrArray(item) { if (Array.isArray(item)) return item; return [item]; } //#endregion //#region src/player-list/AllowList.ts var AllowList = class extends PlayerList { /** * Overwrite the existing allowlist with a set of players. * This method updates the cached with the resulting list from the server. Use `get()` to retrieve it. * @param items list of strings (player names) or Player objects */ async set(items) { return this.callAndParse("set", [fromItemOrArray(items).map(Player.fromInput)]); } /** * Add players to the allowlist. * This method updates the cached with the resulting list from the server. Use `get()` to retrieve it. * @param items list of strings (player names) or Player objects */ async add(items) { return this.callAndParse("add", [fromItemOrArray(items).map(Player.fromInput)]); } /** * Remove players from the allowlist. * This method updates the cached with the resulting list from the server. Use `get()` to retrieve it. * @param items list of strings (player names) or Player objects */ async remove(items) { return this.callAndParse("remove", [fromItemOrArray(items).map(Player.fromInput)]); } getName() { return "minecraft:allowlist"; } parseResult(result) { return Player.parseList(result); } }; //#endregion //#region src/schemas/player/ban/Ban.ts /** * Base class for all ban related classes. */ var Ban = class { /** * Reason for the ban. */ reason; /** * Source of the ban (effectively a comment field). */ source; /** * Expiration date of the ban in ISO 8601 format. If omitted, the ban is permanent. * Use {@link setExpires} to set this field using a Date or bigint. */ expires = null; /** * @param reason reason for the ban * @param source source of the ban * @param expires expiration date of the ban as a Date or string in ISO 8601 format. If omitted, the ban is permanent. */ constructor(reason = null, source = null, expires = null) { this.reason = reason; this.source = source; this.setExpires(expires); } /** * Sets the reason for the ban. * @param reason */ setReason(reason = null) { this.reason = reason; return this; } /** * Sets the source of the ban. * @param source */ setSource(source = null) { this.source = source; return this; } /** * Sets the expiration date of the ban. * @param expires The expiration date as a Date or string in ISO 8601 format. */ setExpires(expires) { if (expires instanceof Date) { if (isNaN(expires.getTime())) throw new Error("Invalid date."); expires = expires.toISOString(); } if (typeof expires != "string" && expires !== null) throw new Error("Expires must be a Date, string in ISO 8601 format or null."); this.expires = expires?.toString() ?? null; return this; } /** * Gets the expiration date of the ban as a Date object. * @returns The expiration date as a Date object, or null if the ban is permanent or the date is invalid. */ getExpiresAsDate() { if (!this.expires) return null; const date = new Date(this.expires); if (isNaN(date.getTime())) return null; return date; } /** * Parse and apply options from a raw response object. * @param data Raw object to parse. * @param path Path to the data in the original response, used for errors. * @param result The original response object, used for errors. * @returns The current UserBan instance. * @throws {IncorrectTypeError} If the data is not a valid options object. * @internal */ parseAndApplyOptions(data, result, ...path) { if ("reason" in data) { if (typeof data.reason !== "string") throw new IncorrectTypeError("string", typeof data.reason, result, ...path, "reason"); this.reason = data.reason; } if ("source" in data) { if (typeof data.source !== "string") throw new IncorrectTypeError("string", typeof data.source, result, ...path, "source"); this.source = data.source; } if ("expires" in data) { if (typeof data.expires !== "string") throw new IncorrectTypeError("string", typeof data.expires, result, ...path, "expires"); this.expires = data.expires; } return this; } }; //#endregion //#region src/schemas/player/ban/IncomingIPBan.ts /** * Request to ban a player by their IP address. */ var IncomingIPBan = class IncomingIPBan extends Ban { /** * IP address to ban. */ ip = null; /** * Connected player who should be banned by their IP address. */ player = null; /** * Creates a new IncomingIPBan instance with the specified IP address. * @param ip */ static withIp(ip) { return new IncomingIPBan(ip); } /** * Creates a new IncomingIPBan instance for the specified player. * If the player is not currently connected to the server, they can't be banned by their IP address. * @param player */ static withConnectedPlayer(player) { return new IncomingIPBan(void 0, player); } /** * Creates a IncomingIPBan instance from a IPBanInput. * @param input * @param reason Default reason if input is not an IncomingIPBan * @param source Default source if input is not an IncomingIPBan * @param expires Default expiry if input is not an IncomingIPBan * @internal */ static fromInput(input, reason, source, expires) { if (input instanceof IncomingIPBan) return input; return (typeof input === "string" ? IncomingIPBan.withIp(input) : IncomingIPBan.withConnectedPlayer(input)).setReason(reason).setSource(source).setExpires(expires); } /** * @param ip IP address to ban * @param player connected player who should be banned by their IP address * @param reason reason for the ban * @param source source of the ban * @param expires expiration date of the ban as a Date or string in ISO 8601 format. If omitted, the ban is permanent. * @internal Use {@link withIp} or {@link withConnectedPlayer} and setters instead. */ constructor(ip = null, player = null, reason = null, source = null, expires = null) { super(reason, source, expires); this.ip = ip; this.player = player; } /** * Sets the IP address to ban. * @param ip */ setIp(ip = null) { this.ip = ip; return this; } /** * Sets the connected player who should be banned by their IP address. * @param player */ setPlayer(player = null) { this.player = player; return this; } }; //#endregion //#region src/schemas/player/ban/IPBan.ts /** * Entry on the IP ban list */ var IPBan = class IPBan extends Ban { /** * Banned ip address. */ ip; /** * Creates a IPBan instance from a IPBanInput. * @param input * @param reason Default reason if input is not an IPBan * @param source Default source if input is not an IPBan * @param expires Default expiry if input is not an IPBan * @internal */ static fromInput(input, reason, source, expires) { if (input instanceof IPBan) return input; return new IPBan(input, reason, source, expires); } /** * Parse an IPBan instance from a raw object. * @param data Raw object to parse. * @param response Full response received from the server, used for errors. * @param path Path to the data in the original response, used for errors. * @returns Parsed IPBan instance. * @throws {IncorrectTypeError} If the data is not a valid IPBan object. * @internal */ static parse(data, response = data, ...path) { if (typeof data !== "object" || data === null) throw new IncorrectTypeError("object", typeof data, response, ...path); if (!("ip" in data)) throw new MissingPropertyError("ip", response, ...path); if (typeof data.ip !== "string") throw new IncorrectTypeError("string", typeof data.ip, response, ...path, `ip`); return new IPBan(data.ip).parseAndApplyOptions(data, response, ...path); } /** * @param ip banned IP address * @param reason reason for the ban * @param source source of the ban * @param expires expiration date of the ban as a Date or string in ISO 8601 format. If omitted, the ban is permanent. */ constructor(ip, reason = null, source = null, expires = null) { super(reason, source, expires); this.ip = ip; } /** * Sets the banned IP address. * @param ip */ setIp(ip) { this.ip = ip; return this; } }; //#endregion //#region src/player-list/IPBanList.ts var IPBanList = class extends PlayerList { /** * Overwrite the existing list with a set of entries. * This method updates the cached with the resulting list from the server. Use `get()` to retrieve it. * @param items * @param reason Default reason if input is not an IPBan * @param source Default source if input is not an IPBan * @param expires Default expiry if input is not an IPBan */ set(items, reason = null, source = null, expires = null) { return this.callAndParse("set", [items.map((input) => IPBan.fromInput(input, reason, source, expires))]); } /** * Ban IP addresses. * This method updates the cached with the resulting list from the server. Use `get()` to retrieve it. * @param items list of ip addresses as strings, IncomingIPBan objects or Player objects of connected players * @param reason Default reason if input is not an IncomingIPBan * @param source Default source if input is not an IncomingIPBan * @param expires Default expiry if input is not an IncomingIPBan */ add(items, reason = null, source = null, expires = null) { return this.callAndParse("add", [fromItemOrArray(items).map((i) => IncomingIPBan.fromInput(i, reason, source, expires))]); } /** * Unban IP addresses. * This method updates the cached with the resulting list from the server. Use `get()` to retrieve it. * @param ips list of IP addresses as strings */ remove(ips) { return this.callAndParse("remove", [fromItemOrArray(ips)]); } getName() { return "minecraft:ip_bans"; } parseResult(result) { const bans = []; for (const [index, entry] of result.entries()) bans.push(IPBan.parse(entry, result, index.toString())); return bans; } }; //#endregion //#region src/schemas/player/ban/UserBan.ts var UserBan = class UserBan extends Ban { /** * Player who should be banned. */ player; /** * Creates a UserBan instance from a UserBanInput. * @param input * @param reason Default reason if input is not a UserBan * @param source Default source if input is not a UserBan * @param expires Default expiry if input is not a UserBan * @internal */ static fromInput(input, reason = null, source = null, expires = null) { if (input instanceof UserBan) return input; return new UserBan(input, reason, source, expires); } /** * Parse an UserBan instance from a raw object. * @param data Raw object to parse. * @param response Full response received from the server, used for errors. * @param path Path to the data in the original response, used for errors. * @returns Parsed UserBan instance. * @throws {IncorrectTypeError} If the data is not a valid UserBan object. * @internal */ static parse(data, response = data, ...path) { if (typeof data !== "object" || data === null) throw new IncorrectTypeError("object", typeof data, response, ...path); if (!("player" in data)) throw new MissingPropertyError("player", response, ...path); return new UserBan(Player.parse(data.player, response, ...path, "player")).parseAndApplyOptions(data, response, ...path); } /** * @param player the player to ban * @param reason reason for the ban * @param source source of the ban * @param expires expiration date of the ban as a Date or string in ISO 8601 format. If omitted, the ban is permanent. */ constructor(player, reason = null, source = null, expires = null) { super(reason, source, expires); this.player = Player.fromInput(player); } /** * Sets the player to ban. * @param player */ setPlayer(player) { this.player = player; return this; } }; //#endregion //#region src/player-list/BanList.ts var BanList = class extends PlayerList { /** * Overwrite the existing list with a set of entries. * This method updates the cached with the resulting list from the server. Use `get()` to retrieve it. * @param items list of strings (player names), Player objects or UserBan objects * @param reason Default reason if input is not a UserBan * @param source Default source if input is not a UserBan * @param expires Default expiry if input is not a UserBan */ set(items, reason = null, source = null, expires = null) { return this.callAndParse("set", [this.fromInputs(items, reason, source, expires)]); } /** * Add items to the list. * This method updates the cached with the resulting list from the server. Use `get()` to retrieve it. * @param items list of strings (player names), Player objects or UserBan objects * @param reason Default reason if input is not a UserBan * @param source Default source if input is not a UserBan * @param expires Default expiry if input is not a UserBan */ add(items, reason = null, source = null, expires = null) { return this.callAndParse("add", [this.fromInputs(items, reason, source, expires)]); } /** * Remove items from the list. * This method updates the cached with the resulting list from the server. Use `get()` to retrieve it. * @param items list of strings (player names) or Player objects */ remove(items) { return this.callAndParse("remove", [fromItemOrArray(items).map(Player.fromInput)]); } getName() { return "minecraft:bans"; } parseResult(result) { const bans = []; for (const [index, entry] of result.entries()) bans.push(UserBan.parse(entry, result, index.toString())); return bans; } /** * Convert input list or item to UserBan instances. * @param inputs input list or item * @param reason Default reason if input is not a UserBan * @param source Default source if input is not a UserBan * @param expires Default expiry if input is not a UserBan * @protected */ fromInputs(inputs, reason, source, expires) { return fromItemOrArray(inputs).map((input) => UserBan.fromInput(input, reason, source, expires)); } }; //#endregion //#region src/schemas/player/KickPlayer.ts /** * Request to kick a player */ var KickPlayer = class { /** * Players to kick. */ player; /** * Message displayed to the player when they are kicked. */ message; /** * @param player players to kick * @param message message displayed to the player when they are kicked */ constructor(player, message) { this.player = player; this.message = message; } }; //#endregion //#region src/schemas/player/Operator.ts var Operator = class Operator { /** * The player who is being made an operator. */ player; /** * Which permissions level the operator has. */ permissionLevel; /** * Whether the operator bypasses the player limit. */ bypassesPlayerLimit; /** * Creates a Operator instance from a OperatorInput. * @param input * @param permissionLevel Default permission level if the input is not an Operator object * @param bypassesPlayerLimit Default bypassesPlayerLimit if the input is not an Operator object * @internal */ static fromInput(input, permissionLevel, bypassesPlayerLimit) { if (input instanceof Operator) return input; return new Operator(input, permissionLevel, bypassesPlayerLimit); } /** * Parse an Operator instance from a raw object. * @param data Raw object to parse. * @param response Full response received from the server, used for errors. * @param path Path to the data in the original response, used for errors. * @returns Parsed Operator instance. * @throws {IncorrectTypeError} If the data is not a valid Operator object. * @internal */ static parse(data, response = data, ...path) { if (typeof data !== "object" || data === null) throw new IncorrectTypeError("object", typeof data, response, ...path); if (!("player" in data)) throw new MissingPropertyError("player", response, ...path); const operator = new Operator(Player.parse(data.player, response, ...path, "player")); if ("permissionLevel" in data) { if (typeof data.permissionLevel !== "number") throw new IncorrectTypeError("number", typeof data.permissionLevel, response, ...path, "permissionLevel"); operator.permissionLevel = data.permissionLevel; } if ("bypassesPlayerLimit" in data) { if (typeof data.bypassesPlayerLimit !== "boolean") throw new IncorrectTypeError("boolean", typeof data.bypassesPlayerLimit, response, ...path, "bypassesPlayerLimit"); operator.bypassesPlayerLimit = data.bypassesPlayerLimit; } return operator; } /** * @param player The player who is being made an operator. * @param permissionLevel Which permissions level the operator has. * @param bypassesPlayerLimit Whether the operator bypasses the player limit. */ constructor(player, permissionLevel, bypassesPlayerLimit) { this.player = Player.fromInput(player); this.permissionLevel = permissionLevel; this.bypassesPlayerLimit = bypassesPlayerLimit; } /** * Sets the player who is being made an operator. * @param player */ setPlayer(player) { this.player = player; return this; } /** * Sets which permissions level the operator has. * @param permissionLevel */ setPermissionLevel(permissionLevel) { this.permissionLevel = permissionLevel; return this; } /** * Sets whether the operator bypasses the player limit. * @param bypassesPlayerLimit */ setBypassesPlayerLimit(bypassesPlayerLimit) { this.bypassesPlayerLimit = bypassesPlayerLimit; return this; } }; //#endregion //#region src/player-list/OperatorList.ts var OperatorList = class extends PlayerList { /** * Overwrite the existing operator list with a set of operators. * This method updates the cached with the resulting list from the server. Use `get()` to retrieve it. * @param items list of strings (player names), Player objects or Operator objects * @param permissionLevel Default permission level if the item is not an Operator object * @param bypassesPlayerLimit Default bypassesPlayerLimit if the item is not an Operator object */ async set(items, permissionLevel, bypassesPlayerLimit) { return this.callAndParse("set", [this.fromInputs(items, permissionLevel, bypassesPlayerLimit)]); } /** * Add players to the operator list. * This method updates the cached with the resulting list from the server. Use `get()` to retrieve it. * @param items list of strings (player names), Player objects or Operator objects * @param permissionLevel Default permission level if the item is not an Operator object * @param bypassesPlayerLimit Default bypassesPlayerLimit if the item is not an Operator object */ async add(items, permissionLevel, bypassesPlayerLimit) { return this.callAndParse("add", [this.fromInputs(items, permissionLevel, bypassesPlayerLimit)]); } /** * Remove players from the operator list. * This method updates the cached with the resulting list from the server. Use `get()` to retrieve it. * @param items list of strings (player names) or Player objects */ async remove(items) { return this.callAndParse("remove", [fromItemOrArray(items).map(Player.fromInput)]); } getName() { return "minecraft:operators"; } fromInputs(inputs, permissionLevel, bypassesPlayerLimit) { return fromItemOrArray(inputs).map((i) => Operator.fromInput(i, permissionLevel, bypassesPlayerLimit)); } parseResult(result) { const ops = []; for (const [index, entry] of result.entries()) ops.push(Operator.parse(entry, result, index.toString())); return ops; } }; //#endregion //#region src/schemas/message/Message.ts var Message = class Message { /** * Literal message to send (non-translatable). */ literal; /** * Translatable message key (e.g. "chat.type.text") */ translatable; /** * Parameters for the translated message (if any). */ translatableParams; /** * Create a literal message. * @param literal */ static literal(literal) { return new Message().setLiteral(literal); } /** * Create a translatable message with optional parameters. * @param translatable translatable message key * @param translatableParams optional parameters for the translated message */ static translatable(translatable, translatableParams = []) { return new Message().setTranslatable(translatable).setTranslatableParams(translatableParams); } /** * Parse a MessageParseable * @param input * @internal */ static fromInput(input) { if (typeof input === "string") return Message.literal(input); return input; } /** * Creates a new Message instance. * @param literal * @param translatable * @param translatableParams * @internal Use static methods {@link Message.literal()} or {@link Message.translatable()} instead. */ constructor(literal, translatable, translatableParams) { this.literal = literal; this.translatable = translatable; this.translatableParams = translatableParams; } /** * Sets the literal message. * @param literal */ setLiteral(literal) { this.literal = literal; return this; } /** * Set the translatable message key. * @param translatable */ setTranslatable(translatable) { this.translatable = translatable; return this; } /** * Add parameters for the translated message. * @param params one or more parameters to add */ addTranslatableParam(params) { this.translatableParams ??= []; if (Array.isArray(params)) this.translatableParams.push(...params); else this.translatableParams.push(params); return this; } /** * Set the parameters for the translated message. * @param translatableParams */ setTranslatableParams(translatableParams) { this.translatableParams = translatableParams; return this; } }; //#endregion //#region src/schemas/message/SystemMessage.ts var SystemMessage = class { /** * The message to send. */ message; /** * Whether to display the message as an overlay above the hotbar (true) or in the chat (false). */ overlay; /** * An optional list of players to receive the message. If omitted, all players will receive the message. */ receivingPlayers; /** * Creates a new SystemMessage instance. * @param message The message to send. * @param overlay Whether to display the message as an overlay above the hotbar (true) or in the chat (false). * @param receivingPlayers An optional list of players to receive the message. If omitted, all players will receive the message. */ constructor(message, receivingPlayers, overlay = false) { this.message = message; this.overlay = overlay; this.receivingPlayers = receivingPlayers; } }; //#endregion //#region src/schemas/gamerule/GameRule.ts var GameRule = class { /** * Key of the game rule (e.g., "doDaylightCycle", "maxEntityCramming"). */ key; /** * Value of the game rule. */ value; constructor(key, value) { this.key = key; this.value = value; } }; //#endregion //#region src/schemas/gamerule/UntypedGameRule.ts var UntypedGameRule = class extends GameRule {}; //#endregion //#region src/server/MinecraftServer.ts /** * This is the main entrypoint for interacting with the Minecraft server management protocol. * It provides methods for retrieving server status, managing players, and accessing various server settings and lists. */ var MinecraftServer = class extends eventemitter3.EventEmitter { #connection; #allowlist; #ipBanList; #banList; #operatorList; #state; #gameRules; /** * This is the main entrypoint for interacting with the server management protocol. * You probably want to use {@link WebSocketConnection.connect} to create a connection. * @param connection The connection to use for communicating with the server. */ constructor(connection) { super(); this.#connection = connection; this.#onConnectionEvent(Notifications_default.SERVER_STARTED, () => this.emit(Notifications_default.SERVER_STARTED)); this.#onConnectionEvent(Notifications_default.SERVER_STOPPING, () => this.emit(Notifications_default.SERVER_STOPPING)); this.#onConnectionEvent(Notifications_default.SERVER_SAVING, () => this.emit(Notifications_default.SERVER_SAVING)); this.#onConnectionEvent(Notifications_default.SERVER_SAVED, () => this.emit(Notifications_default.SERVER_SAVED)); this.#onConnectionEvent(Notifications_default.PLAYER_JOINED, (data) => { const [path, param] = this.#getByNameOrPos(data, "player"); this.emit(Notifications_default.PLAYER_JOINED, Player.parse(param, data, ...path)); }); this.#onConnectionEvent(Notifications_default.PLAYER_LEFT, (data) => { const [path, param] = this.#getByNameOrPos(data, "player"); this.emit(Notifications_default.PLAYER_LEFT, Player.parse(param, data, ...path)); }); this.#onConnectionEvent(Notifications_default.OPERATOR_ADDED, (data) => { const [path, param] = this.#getByNameOrPos(data, "player"); const op = Operator.parse(param, data, ...path); this.emit(Notifications_default.OPERATOR_ADDED, op); this.#operatorList?.addItem(op); }); this.#onConnectionEvent(Notifications_default.OPERATOR_REMOVED, (data) => { const [path, param] = this.#getByNameOrPos(data, "player"); const op = Operator.parse(param, data, ...path); this.emit(Notifications_default.OPERATOR_REMOVED, op); this.#operatorList?.removeMatching((item) => item.player.id === op.player.id); }); this.#onConnectionEvent(Notifications_default.ALLOWLIST_ADDED, (data) => { const [path, param] = this.#getByNameOrPos(data, "player"); const player = Player.parse(param, data, ...path); this.emit(Notifications_default.ALLOWLIST_ADDED, player); this.#allowlist?.addItem(player); }); this.#onConnectionEvent(Notifications_default.ALLOWLIST_REMOVED, (data) => { const [path, param] = this.#getByNameOrPos(data, "player"); const player = Player.parse(param, data, ...path); this.emit(Notifications_default.ALLOWLIST_REMOVED, player); this.#allowlist?.removeMatching((item) => item.id === player.id); }); this.#onConnectionEvent(Notifications_default.IP_BAN_ADDED, (data) => { const [path, param] = this.#getByNameOrPos(data, "player"); const ban = IPBan.parse(param, data, ...path); this.emit(Notifications_default.IP_BAN_ADDED, ban); this.#ipBanList?.addItem(ban); }); this.#onConnectionEvent(Notifications_default.IP_BAN_REMOVED, (data) => { const [path, ip] = this.#getByNameOrPos(data, "player"); if (typeof ip !== "string") throw new IncorrectTypeError("string", typeof ip, data, ...path); this.emit(Notifications_default.IP_BAN_REMOVED, ip); this.#ipBanList?.removeMatching((item) => item.ip === ip); }); this.#onConnectionEvent(Notifications_default.BAN_ADDED, (data) => { const [path, param] = this.#getByNameOrPos(data, "player"); const ban = UserBan.parse(param, data, ...path); this.emit(Notifications_default.BAN_ADDED, ban); this.#banList?.addItem(ban); }); this.#onConnectionEvent(Notifications_default.BAN_REMOVED, (data) => { const [path, param] = this.#getByNameOrPos(data, "player"); const player = Player.parse(param, data, ...path); this.emit(Notifications_default.BAN_REMOVED, player); this.#banList?.removeMatching((item) => item.player.id === player.id); }); this.#onConnectionEvent(Notifications_default.GAME_RULE_UPDATED, (data) => { const [path, param] = this.#getByNameOrPos(data, "gamerule"); const rule = TypedGameRule.parse(param, data, ...path); this.emit(Notifications_default.GAME_RULE_UPDATED, rule); this.#gameRules?.set(rule.key, rule); }); this.#onConnectionEvent(Notifications_default.SERVER_STATUS, (data) => { const [path, param] = this.#getByNameOrPos(data, "status"); this.#state = ServerState.parse(param, data, ...path); this.emit(Notifications_default.SERVER_STATUS, this.#state); }); } /** * Get the current status of the server. * @returns {Promise<ServerState>} The current status of the server. */ async getStatus(force = false) { if (!this.#state || force) { const result = await this.#connection.call("minecraft:server/status", []); this.#state = ServerState.parse(result); } return this.#state; } /** * @returns {Promise<Player[]>} Array of players currently connected to the server. */ async getConnectedPlayers(for