mc-server-management
Version:
A library for the Minecraft server management protocol added in 25w35a
1,414 lines (1,391 loc) • 67.4 kB
JavaScript
//#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