From a66a353de1d4ad754bac49e5ac543c7e3108371b Mon Sep 17 00:00:00 2001 From: FluxxSynth Date: Wed, 17 Jun 2026 17:25:10 -0300 Subject: [PATCH 1/3] feat: Milestone 2 - Mindustry Maps Extended, Pt 1 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - #154: Menu Based Map System — /nextmap GUI with Current/Throwback/Campaigns tabs - #155: Throwback Maps — configurable retired maps, hidden from normal rotation - #156: Campaign Support — Campaign class + config for curated map sequences - #151: Multi-wave opt-out — maps can set no-multi-vnw tag to limit /vnw to 1 wave - Fix: setNextMapOverride always passed null (pre-existing bug) --- build/scripts/achievements.js | 1650 +++---- build/scripts/api.js | 428 +- build/scripts/commands/aggregate.js | 108 +- build/scripts/commands/console.js | 2258 +++++----- build/scripts/commands/general.js | 2988 ++++++------ build/scripts/commands/member.js | 274 +- build/scripts/commands/staff.js | 3168 ++++++------- build/scripts/config.js | 739 +-- build/scripts/files.js | 192 +- build/scripts/fjsContext.js | 202 +- build/scripts/frameworks/commands.js | 44 +- build/scripts/frameworks/commands/commands.js | 1534 +++---- build/scripts/frameworks/commands/errors.js | 40 +- .../scripts/frameworks/commands/formatting.js | 366 +- build/scripts/frameworks/commands/perm.js | 190 +- .../frameworks/commands/requirements.js | 184 +- build/scripts/frameworks/commands/types.js | 58 +- build/scripts/frameworks/io.js | 776 ++-- build/scripts/frameworks/menus.js | 764 ++-- build/scripts/funcs.js | 948 ++-- build/scripts/globals.js | 68 +- build/scripts/index.js | 682 +-- build/scripts/maps.js | 722 +-- build/scripts/metrics.js | 152 +- build/scripts/mindustryTypes.js | 24 +- build/scripts/packetHandlers.js | 562 +-- build/scripts/players.js | 3988 ++++++++--------- build/scripts/promise.js | 272 +- build/scripts/ranks.js | 192 +- build/scripts/timers.js | 328 +- build/scripts/types.js | 12 +- build/scripts/utils.js | 2316 +++++----- build/scripts/votes.js | 436 +- src/commands/general.ts | 129 +- src/config.ts | 18 + src/frameworks/menus.ts | 6 +- src/mindustryTypes.ts | 1 + src/utils.ts | 6 +- 38 files changed, 13541 insertions(+), 13284 deletions(-) diff --git a/build/scripts/achievements.js b/build/scripts/achievements.js index a67f9bff..074d2db7 100644 --- a/build/scripts/achievements.js +++ b/build/scripts/achievements.js @@ -1,825 +1,825 @@ -"use strict"; -var __makeTemplateObject = (this && this.__makeTemplateObject) || function (cooked, raw) { - if (Object.defineProperty) { Object.defineProperty(cooked, "raw", { value: raw }); } else { cooked.raw = raw; } - return cooked; -}; -var __read = (this && this.__read) || function (o, n) { - var m = typeof Symbol === "function" && o[Symbol.iterator]; - if (!m) return o; - var i = m.call(o), r, ar = [], e; - try { - while ((n === void 0 || n-- > 0) && !(r = i.next()).done) ar.push(r.value); - } - catch (error) { e = { error: error }; } - finally { - try { - if (r && !r.done && (m = i["return"])) m.call(i); - } - finally { if (e) throw e.error; } - } - return ar; -}; -var __spreadArray = (this && this.__spreadArray) || function (to, from, pack) { - if (pack || arguments.length === 2) for (var i = 0, l = from.length, ar; i < l; i++) { - if (ar || !(i in from)) { - if (!ar) ar = Array.prototype.slice.call(from, 0, i); - ar[i] = from[i]; - } - } - return to.concat(ar || Array.prototype.slice.call(from)); -}; -var __values = (this && this.__values) || function(o) { - var s = typeof Symbol === "function" && Symbol.iterator, m = s && o[s], i = 0; - if (m) return m.call(o); - if (o && typeof o.length === "number") return { - next: function () { - if (o && i >= o.length) o = void 0; - return { value: o && o[i++], done: !o }; - } - }; - throw new TypeError(s ? "Object is not iterable." : "Symbol.iterator is not defined."); -}; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.Achievements = exports.Achievement = void 0; -var config_1 = require("/config"); -var funcs_1 = require("/funcs"); -var globals_1 = require("/globals"); -var players_1 = require("/players"); -var ranks_1 = require("/ranks"); -var utils_1 = require("/utils"); -//scrap doesn't count -var serpuloItems = [Items.copper, Items.lead, Items.graphite, Items.silicon, Items.metaglass, Items.titanium, Items.plastanium, Items.thorium, Items.surgeAlloy, Items.phaseFabric]; -var erekirItems = [Items.beryllium, Items.graphite, Items.silicon, Items.tungsten, Items.oxide, Items.surgeAlloy, Items.thorium, Items.carbide, Items.phaseFabric]; -var usefulItems10k = { - serpulo: serpuloItems.map(function (i) { return new ItemStack(i, 10000); }), - erekir: erekirItems.map(function (i) { return new ItemStack(i, 10000); }), - sun: __spreadArray(__spreadArray([], __read(serpuloItems), false), __read(erekirItems), false).map(function (i) { return new ItemStack(i, 10000); }), -}; -var allItems1k = Vars.content.items().select(function (i) { return !i.hidden; }).toArray().map(function (i) { return new ItemStack(i, 1000); }); -var mixtechItems = Items.serpuloItems.copy(); -Items.erekirItems.each(function (i) { return mixtechItems.add(i); }); -var Achievement = /** @class */ (function () { - function Achievement(icon, name, description, options) { - var _a; - if (options === void 0) { options = {}; } - this.name = name; - this.notify = "player"; - this.hidden = false; - this.disabled = false; - if (Array.isArray(icon)) { - this.icon = (icon[0].startsWith("[") ? icon[0] : "[".concat(icon[0], "]")) + (typeof icon[1] == "number" ? String.fromCharCode(icon[1]) : icon[1]); - } - else if (typeof icon == "number") { - this.icon = String.fromCharCode(icon); - } - else { - this.icon = icon; - } - if (Array.isArray(description)) { - _a = __read(description, 2), this.description = _a[0], this.extendedDescription = _a[1]; - } - else - this.description = description; - this.nid = Achievement._id++; - Object.assign(this, options); - if (options.modes) { - var _b = __read(options.modes), type = _b[0], modes_1 = _b.slice(1); - if (type == "only") { - this.allowedModes = modes_1; - this.modesText = modes_1.join(", "); - } - else { - this.allowedModes = config_1.GamemodeNames.filter(function (m) { return !modes_1.includes(m); }); - this.modesText = "all except ".concat(modes_1.join(", ")); - } - } - else { - this.allowedModes = config_1.GamemodeNames; - this.modesText = "all"; - } - if (!this.disabled) { - Achievement.all.push(this); - if (this.checkPlayerFrequent || this.checkFrequent) - Achievement.checkFrequent.push(this); - if (this.checkPlayerInfrequent || this.checkInfrequent) - Achievement.checkInfrequent.push(this); - if (this.checkPlayerJoin) - Achievement.checkJoin.push(this); - if (this.checkPlayerGameover || this.checkGameover) - Achievement.checkGameover.push(this); - } - } - Achievement.prototype.message = function () { - return config_1.FColor.achievement(templateObject_1 || (templateObject_1 = __makeTemplateObject(["Achievement granted!\n[accent]", "[white]: ", ""], ["Achievement granted!\\n[accent]", "[white]: ", ""])), this.name, this.description); - }; - Achievement.prototype.messageToEveryone = function (player) { - return config_1.FColor.achievement(templateObject_2 || (templateObject_2 = __makeTemplateObject(["Player ", " has completed the achievement \"", "\"."], ["Player ", " has completed the achievement \"", "\"."])), player.prefixedName, this.name); - }; - Achievement.prototype.allowedInMode = function () { - return this.allowedModes.includes(config_1.Gamemode.name()); - }; - Achievement.prototype.grantToAllOnline = function (team) { - var _this = this; - players_1.FishPlayer.forEachPlayer(function (p) { - if (!_this.has(p) && (!team || p.team() == team)) { - if (_this.notify != "nobody") - p.sendMessage(_this.message()); - _this.setObtained(p); - } - }); - }; - /** Do not call this in a loop on an achievement set to notify everyone. */ - Achievement.prototype.grantTo = function (player, allowRepeatMessage) { - if (allowRepeatMessage === void 0) { allowRepeatMessage = true; } - var has = this.has(player); - if (!has || allowRepeatMessage) { - if (this.notify == "everyone") - Call.sendMessage(this.messageToEveryone(player)); - else if (this.notify == "player") - player.sendMessage(this.message()); - } - if (!has) - this.setObtained(player); - }; - Achievement.prototype.setObtained = function (player) { - //void player.updateSynced(fishP => fishP.achievements.set(this.nid)); - player.achievements.set(this.nid); - }; - Achievement.prototype.has = function (player) { - return player.achievements.get(this.nid); - }; - Achievement.all = []; - /** Checked every second. */ - Achievement.checkFrequent = []; - /** Checked every 10 seconds. Use for states that can be gained but not lost, such as "x wins". */ - Achievement.checkInfrequent = []; - Achievement.checkJoin = []; - Achievement.checkGameover = []; - Achievement._id = 0; - return Achievement; -}()); -exports.Achievement = Achievement; -Events.on(EventType.PlayerJoin, function (_a) { - var e_1, _b; - var _c; - var player = _a.player; - Time.mark(); - var _loop_1 = function (ach) { - if (ach.allowedInMode()) { - var fishP_1 = players_1.FishPlayer.get(player); - if (!ach.has(fishP_1) && ((_c = ach.checkPlayerJoin) === null || _c === void 0 ? void 0 : _c.call(ach, fishP_1))) { - if (fishP_1.dataSynced) - ach.grantTo(fishP_1); - else - Timer.schedule(function () { return ach.grantTo(fishP_1); }, 2); //2 seconds should be enough - } - } - }; - try { - for (var _d = __values(Achievement.checkJoin), _e = _d.next(); !_e.done; _e = _d.next()) { - var ach = _e.value; - _loop_1(ach); - } - } - catch (e_1_1) { e_1 = { error: e_1_1 }; } - finally { - try { - if (_e && !_e.done && (_b = _d.return)) _b.call(_d); - } - finally { if (e_1) throw e_1.error; } - } - Log.debug("ach join @", Time.elapsed()); -}); -globals_1.FishEvents.on("gameOver", function (_, winner) { - var e_2, _a; - var _b; - Time.mark(); - var _loop_2 = function (ach) { - if (ach.allowedInMode()) { - if ((_b = ach.checkGameover) === null || _b === void 0 ? void 0 : _b.call(ach, winner)) - ach.grantToAllOnline(); - else - players_1.FishPlayer.forEachPlayer(function (fishP) { - var _a; - if (!ach.has(fishP) && ((_a = ach.checkPlayerGameover) === null || _a === void 0 ? void 0 : _a.call(ach, fishP, winner))) { - ach.grantTo(fishP); - } - }); - } - }; - try { - for (var _c = __values(Achievement.checkGameover), _d = _c.next(); !_d.done; _d = _c.next()) { - var ach = _d.value; - _loop_2(ach); - } - } - catch (e_2_1) { e_2 = { error: e_2_1 }; } - finally { - try { - if (_d && !_d.done && (_a = _c.return)) _a.call(_c); - } - finally { if (e_2) throw e_2.error; } - } - Log.debug("ach gameover @", Time.elapsed()); -}); -Timer.schedule(function () { - var e_3, _a; - Time.mark(); - var _loop_3 = function (ach) { - if (ach.allowedInMode()) { - if (ach.checkFrequent) { - if (config_1.Gamemode.pvp()) { - Vars.state.teams.active.each(function (_a) { - var team = _a.team; - if (ach.checkFrequent(team)) - ach.grantToAllOnline(team); - }); - } - else { - if (ach.checkFrequent(Vars.state.rules.defaultTeam)) - ach.grantToAllOnline(); - } - } - else { - players_1.FishPlayer.forEachPlayer(function (fishP) { - var _a; - if (!ach.has(fishP) && ((_a = ach.checkPlayerFrequent) === null || _a === void 0 ? void 0 : _a.call(ach, fishP))) - ach.grantTo(fishP); - }); - } - } - }; - try { - for (var _b = __values(Achievement.checkFrequent), _c = _b.next(); !_c.done; _c = _b.next()) { - var ach = _c.value; - _loop_3(ach); - } - } - catch (e_3_1) { e_3 = { error: e_3_1 }; } - finally { - try { - if (_c && !_c.done && (_a = _b.return)) _a.call(_b); - } - finally { if (e_3) throw e_3.error; } - } - Log.debug("ach frequent @", Time.elapsed()); -}, 1, 1); -Timer.schedule(function () { - var e_4, _a; - Time.mark(); - var _loop_4 = function (ach) { - if (ach.allowedInMode()) { - if (ach.checkInfrequent) { - if (config_1.Gamemode.pvp()) { - Vars.state.teams.active.each(function (_a) { - var team = _a.team; - if (ach.checkInfrequent(team)) - ach.grantToAllOnline(team); - }); - } - else { - if (ach.checkInfrequent(Vars.state.rules.defaultTeam)) - ach.grantToAllOnline(); - } - } - else { - players_1.FishPlayer.forEachPlayer(function (fishP) { - var _a; - if (!ach.has(fishP) && ((_a = ach.checkPlayerInfrequent) === null || _a === void 0 ? void 0 : _a.call(ach, fishP))) - ach.grantTo(fishP); - }); - } - } - }; - try { - for (var _b = __values(Achievement.checkInfrequent), _c = _b.next(); !_c.done; _c = _b.next()) { - var ach = _c.value; - _loop_4(ach); - } - } - catch (e_4_1) { e_4 = { error: e_4_1 }; } - finally { - try { - if (_c && !_c.done && (_a = _b.return)) _a.call(_b); - } - finally { if (e_4) throw e_4.error; } - } - Log.debug("ach infrequent @", Time.elapsed()); -}, 10, 10); -exports.Achievements = { - // =========================== - // ╦ ╦ ╔═╗ ╦═╗ ╔╗╔ ╦ ╔╗╔ ╔═╗ ┬ - // ║║║ ╠═╣ ╠╦╝ ║║║ ║ ║║║ ║ ╦ │ - // ╚╩╝ ╩ ╩ ╩╚═ ╝╚╝ ╩ ╝╚╝ ╚═╝ o - // =========================== - // Do not change the order of any achievements. - // Do not remove any achievements: instead, set the "disabled" option to true. - // Reordering achievements will cause ID shifts. - //Joining based - welcome: new Achievement(["gold", Iconc.infoCircle], "Welcome", "Join the server.", { - checkPlayerJoin: function () { return true; }, - notify: "nobody" - }), - migratory_fish: new Achievement(Iconc.exit, "Migratory Fish", "Join all of our servers.", { - disabled: true - }), //TODO - frequent_visitor: new Achievement(Iconc.planeOutline, "Frequent Visitor", ["Join the server 100 times.", "Note: Do not reconnect frequently, that will not work. This achievement requires that you have been playing for 1 month."], { - checkPlayerJoin: function (p) { return p.info().timesJoined >= 100 && (Date.now() - p.globalFirstJoined > funcs_1.Duration.months(1)); } - }), - //Gamemode based - attack: new Achievement(Iconc.modeAttack, "Attack", ["Defeat an attack map.", "You must be present for the beginning and end of the game."], { - modes: ["only", "attack"], - checkPlayerGameover: function (player, winTeam) { - return Vars.state.rules.defaultTeam == winTeam && player.tstats.lastMapStartTime == players_1.FishPlayer.lastMapStartTime; - }, - }), - survival: new Achievement(Iconc.modeSurvival, "Survival", ["Survive 50 waves in a survival map.", "Must be during the same game."], { - modes: ["only", "survival"], - checkPlayerInfrequent: function (player) { - return player.tstats.wavesSurvived >= 50; - }, - }), - pvp: new Achievement(Iconc.modePvp, "PVP", ["Win a match of PVP.", "You must be present for the beginning and end of the game."], { - modes: ["only", "pvp"], - checkPlayerGameover: function (player, winTeam) { - return player.team() == winTeam && player.tstats.lastMapStartTime == players_1.FishPlayer.lastMapStartTime; - }, - }), - sandbox: new Achievement(Iconc.image, "Sandbox", "Spend 1 hour in Sandbox.", { - modes: ["only", "sandbox"], - checkPlayerInfrequent: function (p) { return p.stats.timeInGame > funcs_1.Duration.hours(1); }, - }), - hexed: new Achievement(Iconc.layers, "Hexed", ["Play a match of Hexed.", "You must be present for the beginning and end of the game."], { - modes: ["only", "hexed"], - checkPlayerGameover: function (player) { - return player.tstats.lastMapStartTime == players_1.FishPlayer.lastMapStartTime; - }, - }), - minigame: new Achievement(Iconc.play, "Minigame", ["Win a Minigame.", "You must be present for the beginning and end of the game."], { - modes: ["only", "minigame"], - checkPlayerGameover: function (player, winTeam) { - return player.team() == winTeam && player.tstats.lastMapStartTime == players_1.FishPlayer.lastMapStartTime; - }, - }), - //playtime based - playtime_1: new Achievement(["white", Iconc.googleplay], "Playtime 1", "Spend 1 hour in-game.", { - checkPlayerInfrequent: function (p) { return p.globalStats.timeInGame >= funcs_1.Duration.hours(1); } - }), - playtime_2: new Achievement(["red", Iconc.googleplay], "Playtime 2", "Spend 12 hours in-game.", { - checkPlayerInfrequent: function (p) { return p.globalStats.timeInGame >= funcs_1.Duration.hours(12); } - }), - playtime_3: new Achievement(["orange", Iconc.googleplay], "Playtime 3", "Spend 2 days in-game.", { - checkPlayerInfrequent: function (p) { return p.globalStats.timeInGame >= funcs_1.Duration.days(2); } - }), - playtime_4: new Achievement(["yellow", Iconc.googleplay], "Playtime 4", "Spend 10 days in-game.", { - checkPlayerInfrequent: function (p) { return p.globalStats.timeInGame >= funcs_1.Duration.days(10); } - }), - //victories based - victory_1: new Achievement(["white", Iconc.star], "First Victory", "Win a map run.", { - checkPlayerGameover: function (p) { return p.globalStats.gamesWon >= 1; } - }), - victory_2: new Achievement(["red", Iconc.star], "Victories 2", "Win 5 map runs.", { - checkPlayerGameover: function (p) { return p.globalStats.gamesWon >= 5; } - }), - victory_3: new Achievement(["orange", Iconc.star], "Victories 3", "Win 30 map runs.", { - checkPlayerGameover: function (p) { return p.globalStats.gamesWon >= 30; } - }), - victory_4: new Achievement(["yellow", Iconc.star], "Victories 4", "Win 100 map runs.", { - checkPlayerGameover: function (p) { return p.globalStats.gamesWon >= 100; }, - notify: "everyone" - }), - //games based - games_1: new Achievement(["white", Iconc.itchio], "Games 1", "Play 10 map runs.", { - checkPlayerGameover: function (p) { return p.globalStats.gamesFinished >= 10; } - }), - games_2: new Achievement(["red", Iconc.itchio], "Games 2", "Play 40 map runs.", { - checkPlayerGameover: function (p) { return p.globalStats.gamesFinished >= 40; } - }), - games_3: new Achievement(["orange", Iconc.itchio], "Games 3", "Play 100 map runs.", { - checkPlayerGameover: function (p) { return p.globalStats.gamesFinished >= 100; } - }), - games_4: new Achievement(["yellow", Iconc.itchio], "Games 4", "Play 200 map runs.", { - checkPlayerGameover: function (p) { return p.globalStats.gamesFinished >= 200; }, - notify: "everyone" - }), - //messages based - messages_1: new Achievement(["white", Iconc.chat], "Hello", "Send your first chat message.", { - checkPlayerInfrequent: function (p) { return p.globalStats.chatMessagesSent >= 1; }, - notify: "nobody" - }), - messages_2: new Achievement(["red", Iconc.chat], "Chat 2", ["Send 100 chat messages.", "Warning: you will be kicked if you spam the chat."], { - checkPlayerInfrequent: function (p) { return p.globalStats.chatMessagesSent >= 100; } - }), - messages_3: new Achievement(["orange", Iconc.chat], "Chat 3", ["Send 500 chat messages.", "Warning: you will be kicked if you spam the chat."], { - checkPlayerInfrequent: function (p) { return p.globalStats.chatMessagesSent >= 500; } - }), - messages_4: new Achievement(["yellow", Iconc.chat], "Chat 4", ["Send 2000 chat messages.", "Warning: you will be kicked if you spam the chat."], { - checkPlayerInfrequent: function (p) { return p.globalStats.chatMessagesSent >= 2000; } - }), - messages_5: new Achievement(["lime", Iconc.chat], "Chat 4", ["Send 5000 chat messages.", "Warning: you will be kicked if you spam the chat."], { - checkPlayerInfrequent: function (p) { return p.globalStats.chatMessagesSent >= 5000; }, - notify: "everyone" - }), - //blocks built based - builds_1: new Achievement(["white", Iconc.fileText], "The Factory Must Prepare", "Construct 1 buildings.", { - checkPlayerInfrequent: function (p) { return p.globalStats.blocksPlaced >= 1; }, - notify: "nobody" - }), - builds_2: new Achievement(["red", Iconc.fileText], "The Factory Must Begin", "Construct 200 buildings.", { - checkPlayerInfrequent: function (p) { return p.globalStats.blocksPlaced > 200; } - }), - builds_3: new Achievement(["orange", Iconc.fileText], "The Factory Must Produce", "Construct 1000 buildings.", { - checkPlayerInfrequent: function (p) { return p.globalStats.blocksPlaced > 1000; } - }), - builds_4: new Achievement(["yellow", Iconc.fileText], "The Factory Must Grow", "Construct 5000 buildings.", { - checkPlayerInfrequent: function (p) { return p.globalStats.blocksPlaced > 5000; }, - }), - //units - t5: new Achievement(Blocks.tetrativeReconstructor.emoji(), "T5", "Control a T5 unit.", { - modes: ["not", "sandbox"], - checkPlayerFrequent: function (player) { - var _a; - return globals_1.unitsT5.includes((_a = player.unit()) === null || _a === void 0 ? void 0 : _a.type); - }, - }), - dibs: new Achievement(["green", Blocks.tetrativeReconstructor.emoji()], "Dibs", "Be the first player to control the first T5 unit made by a reconstructor that you placed.", { - modes: ["not", "sandbox"], - disabled: true - }), //TODO - worm: new Achievement(UnitTypes.latum.emoji(), "Worm", "Control a Latum.", { - checkPlayerFrequent: function (player) { - var _a; - return ((_a = player.unit()) === null || _a === void 0 ? void 0 : _a.type) == UnitTypes.latum; - } - }), - //pvp - above_average: new Achievement(Iconc.chartBar, "Above Average", ["Reach a win rate above 50%.", "Must be over at least 20 games of PVP."], { - modes: ["only", "pvp"], - checkPlayerInfrequent: function (p) { return p.stats.gamesWon / p.stats.gamesFinished > 0.5 && p.stats.gamesFinished >= 20; } - }), - head_start: new Achievement(Iconc.commandAttack, "Head Start", ["Win a match of PVP where your opponents have a 5 minute head start.", "Your team must wait for the first 5 minutes without building or descontructing any buildings."], { - modes: ["only", "pvp"], - disabled: true - }), //TODO - one_v_two: new Achievement(["red", Iconc.modePvp], "1v2", "Defeat two (or more) opponents in PVP without help from other players.", { - modes: ["only", "pvp"], - disabled: true - }), //TODO - //sandbox - underpowered: new Achievement(["red", Blocks.powerSource.emoji()], "Underpowered", "Overload a power source.", { - modes: ["only", "sandbox"], - checkFrequent: function () { - var found = false; - //deliberate ordering for performance reasons - Groups.powerGraph.each(function (_a) { - var graph = _a.graph; - //we don't need to actually check for power sources, just assume that ~1mil power is a source - if (graph.lastPowerNeeded > graph.lastPowerProduced && graph.lastPowerNeeded < 1e10 && (graph.lastPowerProduced / Time.delta * 60) >= 999900) - found = true; - }); - return found; - } - }), - //easter eggs - memory_corruption: new Achievement(["red", Iconc.host], "Is the server OK?", "Witness a memory corruption.", { - notify: "nobody" - }), - run_js_without_perms: new Achievement(["yellow", Iconc.warning], "XKCD 838", ["Receive a warning from the server that an incident will be reported.", "One of the admin commands has a custom error message."], { - notify: "everyone" - }), - script_kiddie: new Achievement(["red", Iconc.warning], "Script Kiddie", ["Pretend to be a hacker. The server will disagree.", "Change your name to something including \"hacker\"."], { - notify: "nobody" - }), - hacker: new Achievement(["lightgray", Iconc.host], "Hacker", "Find a bug in the server and report it responsibly.", { - hidden: true - }), - //items based - items_10k: new Achievement(["green", Iconc.distribution], "Cornucopia", "Obtain 10k of every useful resource.", { - modes: ["not", "sandbox"], - checkPlayerFrequent: function (player) { - var _a; - if (!Vars.state.planet) - return false; - return ((_a = player.team().items()) === null || _a === void 0 ? void 0 : _a.has(usefulItems10k[Vars.state.planet.name])) || false; - }, - }), - fullVault: new Achievement(["green", Blocks.vault.emoji()], "Well Stocked", ["Fill a vault with every obtainable item.", "Requires mixtech."], { - modes: ["not", "sandbox"], - checkInfrequent: function (team) { - return Vars.indexer.getFlagged(team, BlockFlag.storage).contains(boolf(function (b) { return b.block == Blocks.vault && b.items.has(allItems1k) && b.linkedCore == null; })); - }, - }), - full_core: new Achievement(["green", Blocks.coreAcropolis.emoji()], "Multiblock Incinerator", "Completely fill the core with all obtainable items on a map with core incineration enabled.", { - modes: ["not", "sandbox"], - checkFrequent: function (team) { - var _a; - if (!Vars.state.planet) - return false; - var items; - switch (Vars.state.planet.name) { - case "serpulo": - items = Items.serpuloItems; - break; - case "erekir": - items = Items.erekirItems; - break; - case "sun": - items = mixtechItems; - break; - } - var capacity = (_a = team.core()) === null || _a === void 0 ? void 0 : _a.storageCapacity; - if (!capacity) - return false; - var module = team.items(); - return items.allMatch(function (i) { return module.has(i, capacity); }); - }, - }), - siligone: new Achievement(["red", Items.silicon.emoji()], "Siligone", ["Run out of silicon.", "You must have reached 2000 silicon before running out."], { - modes: ["not", "sandbox"] - }), - silicon_100k: new Achievement(["green", Items.silicon.emoji()], "Silicon for days", "Obtain 100k silicon.", { - modes: ["not", "sandbox"], - checkFrequent: function (team) { return team.items().has(Items.silicon, 100000); } - }), - //other players based - alone: new Achievement(["red", Iconc.players], "Alone", "Be the only player online for more than two minutes", { - notify: "nobody" - }), - join_playercount_20: new Achievement(["lime", Iconc.players], "Is there enough room?", "Join a server with 20 players online", { - checkPlayerJoin: function () { return Groups.player.size() > 20; }, - }), - meet_staff: new Achievement(["lime", Iconc.hammer], "Griefer Beware", "Meet a staff member in-game", { - checkPlayerJoin: function () { return Groups.player.contains(function (p) { return players_1.FishPlayer.get(p).ranksAtLeast("mod"); }); }, - }), - meet_fish: new Achievement(["blue", Iconc.admin], "The Big Fish", "Meet >|||>Fish himself in-game", { - checkPlayerJoin: function () { return Groups.player.contains(function (p) { return players_1.FishPlayer.get(p).ranksAtLeast("fish"); }); }, - hidden: true, - }), - server_speak: new Achievement(["pink", Iconc.host], "It Speaks!", "Hear the server talk in chat."), - see_marked_griefer: new Achievement(["red", Iconc.hammer], "Flying Tonk", "See a marked griefer in-game.", { - checkInfrequent: function () { return Groups.player.contains(function (p) { return players_1.FishPlayer.get(p).marked(); }); }, - }), - //maps based - beat_map_not_in_rotation: new Achievement(["pink", Iconc.map], "How?", "Beat a map that isn't in the list of maps.", { - notify: "everyone", - modes: ["not", "pvp"], - checkGameover: function (team) { return team == Vars.state.rules.defaultTeam && !Vars.state.map.custom; } - }), - //misc - power_1mil: new Achievement(["green", Blocks.powerSource.emoji()], "Who needs sources?", "Reach a power production of 1 million without using power sources.", { - modes: ["not", "sandbox"], - checkFrequent: function (team) { - var found = false; - //deliberate ordering for performance reasons - Groups.powerGraph.each(function (_a) { - var _b; - var graph = _a.graph; - //we need to actually check for power sources - if ((graph.lastPowerProduced / Time.delta * 60) > 1e6 && - !graph.producers.contains(boolf(function (b) { return b.block == Blocks.powerSource; })) && - ((_b = graph.producers.firstOpt()) === null || _b === void 0 ? void 0 : _b.team) == team) - found = true; - }); - return found; - } - }), - pacifist_crawler: new Achievement(UnitTypes.crawler.emoji(), "Pacifist Crawler", "Control a crawler for 15 minutes without exploding.", { - modes: ["not", "sandbox"], - disabled: true - }), //TODO - core_low_hp: new Achievement(["yellow", Blocks.coreNucleus.emoji()], "Close Call", "Have your core reach less than 50 health, but survive.", { - modes: ["not", "sandbox"], - }), - enemy_core_low_hp: new Achievement(["red", Blocks.coreNucleus.emoji()], "So Close", "Cause the enemy core to reach less than 50 health, but survive.", { - modes: ["not", "sandbox"], - }), - verified: new Achievement([ranks_1.Rank.active.color, Iconc.ok], "Verified", "Be promoted automatically to ".concat(ranks_1.Rank.active.coloredName(), " rank."), { - checkPlayerJoin: function (p) { return p.ranksAtLeast("active"); }, notify: "nobody" - }), - click_me: new Achievement(Iconc.bookOpen, "Clicked", "Run /achievementgrid and click this achievement."), - afk: new Achievement(["yellow", Iconc.lock], "AFK?", "Win a game without interacting with any blocks.", { - modes: ["not", "sandbox"], - checkPlayerGameover: function (player, winTeam) { - return player.team() == winTeam && player.tstats.blockInteractionsThisMap == 0; - }, - }), - status_effects_5: new Achievement(StatusEffects.electrified.emoji(), "A Furious Cocktail", "Have at least 5 status effects at once.", { - checkPlayerFrequent: function (p) { - var unit = p.unit(); - if (!unit) - return false; - var statuses = (0, utils_1.getStatuses)(unit); - return statuses.size >= 5; - }, - modes: ["not", "sandbox"] - }), - drown_big_tank: new Achievement(["blue", UnitTypes.conquer.emoji()], "Not Waterproof", "Drown an enemy Conquer or Vanquish.", { - notify: "everyone", - modes: ["not", "sandbox"] - }), - drown_mace_in_cryo: new Achievement(["cyan", UnitTypes.mace.emoji()], "Cooldown", "Drown a Mace in ".concat(Blocks.cryofluid.emoji(), " Cryofluid."), { - notify: "everyone", - modes: ["not", "sandbox"] - }), - max_boost_duo: new Achievement(["yellow", Blocks.duo.emoji()], "In Duo We Trust", "Control a Duo with maximum boosts.", { - checkPlayerFrequent: function (player) { - var _a, _b; - var tile = (_b = (_a = player.unit()) === null || _a === void 0 ? void 0 : _a.tile) === null || _b === void 0 ? void 0 : _b.call(_a); - if (!tile) - return false; - return tile.block == Blocks.duo && !tile.ammo.isEmpty() && tile.ammo.peek().item == Items.silicon && tile.liquids.current() == Liquids.cryofluid && tile.timeScale() >= 2.5; - }, - notify: "everyone", - modes: ["not", "sandbox"] - }), - foreshadow_overkill: new Achievement(["yellow", Blocks.foreshadow.emoji()], "Overkill", ["Kill a Dagger with a maximally boosted Foreshadow.", "Hint: the maximum overdrive is not +150%..."], { - notify: "everyone", - modes: ["not", "sandbox"] - }), - impacts_15: new Achievement(["green", Blocks.impactReactor.emoji()], "Darthscion's Nightmare", "Run 15 impact reactors at full efficiency.", { - modes: ["not", "sandbox"], - notify: "everyone", - checkInfrequent: function (team) { - var found = false; - //deliberate ordering for performance reasons - Groups.powerGraph.each(function (_a) { - var graph = _a.graph; - if (graph.producers.size >= 15 && graph.producers.count(function (b) { return b.block == Blocks.impactReactor && b.warmup > 0.99999; }) > 15 && graph.producers.first().team == team) - found = true; - }); - return found; - }, - }), - help_help: new Achievement(["brown", Iconc.info], "Help with help", "Run /help help", { - notify: "everyone" - }), - ohno: new Achievement(["scarlet", UnitTypes.alpha.emoji()], "Oh no", "Control an ohno unit."), - sniper_duel: new Achievement(["yellow", UnitTypes.omura.emoji()], "Sniper duel", "Kill a Foreshadow with an Omura from outside its range."), - around_the_world: new Achievement(Iconc.planet, "Around the World", "Fly your unit around the entire map without entering it, starting from the lower left.", { - notify: "everyone", - }), -}; -Object.entries(exports.Achievements).forEach(function (_a) { - var _b = __read(_a, 2), id = _b[0], a = _b[1]; - return a.sid = id; -}); -globals_1.FishEvents.on("commandUnauthorized", function (_, player, name) { - if ((name == "js" || name == "fjs") && !exports.Achievements.run_js_without_perms.has(player)) - exports.Achievements.run_js_without_perms.grantTo(player); -}); -Events.on(EventType.UnitDrownEvent, function (_a) { - var _b; - var unit = _a.unit; - if (!config_1.Gamemode.sandbox()) { - if (unit.type == UnitTypes.mace && ((_b = unit.tileOn()) === null || _b === void 0 ? void 0 : _b.floor()) == Blocks.cryofluid) - exports.Achievements.drown_mace_in_cryo.grantToAllOnline(); - else if (unit.type == UnitTypes.conquer || unit.type == UnitTypes.vanquish) { - if (config_1.Gamemode.pvp()) { - Vars.state.teams.active.map(function (t) { return t.team; }).select(function (t) { return t !== unit.team; }).each(function (t) { return exports.Achievements.drown_big_tank.grantToAllOnline(t); }); - } - else { - if (unit.team !== Vars.state.rules.defaultTeam) - exports.Achievements.drown_big_tank.grantToAllOnline(); - } - } - } -}); -Events.on(EventType.UnitBulletDestroyEvent, function (_a) { - var _b; - var unit = _a.unit, bullet = _a.bullet; - if (!config_1.Gamemode.sandbox() && unit.type == UnitTypes.dagger && ((_b = bullet.owner) === null || _b === void 0 ? void 0 : _b.block) == Blocks.foreshadow) { - var build = bullet.owner; - if (build.liquids.current() == Liquids.cryofluid && build.timeScale() >= 3) - exports.Achievements.foreshadow_overkill.grantToAllOnline(build.team); - } -}); -Events.on(EventType.BuildingBulletDestroyEvent, function (_a) { - var _b; - var build = _a.build, bullet = _a.bullet; - if (!config_1.Gamemode.sandbox() && build.block == Blocks.foreshadow && ((_b = bullet.owner) === null || _b === void 0 ? void 0 : _b.type) == UnitTypes.omura) { - var unit = bullet.owner; - var player = unit.getPlayer(); - if (player && !unit.within(build, build.range() + unit.hitSize / 2)) - exports.Achievements.sniper_duel.grantTo(players_1.FishPlayer.get(player)); - } -}); -var siliconReached = Team.all.map(function (_) { return false; }); -Events.on(EventType.GameOverEvent, function () { return siliconReached = Team.all.map(function (_) { return false; }); }); -var isAlone = 0; -Timer.schedule(function () { - if (!Vars.state.gameOver && !config_1.Gamemode.sandbox()) { - Vars.state.teams.active.each(function (_a) { - var team = _a.team; - if (team.items().has(Items.silicon, 2000)) - siliconReached[team.id] = true; - else if (siliconReached[team.id] && team.items().get(Items.silicon) == 0) - exports.Achievements.siligone.grantToAllOnline(team); - }); - } - if (Groups.player.size() == 1) { - if (isAlone == 0) - isAlone = Date.now(); - else if (Date.now() > isAlone + funcs_1.Duration.minutes(2)) - exports.Achievements.alone.grantToAllOnline(); - } - else - isAlone = 0; -}, 2, 2); -var coreHealthTime = new Map(); -if (!config_1.Gamemode.sandbox()) - Timer.schedule(function () { - coreHealthTime.forEach(function (value, core) { - if (Date.now() > value) { - if (core.dead) { - coreHealthTime.delete(core); - } - else if (core.health > 50) { - //grant achievement - exports.Achievements.core_low_hp.grantToAllOnline(core.team); - players_1.FishPlayer.forEachPlayer(function (p) { - if (core.team != p.team() && !exports.Achievements.enemy_core_low_hp.has(p)) - exports.Achievements.enemy_core_low_hp.grantTo(p); - }); - coreHealthTime.delete(core); - } - } - }); - Vars.state.teams.active.flatMap(function (t) { return t.cores; }).each(function (core) { - if (core.health < 50 && !coreHealthTime.get(core)) - coreHealthTime.set(core, Date.now() + 12000); - }); - }, 1, 1); -var aroundTheWorld = {}; -Timer.schedule(function () { - var e_5, _a; - players_1.FishPlayer.forEachPlayer(function (p) { - var _a; - var _b; - var unit = p.unit(); - if (unit && unit.x < 0 && unit.y < 0) { - (_a = aroundTheWorld[_b = p.uuid]) !== null && _a !== void 0 ? _a : (aroundTheWorld[_b] = { player: p, unit: unit, side: "left" }); - } - }); - var _loop_5 = function (uuid, entry) { - if (!(function () { - if (entry.unit.dead) - return false; - var left = entry.unit.x < 0; - var bottom = entry.unit.y < 0; - var right = entry.unit.x > (Vars.world.width() - 1) * 8; - var top = entry.unit.y > (Vars.world.height() - 1) * 8; - switch (entry.side) { - case "left": - if (!left) - return false; - if (top) - entry.side = "top"; - break; - case "top": - if (!top) - return false; - if (right) - entry.side = "right"; - break; - case "right": - if (!right) - return false; - if (bottom) - entry.side = "bottom"; - break; - case "bottom": - if (!bottom) - return false; - if (left) { - exports.Achievements.around_the_world.grantTo(entry.player, true); - return false; - } - break; - } - return true; - })()) - delete aroundTheWorld[uuid]; - }; - try { - for (var _b = __values(Object.entries(aroundTheWorld)), _c = _b.next(); !_c.done; _c = _b.next()) { - var _d = __read(_c.value, 2), uuid = _d[0], entry = _d[1]; - _loop_5(uuid, entry); - } - } - catch (e_5_1) { e_5 = { error: e_5_1 }; } - finally { - try { - if (_c && !_c.done && (_a = _b.return)) _a.call(_b); - } - finally { if (e_5) throw e_5.error; } - } -}, 1, 0.5); -Events.on(EventType.GameOverEvent, function () { return coreHealthTime.clear(); }); -Events.on(EventType.WorldLoadEvent, function () { return coreHealthTime.clear(); }); -globals_1.FishEvents.on("scriptKiddie", function (_, p) { return Timer.schedule(function () { - if (!exports.Achievements.script_kiddie.has(p)) - exports.Achievements.script_kiddie.grantTo(p); -}, 2); }); -globals_1.FishEvents.on("memoryCorruption", function () { return exports.Achievements.memory_corruption.grantToAllOnline(); }); -globals_1.FishEvents.on("serverSays", function () { return exports.Achievements.server_speak.grantToAllOnline(); }); -var templateObject_1, templateObject_2; +"use strict"; +var __makeTemplateObject = (this && this.__makeTemplateObject) || function (cooked, raw) { + if (Object.defineProperty) { Object.defineProperty(cooked, "raw", { value: raw }); } else { cooked.raw = raw; } + return cooked; +}; +var __read = (this && this.__read) || function (o, n) { + var m = typeof Symbol === "function" && o[Symbol.iterator]; + if (!m) return o; + var i = m.call(o), r, ar = [], e; + try { + while ((n === void 0 || n-- > 0) && !(r = i.next()).done) ar.push(r.value); + } + catch (error) { e = { error: error }; } + finally { + try { + if (r && !r.done && (m = i["return"])) m.call(i); + } + finally { if (e) throw e.error; } + } + return ar; +}; +var __spreadArray = (this && this.__spreadArray) || function (to, from, pack) { + if (pack || arguments.length === 2) for (var i = 0, l = from.length, ar; i < l; i++) { + if (ar || !(i in from)) { + if (!ar) ar = Array.prototype.slice.call(from, 0, i); + ar[i] = from[i]; + } + } + return to.concat(ar || Array.prototype.slice.call(from)); +}; +var __values = (this && this.__values) || function(o) { + var s = typeof Symbol === "function" && Symbol.iterator, m = s && o[s], i = 0; + if (m) return m.call(o); + if (o && typeof o.length === "number") return { + next: function () { + if (o && i >= o.length) o = void 0; + return { value: o && o[i++], done: !o }; + } + }; + throw new TypeError(s ? "Object is not iterable." : "Symbol.iterator is not defined."); +}; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.Achievements = exports.Achievement = void 0; +var config_1 = require("/config"); +var funcs_1 = require("/funcs"); +var globals_1 = require("/globals"); +var players_1 = require("/players"); +var ranks_1 = require("/ranks"); +var utils_1 = require("/utils"); +//scrap doesn't count +var serpuloItems = [Items.copper, Items.lead, Items.graphite, Items.silicon, Items.metaglass, Items.titanium, Items.plastanium, Items.thorium, Items.surgeAlloy, Items.phaseFabric]; +var erekirItems = [Items.beryllium, Items.graphite, Items.silicon, Items.tungsten, Items.oxide, Items.surgeAlloy, Items.thorium, Items.carbide, Items.phaseFabric]; +var usefulItems10k = { + serpulo: serpuloItems.map(function (i) { return new ItemStack(i, 10000); }), + erekir: erekirItems.map(function (i) { return new ItemStack(i, 10000); }), + sun: __spreadArray(__spreadArray([], __read(serpuloItems), false), __read(erekirItems), false).map(function (i) { return new ItemStack(i, 10000); }), +}; +var allItems1k = Vars.content.items().select(function (i) { return !i.hidden; }).toArray().map(function (i) { return new ItemStack(i, 1000); }); +var mixtechItems = Items.serpuloItems.copy(); +Items.erekirItems.each(function (i) { return mixtechItems.add(i); }); +var Achievement = /** @class */ (function () { + function Achievement(icon, name, description, options) { + var _a; + if (options === void 0) { options = {}; } + this.name = name; + this.notify = "player"; + this.hidden = false; + this.disabled = false; + if (Array.isArray(icon)) { + this.icon = (icon[0].startsWith("[") ? icon[0] : "[".concat(icon[0], "]")) + (typeof icon[1] == "number" ? String.fromCharCode(icon[1]) : icon[1]); + } + else if (typeof icon == "number") { + this.icon = String.fromCharCode(icon); + } + else { + this.icon = icon; + } + if (Array.isArray(description)) { + _a = __read(description, 2), this.description = _a[0], this.extendedDescription = _a[1]; + } + else + this.description = description; + this.nid = Achievement._id++; + Object.assign(this, options); + if (options.modes) { + var _b = __read(options.modes), type = _b[0], modes_1 = _b.slice(1); + if (type == "only") { + this.allowedModes = modes_1; + this.modesText = modes_1.join(", "); + } + else { + this.allowedModes = config_1.GamemodeNames.filter(function (m) { return !modes_1.includes(m); }); + this.modesText = "all except ".concat(modes_1.join(", ")); + } + } + else { + this.allowedModes = config_1.GamemodeNames; + this.modesText = "all"; + } + if (!this.disabled) { + Achievement.all.push(this); + if (this.checkPlayerFrequent || this.checkFrequent) + Achievement.checkFrequent.push(this); + if (this.checkPlayerInfrequent || this.checkInfrequent) + Achievement.checkInfrequent.push(this); + if (this.checkPlayerJoin) + Achievement.checkJoin.push(this); + if (this.checkPlayerGameover || this.checkGameover) + Achievement.checkGameover.push(this); + } + } + Achievement.prototype.message = function () { + return config_1.FColor.achievement(templateObject_1 || (templateObject_1 = __makeTemplateObject(["Achievement granted!\n[accent]", "[white]: ", ""], ["Achievement granted!\\n[accent]", "[white]: ", ""])), this.name, this.description); + }; + Achievement.prototype.messageToEveryone = function (player) { + return config_1.FColor.achievement(templateObject_2 || (templateObject_2 = __makeTemplateObject(["Player ", " has completed the achievement \"", "\"."], ["Player ", " has completed the achievement \"", "\"."])), player.prefixedName, this.name); + }; + Achievement.prototype.allowedInMode = function () { + return this.allowedModes.includes(config_1.Gamemode.name()); + }; + Achievement.prototype.grantToAllOnline = function (team) { + var _this = this; + players_1.FishPlayer.forEachPlayer(function (p) { + if (!_this.has(p) && (!team || p.team() == team)) { + if (_this.notify != "nobody") + p.sendMessage(_this.message()); + _this.setObtained(p); + } + }); + }; + /** Do not call this in a loop on an achievement set to notify everyone. */ + Achievement.prototype.grantTo = function (player, allowRepeatMessage) { + if (allowRepeatMessage === void 0) { allowRepeatMessage = true; } + var has = this.has(player); + if (!has || allowRepeatMessage) { + if (this.notify == "everyone") + Call.sendMessage(this.messageToEveryone(player)); + else if (this.notify == "player") + player.sendMessage(this.message()); + } + if (!has) + this.setObtained(player); + }; + Achievement.prototype.setObtained = function (player) { + //void player.updateSynced(fishP => fishP.achievements.set(this.nid)); + player.achievements.set(this.nid); + }; + Achievement.prototype.has = function (player) { + return player.achievements.get(this.nid); + }; + Achievement.all = []; + /** Checked every second. */ + Achievement.checkFrequent = []; + /** Checked every 10 seconds. Use for states that can be gained but not lost, such as "x wins". */ + Achievement.checkInfrequent = []; + Achievement.checkJoin = []; + Achievement.checkGameover = []; + Achievement._id = 0; + return Achievement; +}()); +exports.Achievement = Achievement; +Events.on(EventType.PlayerJoin, function (_a) { + var e_1, _b; + var _c; + var player = _a.player; + Time.mark(); + var _loop_1 = function (ach) { + if (ach.allowedInMode()) { + var fishP_1 = players_1.FishPlayer.get(player); + if (!ach.has(fishP_1) && ((_c = ach.checkPlayerJoin) === null || _c === void 0 ? void 0 : _c.call(ach, fishP_1))) { + if (fishP_1.dataSynced) + ach.grantTo(fishP_1); + else + Timer.schedule(function () { return ach.grantTo(fishP_1); }, 2); //2 seconds should be enough + } + } + }; + try { + for (var _d = __values(Achievement.checkJoin), _e = _d.next(); !_e.done; _e = _d.next()) { + var ach = _e.value; + _loop_1(ach); + } + } + catch (e_1_1) { e_1 = { error: e_1_1 }; } + finally { + try { + if (_e && !_e.done && (_b = _d.return)) _b.call(_d); + } + finally { if (e_1) throw e_1.error; } + } + Log.debug("ach join @", Time.elapsed()); +}); +globals_1.FishEvents.on("gameOver", function (_, winner) { + var e_2, _a; + var _b; + Time.mark(); + var _loop_2 = function (ach) { + if (ach.allowedInMode()) { + if ((_b = ach.checkGameover) === null || _b === void 0 ? void 0 : _b.call(ach, winner)) + ach.grantToAllOnline(); + else + players_1.FishPlayer.forEachPlayer(function (fishP) { + var _a; + if (!ach.has(fishP) && ((_a = ach.checkPlayerGameover) === null || _a === void 0 ? void 0 : _a.call(ach, fishP, winner))) { + ach.grantTo(fishP); + } + }); + } + }; + try { + for (var _c = __values(Achievement.checkGameover), _d = _c.next(); !_d.done; _d = _c.next()) { + var ach = _d.value; + _loop_2(ach); + } + } + catch (e_2_1) { e_2 = { error: e_2_1 }; } + finally { + try { + if (_d && !_d.done && (_a = _c.return)) _a.call(_c); + } + finally { if (e_2) throw e_2.error; } + } + Log.debug("ach gameover @", Time.elapsed()); +}); +Timer.schedule(function () { + var e_3, _a; + Time.mark(); + var _loop_3 = function (ach) { + if (ach.allowedInMode()) { + if (ach.checkFrequent) { + if (config_1.Gamemode.pvp()) { + Vars.state.teams.active.each(function (_a) { + var team = _a.team; + if (ach.checkFrequent(team)) + ach.grantToAllOnline(team); + }); + } + else { + if (ach.checkFrequent(Vars.state.rules.defaultTeam)) + ach.grantToAllOnline(); + } + } + else { + players_1.FishPlayer.forEachPlayer(function (fishP) { + var _a; + if (!ach.has(fishP) && ((_a = ach.checkPlayerFrequent) === null || _a === void 0 ? void 0 : _a.call(ach, fishP))) + ach.grantTo(fishP); + }); + } + } + }; + try { + for (var _b = __values(Achievement.checkFrequent), _c = _b.next(); !_c.done; _c = _b.next()) { + var ach = _c.value; + _loop_3(ach); + } + } + catch (e_3_1) { e_3 = { error: e_3_1 }; } + finally { + try { + if (_c && !_c.done && (_a = _b.return)) _a.call(_b); + } + finally { if (e_3) throw e_3.error; } + } + Log.debug("ach frequent @", Time.elapsed()); +}, 1, 1); +Timer.schedule(function () { + var e_4, _a; + Time.mark(); + var _loop_4 = function (ach) { + if (ach.allowedInMode()) { + if (ach.checkInfrequent) { + if (config_1.Gamemode.pvp()) { + Vars.state.teams.active.each(function (_a) { + var team = _a.team; + if (ach.checkInfrequent(team)) + ach.grantToAllOnline(team); + }); + } + else { + if (ach.checkInfrequent(Vars.state.rules.defaultTeam)) + ach.grantToAllOnline(); + } + } + else { + players_1.FishPlayer.forEachPlayer(function (fishP) { + var _a; + if (!ach.has(fishP) && ((_a = ach.checkPlayerInfrequent) === null || _a === void 0 ? void 0 : _a.call(ach, fishP))) + ach.grantTo(fishP); + }); + } + } + }; + try { + for (var _b = __values(Achievement.checkInfrequent), _c = _b.next(); !_c.done; _c = _b.next()) { + var ach = _c.value; + _loop_4(ach); + } + } + catch (e_4_1) { e_4 = { error: e_4_1 }; } + finally { + try { + if (_c && !_c.done && (_a = _b.return)) _a.call(_b); + } + finally { if (e_4) throw e_4.error; } + } + Log.debug("ach infrequent @", Time.elapsed()); +}, 10, 10); +exports.Achievements = { + // =========================== + // ╦ ╦ ╔═╗ ╦═╗ ╔╗╔ ╦ ╔╗╔ ╔═╗ ┬ + // ║║║ ╠═╣ ╠╦╝ ║║║ ║ ║║║ ║ ╦ │ + // ╚╩╝ ╩ ╩ ╩╚═ ╝╚╝ ╩ ╝╚╝ ╚═╝ o + // =========================== + // Do not change the order of any achievements. + // Do not remove any achievements: instead, set the "disabled" option to true. + // Reordering achievements will cause ID shifts. + //Joining based + welcome: new Achievement(["gold", Iconc.infoCircle], "Welcome", "Join the server.", { + checkPlayerJoin: function () { return true; }, + notify: "nobody" + }), + migratory_fish: new Achievement(Iconc.exit, "Migratory Fish", "Join all of our servers.", { + disabled: true + }), //TODO + frequent_visitor: new Achievement(Iconc.planeOutline, "Frequent Visitor", ["Join the server 100 times.", "Note: Do not reconnect frequently, that will not work. This achievement requires that you have been playing for 1 month."], { + checkPlayerJoin: function (p) { return p.info().timesJoined >= 100 && (Date.now() - p.globalFirstJoined > funcs_1.Duration.months(1)); } + }), + //Gamemode based + attack: new Achievement(Iconc.modeAttack, "Attack", ["Defeat an attack map.", "You must be present for the beginning and end of the game."], { + modes: ["only", "attack"], + checkPlayerGameover: function (player, winTeam) { + return Vars.state.rules.defaultTeam == winTeam && player.tstats.lastMapStartTime == players_1.FishPlayer.lastMapStartTime; + }, + }), + survival: new Achievement(Iconc.modeSurvival, "Survival", ["Survive 50 waves in a survival map.", "Must be during the same game."], { + modes: ["only", "survival"], + checkPlayerInfrequent: function (player) { + return player.tstats.wavesSurvived >= 50; + }, + }), + pvp: new Achievement(Iconc.modePvp, "PVP", ["Win a match of PVP.", "You must be present for the beginning and end of the game."], { + modes: ["only", "pvp"], + checkPlayerGameover: function (player, winTeam) { + return player.team() == winTeam && player.tstats.lastMapStartTime == players_1.FishPlayer.lastMapStartTime; + }, + }), + sandbox: new Achievement(Iconc.image, "Sandbox", "Spend 1 hour in Sandbox.", { + modes: ["only", "sandbox"], + checkPlayerInfrequent: function (p) { return p.stats.timeInGame > funcs_1.Duration.hours(1); }, + }), + hexed: new Achievement(Iconc.layers, "Hexed", ["Play a match of Hexed.", "You must be present for the beginning and end of the game."], { + modes: ["only", "hexed"], + checkPlayerGameover: function (player) { + return player.tstats.lastMapStartTime == players_1.FishPlayer.lastMapStartTime; + }, + }), + minigame: new Achievement(Iconc.play, "Minigame", ["Win a Minigame.", "You must be present for the beginning and end of the game."], { + modes: ["only", "minigame"], + checkPlayerGameover: function (player, winTeam) { + return player.team() == winTeam && player.tstats.lastMapStartTime == players_1.FishPlayer.lastMapStartTime; + }, + }), + //playtime based + playtime_1: new Achievement(["white", Iconc.googleplay], "Playtime 1", "Spend 1 hour in-game.", { + checkPlayerInfrequent: function (p) { return p.globalStats.timeInGame >= funcs_1.Duration.hours(1); } + }), + playtime_2: new Achievement(["red", Iconc.googleplay], "Playtime 2", "Spend 12 hours in-game.", { + checkPlayerInfrequent: function (p) { return p.globalStats.timeInGame >= funcs_1.Duration.hours(12); } + }), + playtime_3: new Achievement(["orange", Iconc.googleplay], "Playtime 3", "Spend 2 days in-game.", { + checkPlayerInfrequent: function (p) { return p.globalStats.timeInGame >= funcs_1.Duration.days(2); } + }), + playtime_4: new Achievement(["yellow", Iconc.googleplay], "Playtime 4", "Spend 10 days in-game.", { + checkPlayerInfrequent: function (p) { return p.globalStats.timeInGame >= funcs_1.Duration.days(10); } + }), + //victories based + victory_1: new Achievement(["white", Iconc.star], "First Victory", "Win a map run.", { + checkPlayerGameover: function (p) { return p.globalStats.gamesWon >= 1; } + }), + victory_2: new Achievement(["red", Iconc.star], "Victories 2", "Win 5 map runs.", { + checkPlayerGameover: function (p) { return p.globalStats.gamesWon >= 5; } + }), + victory_3: new Achievement(["orange", Iconc.star], "Victories 3", "Win 30 map runs.", { + checkPlayerGameover: function (p) { return p.globalStats.gamesWon >= 30; } + }), + victory_4: new Achievement(["yellow", Iconc.star], "Victories 4", "Win 100 map runs.", { + checkPlayerGameover: function (p) { return p.globalStats.gamesWon >= 100; }, + notify: "everyone" + }), + //games based + games_1: new Achievement(["white", Iconc.itchio], "Games 1", "Play 10 map runs.", { + checkPlayerGameover: function (p) { return p.globalStats.gamesFinished >= 10; } + }), + games_2: new Achievement(["red", Iconc.itchio], "Games 2", "Play 40 map runs.", { + checkPlayerGameover: function (p) { return p.globalStats.gamesFinished >= 40; } + }), + games_3: new Achievement(["orange", Iconc.itchio], "Games 3", "Play 100 map runs.", { + checkPlayerGameover: function (p) { return p.globalStats.gamesFinished >= 100; } + }), + games_4: new Achievement(["yellow", Iconc.itchio], "Games 4", "Play 200 map runs.", { + checkPlayerGameover: function (p) { return p.globalStats.gamesFinished >= 200; }, + notify: "everyone" + }), + //messages based + messages_1: new Achievement(["white", Iconc.chat], "Hello", "Send your first chat message.", { + checkPlayerInfrequent: function (p) { return p.globalStats.chatMessagesSent >= 1; }, + notify: "nobody" + }), + messages_2: new Achievement(["red", Iconc.chat], "Chat 2", ["Send 100 chat messages.", "Warning: you will be kicked if you spam the chat."], { + checkPlayerInfrequent: function (p) { return p.globalStats.chatMessagesSent >= 100; } + }), + messages_3: new Achievement(["orange", Iconc.chat], "Chat 3", ["Send 500 chat messages.", "Warning: you will be kicked if you spam the chat."], { + checkPlayerInfrequent: function (p) { return p.globalStats.chatMessagesSent >= 500; } + }), + messages_4: new Achievement(["yellow", Iconc.chat], "Chat 4", ["Send 2000 chat messages.", "Warning: you will be kicked if you spam the chat."], { + checkPlayerInfrequent: function (p) { return p.globalStats.chatMessagesSent >= 2000; } + }), + messages_5: new Achievement(["lime", Iconc.chat], "Chat 4", ["Send 5000 chat messages.", "Warning: you will be kicked if you spam the chat."], { + checkPlayerInfrequent: function (p) { return p.globalStats.chatMessagesSent >= 5000; }, + notify: "everyone" + }), + //blocks built based + builds_1: new Achievement(["white", Iconc.fileText], "The Factory Must Prepare", "Construct 1 buildings.", { + checkPlayerInfrequent: function (p) { return p.globalStats.blocksPlaced >= 1; }, + notify: "nobody" + }), + builds_2: new Achievement(["red", Iconc.fileText], "The Factory Must Begin", "Construct 200 buildings.", { + checkPlayerInfrequent: function (p) { return p.globalStats.blocksPlaced > 200; } + }), + builds_3: new Achievement(["orange", Iconc.fileText], "The Factory Must Produce", "Construct 1000 buildings.", { + checkPlayerInfrequent: function (p) { return p.globalStats.blocksPlaced > 1000; } + }), + builds_4: new Achievement(["yellow", Iconc.fileText], "The Factory Must Grow", "Construct 5000 buildings.", { + checkPlayerInfrequent: function (p) { return p.globalStats.blocksPlaced > 5000; }, + }), + //units + t5: new Achievement(Blocks.tetrativeReconstructor.emoji(), "T5", "Control a T5 unit.", { + modes: ["not", "sandbox"], + checkPlayerFrequent: function (player) { + var _a; + return globals_1.unitsT5.includes((_a = player.unit()) === null || _a === void 0 ? void 0 : _a.type); + }, + }), + dibs: new Achievement(["green", Blocks.tetrativeReconstructor.emoji()], "Dibs", "Be the first player to control the first T5 unit made by a reconstructor that you placed.", { + modes: ["not", "sandbox"], + disabled: true + }), //TODO + worm: new Achievement(UnitTypes.latum.emoji(), "Worm", "Control a Latum.", { + checkPlayerFrequent: function (player) { + var _a; + return ((_a = player.unit()) === null || _a === void 0 ? void 0 : _a.type) == UnitTypes.latum; + } + }), + //pvp + above_average: new Achievement(Iconc.chartBar, "Above Average", ["Reach a win rate above 50%.", "Must be over at least 20 games of PVP."], { + modes: ["only", "pvp"], + checkPlayerInfrequent: function (p) { return p.stats.gamesWon / p.stats.gamesFinished > 0.5 && p.stats.gamesFinished >= 20; } + }), + head_start: new Achievement(Iconc.commandAttack, "Head Start", ["Win a match of PVP where your opponents have a 5 minute head start.", "Your team must wait for the first 5 minutes without building or descontructing any buildings."], { + modes: ["only", "pvp"], + disabled: true + }), //TODO + one_v_two: new Achievement(["red", Iconc.modePvp], "1v2", "Defeat two (or more) opponents in PVP without help from other players.", { + modes: ["only", "pvp"], + disabled: true + }), //TODO + //sandbox + underpowered: new Achievement(["red", Blocks.powerSource.emoji()], "Underpowered", "Overload a power source.", { + modes: ["only", "sandbox"], + checkFrequent: function () { + var found = false; + //deliberate ordering for performance reasons + Groups.powerGraph.each(function (_a) { + var graph = _a.graph; + //we don't need to actually check for power sources, just assume that ~1mil power is a source + if (graph.lastPowerNeeded > graph.lastPowerProduced && graph.lastPowerNeeded < 1e10 && (graph.lastPowerProduced / Time.delta * 60) >= 999900) + found = true; + }); + return found; + } + }), + //easter eggs + memory_corruption: new Achievement(["red", Iconc.host], "Is the server OK?", "Witness a memory corruption.", { + notify: "nobody" + }), + run_js_without_perms: new Achievement(["yellow", Iconc.warning], "XKCD 838", ["Receive a warning from the server that an incident will be reported.", "One of the admin commands has a custom error message."], { + notify: "everyone" + }), + script_kiddie: new Achievement(["red", Iconc.warning], "Script Kiddie", ["Pretend to be a hacker. The server will disagree.", "Change your name to something including \"hacker\"."], { + notify: "nobody" + }), + hacker: new Achievement(["lightgray", Iconc.host], "Hacker", "Find a bug in the server and report it responsibly.", { + hidden: true + }), + //items based + items_10k: new Achievement(["green", Iconc.distribution], "Cornucopia", "Obtain 10k of every useful resource.", { + modes: ["not", "sandbox"], + checkPlayerFrequent: function (player) { + var _a; + if (!Vars.state.planet) + return false; + return ((_a = player.team().items()) === null || _a === void 0 ? void 0 : _a.has(usefulItems10k[Vars.state.planet.name])) || false; + }, + }), + fullVault: new Achievement(["green", Blocks.vault.emoji()], "Well Stocked", ["Fill a vault with every obtainable item.", "Requires mixtech."], { + modes: ["not", "sandbox"], + checkInfrequent: function (team) { + return Vars.indexer.getFlagged(team, BlockFlag.storage).contains(boolf(function (b) { return b.block == Blocks.vault && b.items.has(allItems1k) && b.linkedCore == null; })); + }, + }), + full_core: new Achievement(["green", Blocks.coreAcropolis.emoji()], "Multiblock Incinerator", "Completely fill the core with all obtainable items on a map with core incineration enabled.", { + modes: ["not", "sandbox"], + checkFrequent: function (team) { + var _a; + if (!Vars.state.planet) + return false; + var items; + switch (Vars.state.planet.name) { + case "serpulo": + items = Items.serpuloItems; + break; + case "erekir": + items = Items.erekirItems; + break; + case "sun": + items = mixtechItems; + break; + } + var capacity = (_a = team.core()) === null || _a === void 0 ? void 0 : _a.storageCapacity; + if (!capacity) + return false; + var module = team.items(); + return items.allMatch(function (i) { return module.has(i, capacity); }); + }, + }), + siligone: new Achievement(["red", Items.silicon.emoji()], "Siligone", ["Run out of silicon.", "You must have reached 2000 silicon before running out."], { + modes: ["not", "sandbox"] + }), + silicon_100k: new Achievement(["green", Items.silicon.emoji()], "Silicon for days", "Obtain 100k silicon.", { + modes: ["not", "sandbox"], + checkFrequent: function (team) { return team.items().has(Items.silicon, 100000); } + }), + //other players based + alone: new Achievement(["red", Iconc.players], "Alone", "Be the only player online for more than two minutes", { + notify: "nobody" + }), + join_playercount_20: new Achievement(["lime", Iconc.players], "Is there enough room?", "Join a server with 20 players online", { + checkPlayerJoin: function () { return Groups.player.size() > 20; }, + }), + meet_staff: new Achievement(["lime", Iconc.hammer], "Griefer Beware", "Meet a staff member in-game", { + checkPlayerJoin: function () { return Groups.player.contains(function (p) { return players_1.FishPlayer.get(p).ranksAtLeast("mod"); }); }, + }), + meet_fish: new Achievement(["blue", Iconc.admin], "The Big Fish", "Meet >|||>Fish himself in-game", { + checkPlayerJoin: function () { return Groups.player.contains(function (p) { return players_1.FishPlayer.get(p).ranksAtLeast("fish"); }); }, + hidden: true, + }), + server_speak: new Achievement(["pink", Iconc.host], "It Speaks!", "Hear the server talk in chat."), + see_marked_griefer: new Achievement(["red", Iconc.hammer], "Flying Tonk", "See a marked griefer in-game.", { + checkInfrequent: function () { return Groups.player.contains(function (p) { return players_1.FishPlayer.get(p).marked(); }); }, + }), + //maps based + beat_map_not_in_rotation: new Achievement(["pink", Iconc.map], "How?", "Beat a map that isn't in the list of maps.", { + notify: "everyone", + modes: ["not", "pvp"], + checkGameover: function (team) { return team == Vars.state.rules.defaultTeam && !Vars.state.map.custom; } + }), + //misc + power_1mil: new Achievement(["green", Blocks.powerSource.emoji()], "Who needs sources?", "Reach a power production of 1 million without using power sources.", { + modes: ["not", "sandbox"], + checkFrequent: function (team) { + var found = false; + //deliberate ordering for performance reasons + Groups.powerGraph.each(function (_a) { + var _b; + var graph = _a.graph; + //we need to actually check for power sources + if ((graph.lastPowerProduced / Time.delta * 60) > 1e6 && + !graph.producers.contains(boolf(function (b) { return b.block == Blocks.powerSource; })) && + ((_b = graph.producers.firstOpt()) === null || _b === void 0 ? void 0 : _b.team) == team) + found = true; + }); + return found; + } + }), + pacifist_crawler: new Achievement(UnitTypes.crawler.emoji(), "Pacifist Crawler", "Control a crawler for 15 minutes without exploding.", { + modes: ["not", "sandbox"], + disabled: true + }), //TODO + core_low_hp: new Achievement(["yellow", Blocks.coreNucleus.emoji()], "Close Call", "Have your core reach less than 50 health, but survive.", { + modes: ["not", "sandbox"], + }), + enemy_core_low_hp: new Achievement(["red", Blocks.coreNucleus.emoji()], "So Close", "Cause the enemy core to reach less than 50 health, but survive.", { + modes: ["not", "sandbox"], + }), + verified: new Achievement([ranks_1.Rank.active.color, Iconc.ok], "Verified", "Be promoted automatically to ".concat(ranks_1.Rank.active.coloredName(), " rank."), { + checkPlayerJoin: function (p) { return p.ranksAtLeast("active"); }, notify: "nobody" + }), + click_me: new Achievement(Iconc.bookOpen, "Clicked", "Run /achievementgrid and click this achievement."), + afk: new Achievement(["yellow", Iconc.lock], "AFK?", "Win a game without interacting with any blocks.", { + modes: ["not", "sandbox"], + checkPlayerGameover: function (player, winTeam) { + return player.team() == winTeam && player.tstats.blockInteractionsThisMap == 0; + }, + }), + status_effects_5: new Achievement(StatusEffects.electrified.emoji(), "A Furious Cocktail", "Have at least 5 status effects at once.", { + checkPlayerFrequent: function (p) { + var unit = p.unit(); + if (!unit) + return false; + var statuses = (0, utils_1.getStatuses)(unit); + return statuses.size >= 5; + }, + modes: ["not", "sandbox"] + }), + drown_big_tank: new Achievement(["blue", UnitTypes.conquer.emoji()], "Not Waterproof", "Drown an enemy Conquer or Vanquish.", { + notify: "everyone", + modes: ["not", "sandbox"] + }), + drown_mace_in_cryo: new Achievement(["cyan", UnitTypes.mace.emoji()], "Cooldown", "Drown a Mace in ".concat(Blocks.cryofluid.emoji(), " Cryofluid."), { + notify: "everyone", + modes: ["not", "sandbox"] + }), + max_boost_duo: new Achievement(["yellow", Blocks.duo.emoji()], "In Duo We Trust", "Control a Duo with maximum boosts.", { + checkPlayerFrequent: function (player) { + var _a, _b; + var tile = (_b = (_a = player.unit()) === null || _a === void 0 ? void 0 : _a.tile) === null || _b === void 0 ? void 0 : _b.call(_a); + if (!tile) + return false; + return tile.block == Blocks.duo && !tile.ammo.isEmpty() && tile.ammo.peek().item == Items.silicon && tile.liquids.current() == Liquids.cryofluid && tile.timeScale() >= 2.5; + }, + notify: "everyone", + modes: ["not", "sandbox"] + }), + foreshadow_overkill: new Achievement(["yellow", Blocks.foreshadow.emoji()], "Overkill", ["Kill a Dagger with a maximally boosted Foreshadow.", "Hint: the maximum overdrive is not +150%..."], { + notify: "everyone", + modes: ["not", "sandbox"] + }), + impacts_15: new Achievement(["green", Blocks.impactReactor.emoji()], "Darthscion's Nightmare", "Run 15 impact reactors at full efficiency.", { + modes: ["not", "sandbox"], + notify: "everyone", + checkInfrequent: function (team) { + var found = false; + //deliberate ordering for performance reasons + Groups.powerGraph.each(function (_a) { + var graph = _a.graph; + if (graph.producers.size >= 15 && graph.producers.count(function (b) { return b.block == Blocks.impactReactor && b.warmup > 0.99999; }) > 15 && graph.producers.first().team == team) + found = true; + }); + return found; + }, + }), + help_help: new Achievement(["brown", Iconc.info], "Help with help", "Run /help help", { + notify: "everyone" + }), + ohno: new Achievement(["scarlet", UnitTypes.alpha.emoji()], "Oh no", "Control an ohno unit."), + sniper_duel: new Achievement(["yellow", UnitTypes.omura.emoji()], "Sniper duel", "Kill a Foreshadow with an Omura from outside its range."), + around_the_world: new Achievement(Iconc.planet, "Around the World", "Fly your unit around the entire map without entering it, starting from the lower left.", { + notify: "everyone", + }), +}; +Object.entries(exports.Achievements).forEach(function (_a) { + var _b = __read(_a, 2), id = _b[0], a = _b[1]; + return a.sid = id; +}); +globals_1.FishEvents.on("commandUnauthorized", function (_, player, name) { + if ((name == "js" || name == "fjs") && !exports.Achievements.run_js_without_perms.has(player)) + exports.Achievements.run_js_without_perms.grantTo(player); +}); +Events.on(EventType.UnitDrownEvent, function (_a) { + var _b; + var unit = _a.unit; + if (!config_1.Gamemode.sandbox()) { + if (unit.type == UnitTypes.mace && ((_b = unit.tileOn()) === null || _b === void 0 ? void 0 : _b.floor()) == Blocks.cryofluid) + exports.Achievements.drown_mace_in_cryo.grantToAllOnline(); + else if (unit.type == UnitTypes.conquer || unit.type == UnitTypes.vanquish) { + if (config_1.Gamemode.pvp()) { + Vars.state.teams.active.map(function (t) { return t.team; }).select(function (t) { return t !== unit.team; }).each(function (t) { return exports.Achievements.drown_big_tank.grantToAllOnline(t); }); + } + else { + if (unit.team !== Vars.state.rules.defaultTeam) + exports.Achievements.drown_big_tank.grantToAllOnline(); + } + } + } +}); +Events.on(EventType.UnitBulletDestroyEvent, function (_a) { + var _b; + var unit = _a.unit, bullet = _a.bullet; + if (!config_1.Gamemode.sandbox() && unit.type == UnitTypes.dagger && ((_b = bullet.owner) === null || _b === void 0 ? void 0 : _b.block) == Blocks.foreshadow) { + var build = bullet.owner; + if (build.liquids.current() == Liquids.cryofluid && build.timeScale() >= 3) + exports.Achievements.foreshadow_overkill.grantToAllOnline(build.team); + } +}); +Events.on(EventType.BuildingBulletDestroyEvent, function (_a) { + var _b; + var build = _a.build, bullet = _a.bullet; + if (!config_1.Gamemode.sandbox() && build.block == Blocks.foreshadow && ((_b = bullet.owner) === null || _b === void 0 ? void 0 : _b.type) == UnitTypes.omura) { + var unit = bullet.owner; + var player = unit.getPlayer(); + if (player && !unit.within(build, build.range() + unit.hitSize / 2)) + exports.Achievements.sniper_duel.grantTo(players_1.FishPlayer.get(player)); + } +}); +var siliconReached = Team.all.map(function (_) { return false; }); +Events.on(EventType.GameOverEvent, function () { return siliconReached = Team.all.map(function (_) { return false; }); }); +var isAlone = 0; +Timer.schedule(function () { + if (!Vars.state.gameOver && !config_1.Gamemode.sandbox()) { + Vars.state.teams.active.each(function (_a) { + var team = _a.team; + if (team.items().has(Items.silicon, 2000)) + siliconReached[team.id] = true; + else if (siliconReached[team.id] && team.items().get(Items.silicon) == 0) + exports.Achievements.siligone.grantToAllOnline(team); + }); + } + if (Groups.player.size() == 1) { + if (isAlone == 0) + isAlone = Date.now(); + else if (Date.now() > isAlone + funcs_1.Duration.minutes(2)) + exports.Achievements.alone.grantToAllOnline(); + } + else + isAlone = 0; +}, 2, 2); +var coreHealthTime = new Map(); +if (!config_1.Gamemode.sandbox()) + Timer.schedule(function () { + coreHealthTime.forEach(function (value, core) { + if (Date.now() > value) { + if (core.dead) { + coreHealthTime.delete(core); + } + else if (core.health > 50) { + //grant achievement + exports.Achievements.core_low_hp.grantToAllOnline(core.team); + players_1.FishPlayer.forEachPlayer(function (p) { + if (core.team != p.team() && !exports.Achievements.enemy_core_low_hp.has(p)) + exports.Achievements.enemy_core_low_hp.grantTo(p); + }); + coreHealthTime.delete(core); + } + } + }); + Vars.state.teams.active.flatMap(function (t) { return t.cores; }).each(function (core) { + if (core.health < 50 && !coreHealthTime.get(core)) + coreHealthTime.set(core, Date.now() + 12000); + }); + }, 1, 1); +var aroundTheWorld = {}; +Timer.schedule(function () { + var e_5, _a; + players_1.FishPlayer.forEachPlayer(function (p) { + var _a; + var _b; + var unit = p.unit(); + if (unit && unit.x < 0 && unit.y < 0) { + (_a = aroundTheWorld[_b = p.uuid]) !== null && _a !== void 0 ? _a : (aroundTheWorld[_b] = { player: p, unit: unit, side: "left" }); + } + }); + var _loop_5 = function (uuid, entry) { + if (!(function () { + if (entry.unit.dead) + return false; + var left = entry.unit.x < 0; + var bottom = entry.unit.y < 0; + var right = entry.unit.x > (Vars.world.width() - 1) * 8; + var top = entry.unit.y > (Vars.world.height() - 1) * 8; + switch (entry.side) { + case "left": + if (!left) + return false; + if (top) + entry.side = "top"; + break; + case "top": + if (!top) + return false; + if (right) + entry.side = "right"; + break; + case "right": + if (!right) + return false; + if (bottom) + entry.side = "bottom"; + break; + case "bottom": + if (!bottom) + return false; + if (left) { + exports.Achievements.around_the_world.grantTo(entry.player, true); + return false; + } + break; + } + return true; + })()) + delete aroundTheWorld[uuid]; + }; + try { + for (var _b = __values(Object.entries(aroundTheWorld)), _c = _b.next(); !_c.done; _c = _b.next()) { + var _d = __read(_c.value, 2), uuid = _d[0], entry = _d[1]; + _loop_5(uuid, entry); + } + } + catch (e_5_1) { e_5 = { error: e_5_1 }; } + finally { + try { + if (_c && !_c.done && (_a = _b.return)) _a.call(_b); + } + finally { if (e_5) throw e_5.error; } + } +}, 1, 0.5); +Events.on(EventType.GameOverEvent, function () { return coreHealthTime.clear(); }); +Events.on(EventType.WorldLoadEvent, function () { return coreHealthTime.clear(); }); +globals_1.FishEvents.on("scriptKiddie", function (_, p) { return Timer.schedule(function () { + if (!exports.Achievements.script_kiddie.has(p)) + exports.Achievements.script_kiddie.grantTo(p); +}, 2); }); +globals_1.FishEvents.on("memoryCorruption", function () { return exports.Achievements.memory_corruption.grantToAllOnline(); }); +globals_1.FishEvents.on("serverSays", function () { return exports.Achievements.server_speak.grantToAllOnline(); }); +var templateObject_1, templateObject_2; diff --git a/build/scripts/api.js b/build/scripts/api.js index 8196294e..e479452f 100644 --- a/build/scripts/api.js +++ b/build/scripts/api.js @@ -1,214 +1,214 @@ -"use strict"; -/* -Copyright © BalaM314, 2026. All Rights Reserved. -This file contains wrappers over the API calls to the backend server. -*/ -Object.defineProperty(exports, "__esModule", { value: true }); -exports.isVpn = isVpn; -exports.isVpnCached = isVpnCached; -exports.sendModerationMessage = sendModerationMessage; -exports.getStaffMessages = getStaffMessages; -exports.sendStaffMessage = sendStaffMessage; -exports.ban = ban; -exports.unban = unban; -exports.getBanned = getBanned; -exports.getFishPlayerData = getFishPlayerData; -exports.setFishPlayerData = setFishPlayerData; -var config_1 = require("/config"); -var players_1 = require("/players"); -var promise_1 = require("/promise"); -var cachedIps = {}; -/** Make an API request to see if an IP is likely VPN. */ -function isVpn(ip, callback, callbackError) { - if (ip in cachedIps) - return callback(cachedIps[ip]); - Http.get("http://ip-api.com/json/".concat(ip, "?fields=proxy,hosting"), function (res) { - var data = res.getResultAsString(); - var json = JSON.parse(data); - var isVpn = json.proxy || json.hosting; - cachedIps[ip] = isVpn; - players_1.FishPlayer.stats.numIpsChecked++; - if (isVpn) - players_1.FishPlayer.stats.numIpsFlagged++; - callback(isVpn); - }, callbackError !== null && callbackError !== void 0 ? callbackError : (function (err) { - Log.err("[API] Network error when trying to call api.isVpn()"); - players_1.FishPlayer.stats.numIpsErrored++; - callback(false); - })); -} -function isVpnCached(ip) { - return cachedIps[ip]; -} -/** Send text to the moderation logs channel in Discord. */ -function sendModerationMessage(message) { - if (config_1.Mode.noBackend) { - Log.info("Sent moderation log message: ".concat(message)); - return; - } - var req = Http.post("http://".concat(config_1.backendIP, "/api/mod-dump"), JSON.stringify({ message: message })).header('Content-Type', 'application/json').header('Accept', '*/*'); - req.timeout = 10000; - req.error(function () { return Log.err("[API] Network error when trying to call api.sendModerationMessage()"); }); - req.submit(function (response) { - //Log.info(response.getResultAsString()); - }); -} -/** Get staff messages from discord. */ -function getStaffMessages(callback) { - if (config_1.Mode.noBackend) - return; - var req = Http.post("http://".concat(config_1.backendIP, "/api/getStaffMessages"), JSON.stringify({ server: config_1.Gamemode.name() })) - .header('Content-Type', 'application/json').header('Accept', '*/*'); - req.timeout = 10000; - req.error(function () { return Log.err("[API] Network error when trying to call api.getStaffMessages()"); }); - req.submit(function (response) { - var temp = response.getResultAsString(); - if (!temp.length) - Log.err("[API] Network error(empty response) when trying to call api.getStaffMessages()"); - else - callback(JSON.parse(temp).messages); - }); -} -/** Send staff messages from server. */ -function sendStaffMessage(message, playerName, isStaff, callback) { - if (config_1.Mode.noBackend) - return; - var req = Http.post("http://".concat(config_1.backendIP, "/api/sendStaffMessage"), - // need to send both name variants so one can be sent to the other servers with color and discord can use the clean one - JSON.stringify({ message: message, playerName: playerName, cleanedName: Strings.stripColors(playerName), server: config_1.Gamemode.name(), isStaff: isStaff })).header('Content-Type', 'application/json').header('Accept', '*/*'); - req.timeout = 10000; - req.error(function () { - Log.err("[API] Network error when trying to call api.sendStaffMessage()"); - callback === null || callback === void 0 ? void 0 : callback(false); - }); - req.submit(function (response) { - var temp = response.getResultAsString(); - if (!temp.length) - Log.err("[API] Network error(empty response) when trying to call api.sendStaffMessage()"); - else - callback === null || callback === void 0 ? void 0 : callback(JSON.parse(temp).data); - }); -} -/** Bans the provided ip and/or uuid. */ -function ban(data, callback) { - if (callback === void 0) { callback = function () { }; } - if (config_1.Mode.noBackend) - return; - var req = Http.post("http://".concat(config_1.backendIP, "/api/ban"), JSON.stringify(data)) - .header('Content-Type', 'application/json') - .header('Accept', '*/*'); - req.timeout = 10000; - req.error(function () { return Log.err("[API] Network error when trying to call api.ban(".concat(data.ip, ", ").concat(data.uuid, ")")); }); - req.submit(function (response) { - var str = response.getResultAsString(); - if (!str.length) - return Log.err("[API] Network error(empty response) when trying to call api.ban()"); - callback(JSON.parse(str).data); - }); -} -/** Unbans the provided ip and/or uuid. */ -function unban(data, callback) { - if (callback === void 0) { callback = function () { }; } - if (config_1.Mode.noBackend) - return; - var req = Http.post("http://".concat(config_1.backendIP, "/api/unban"), JSON.stringify(data)) - .header('Content-Type', 'application/json') - .header('Accept', '*/*'); - req.timeout = 10000; - req.error(function () { return Log.err("[API] Network error when trying to call api.ban({".concat(data.ip, ", ").concat(data.uuid, "})")); }); - req.submit(function (response) { - var str = response.getResultAsString(); - if (!str.length) - return Log.err("[API] Network error(empty response) when trying to call api.unban()"); - var parsedData = JSON.parse(str); - callback(parsedData.status, parsedData.error); - }); -} -/** Gets if either the provided uuid or ip is banned. */ -function getBanned(data, callback) { - if (config_1.Mode.noBackend) { - Log.info("[API] Attempted to getBanned(".concat(data.uuid, "/").concat(data.ip, "), assuming false due to local debug")); - callback(false); - return; - } - //TODO cache 4s - var req = Http.post("http://".concat(config_1.backendIP, "/api/checkIsBanned"), JSON.stringify(data)) - .header('Content-Type', 'application/json') - .header('Accept', '*/*'); - req.timeout = 10000; - req.error(function () { return Log.err("[API] Network error when trying to call api.getBanned()"); }); - req.submit(function (response) { - var str = response.getResultAsString(); - if (!str.length) - return Log.err("[API] Network error(empty response) when trying to call api.getBanned()"); - callback(JSON.parse(str).data); - }); -} -/** - * Fetches fish player data from the backend. - **/ -function getFishPlayerData(uuid) { - var _a = promise_1.Promise.withResolvers(), promise = _a.promise, resolve = _a.resolve, reject = _a.reject; - function fail(err) { - Log.err("[API] Network error when trying to call api.getFishPlayerData()"); - if (err) - Log.err(err); - reject(err); - } - if (config_1.Mode.noBackend) { - reject("local debug mode"); - return promise; - } - var req = Http.post("http://".concat(config_1.backendIP, "/api/fish-player"), JSON.stringify({ - id: uuid, - gamemode: config_1.Gamemode.name(), - })) - .header('Content-Type', 'application/json') - .header('Accept', '*/*'); - req.timeout = 10000; - req.error(fail); - req.submit(function (response) { - var data = response.getResultAsString(); - if (data) { - var result = JSON.parse(data); - if (!result || typeof result != "object") - fail("Invalid fish player data"); - resolve(result); - } - else { - resolve(null); - } - }); - return promise; -} -/** Pushes fish player data to the backend. */ -function setFishPlayerData(data, repeats, ignoreActivelySyncedFields) { - var _a = promise_1.Promise.withResolvers(), promise = _a.promise, resolve = _a.resolve, reject = _a.reject; - if (config_1.Mode.noBackend) { - resolve(); - return promise; - } - var req = Http.post("http://".concat(config_1.backendIP, "/api/fish-player/set"), JSON.stringify({ - player: data, - gamemode: config_1.Gamemode.name(), - ignoreActivelySyncedFields: ignoreActivelySyncedFields, - })) - .header('Content-Type', 'application/json') - .header('Accept', '*/*'); - req.timeout = 10000; - req.error(function (err) { - var _a, _b; - Log.err("[API] Network error when trying to call api.setFishPlayerData(), repeats=".concat(repeats)); - Log.err(err); - if (err === null || err === void 0 ? void 0 : err.response) - Log.err(err.response.getResultAsString()); - if (repeats > 0 && !(((_a = err.status) === null || _a === void 0 ? void 0 : _a.code) >= 400 && ((_b = err.status) === null || _b === void 0 ? void 0 : _b.code) <= 499)) - setFishPlayerData(data, repeats - 1, ignoreActivelySyncedFields).then(resolve).catch(reject); - else - reject(err); - }); - req.submit(function (response) { - resolve(); - }); - return promise; -} +"use strict"; +/* +Copyright © BalaM314, 2026. All Rights Reserved. +This file contains wrappers over the API calls to the backend server. +*/ +Object.defineProperty(exports, "__esModule", { value: true }); +exports.isVpn = isVpn; +exports.isVpnCached = isVpnCached; +exports.sendModerationMessage = sendModerationMessage; +exports.getStaffMessages = getStaffMessages; +exports.sendStaffMessage = sendStaffMessage; +exports.ban = ban; +exports.unban = unban; +exports.getBanned = getBanned; +exports.getFishPlayerData = getFishPlayerData; +exports.setFishPlayerData = setFishPlayerData; +var config_1 = require("/config"); +var players_1 = require("/players"); +var promise_1 = require("/promise"); +var cachedIps = {}; +/** Make an API request to see if an IP is likely VPN. */ +function isVpn(ip, callback, callbackError) { + if (ip in cachedIps) + return callback(cachedIps[ip]); + Http.get("http://ip-api.com/json/".concat(ip, "?fields=proxy,hosting"), function (res) { + var data = res.getResultAsString(); + var json = JSON.parse(data); + var isVpn = json.proxy || json.hosting; + cachedIps[ip] = isVpn; + players_1.FishPlayer.stats.numIpsChecked++; + if (isVpn) + players_1.FishPlayer.stats.numIpsFlagged++; + callback(isVpn); + }, callbackError !== null && callbackError !== void 0 ? callbackError : (function (err) { + Log.err("[API] Network error when trying to call api.isVpn()"); + players_1.FishPlayer.stats.numIpsErrored++; + callback(false); + })); +} +function isVpnCached(ip) { + return cachedIps[ip]; +} +/** Send text to the moderation logs channel in Discord. */ +function sendModerationMessage(message) { + if (config_1.Mode.noBackend) { + Log.info("Sent moderation log message: ".concat(message)); + return; + } + var req = Http.post("http://".concat(config_1.backendIP, "/api/mod-dump"), JSON.stringify({ message: message })).header('Content-Type', 'application/json').header('Accept', '*/*'); + req.timeout = 10000; + req.error(function () { return Log.err("[API] Network error when trying to call api.sendModerationMessage()"); }); + req.submit(function (response) { + //Log.info(response.getResultAsString()); + }); +} +/** Get staff messages from discord. */ +function getStaffMessages(callback) { + if (config_1.Mode.noBackend) + return; + var req = Http.post("http://".concat(config_1.backendIP, "/api/getStaffMessages"), JSON.stringify({ server: config_1.Gamemode.name() })) + .header('Content-Type', 'application/json').header('Accept', '*/*'); + req.timeout = 10000; + req.error(function () { return Log.err("[API] Network error when trying to call api.getStaffMessages()"); }); + req.submit(function (response) { + var temp = response.getResultAsString(); + if (!temp.length) + Log.err("[API] Network error(empty response) when trying to call api.getStaffMessages()"); + else + callback(JSON.parse(temp).messages); + }); +} +/** Send staff messages from server. */ +function sendStaffMessage(message, playerName, isStaff, callback) { + if (config_1.Mode.noBackend) + return; + var req = Http.post("http://".concat(config_1.backendIP, "/api/sendStaffMessage"), + // need to send both name variants so one can be sent to the other servers with color and discord can use the clean one + JSON.stringify({ message: message, playerName: playerName, cleanedName: Strings.stripColors(playerName), server: config_1.Gamemode.name(), isStaff: isStaff })).header('Content-Type', 'application/json').header('Accept', '*/*'); + req.timeout = 10000; + req.error(function () { + Log.err("[API] Network error when trying to call api.sendStaffMessage()"); + callback === null || callback === void 0 ? void 0 : callback(false); + }); + req.submit(function (response) { + var temp = response.getResultAsString(); + if (!temp.length) + Log.err("[API] Network error(empty response) when trying to call api.sendStaffMessage()"); + else + callback === null || callback === void 0 ? void 0 : callback(JSON.parse(temp).data); + }); +} +/** Bans the provided ip and/or uuid. */ +function ban(data, callback) { + if (callback === void 0) { callback = function () { }; } + if (config_1.Mode.noBackend) + return; + var req = Http.post("http://".concat(config_1.backendIP, "/api/ban"), JSON.stringify(data)) + .header('Content-Type', 'application/json') + .header('Accept', '*/*'); + req.timeout = 10000; + req.error(function () { return Log.err("[API] Network error when trying to call api.ban(".concat(data.ip, ", ").concat(data.uuid, ")")); }); + req.submit(function (response) { + var str = response.getResultAsString(); + if (!str.length) + return Log.err("[API] Network error(empty response) when trying to call api.ban()"); + callback(JSON.parse(str).data); + }); +} +/** Unbans the provided ip and/or uuid. */ +function unban(data, callback) { + if (callback === void 0) { callback = function () { }; } + if (config_1.Mode.noBackend) + return; + var req = Http.post("http://".concat(config_1.backendIP, "/api/unban"), JSON.stringify(data)) + .header('Content-Type', 'application/json') + .header('Accept', '*/*'); + req.timeout = 10000; + req.error(function () { return Log.err("[API] Network error when trying to call api.ban({".concat(data.ip, ", ").concat(data.uuid, "})")); }); + req.submit(function (response) { + var str = response.getResultAsString(); + if (!str.length) + return Log.err("[API] Network error(empty response) when trying to call api.unban()"); + var parsedData = JSON.parse(str); + callback(parsedData.status, parsedData.error); + }); +} +/** Gets if either the provided uuid or ip is banned. */ +function getBanned(data, callback) { + if (config_1.Mode.noBackend) { + Log.info("[API] Attempted to getBanned(".concat(data.uuid, "/").concat(data.ip, "), assuming false due to local debug")); + callback(false); + return; + } + //TODO cache 4s + var req = Http.post("http://".concat(config_1.backendIP, "/api/checkIsBanned"), JSON.stringify(data)) + .header('Content-Type', 'application/json') + .header('Accept', '*/*'); + req.timeout = 10000; + req.error(function () { return Log.err("[API] Network error when trying to call api.getBanned()"); }); + req.submit(function (response) { + var str = response.getResultAsString(); + if (!str.length) + return Log.err("[API] Network error(empty response) when trying to call api.getBanned()"); + callback(JSON.parse(str).data); + }); +} +/** + * Fetches fish player data from the backend. + **/ +function getFishPlayerData(uuid) { + var _a = promise_1.Promise.withResolvers(), promise = _a.promise, resolve = _a.resolve, reject = _a.reject; + function fail(err) { + Log.err("[API] Network error when trying to call api.getFishPlayerData()"); + if (err) + Log.err(err); + reject(err); + } + if (config_1.Mode.noBackend) { + reject("local debug mode"); + return promise; + } + var req = Http.post("http://".concat(config_1.backendIP, "/api/fish-player"), JSON.stringify({ + id: uuid, + gamemode: config_1.Gamemode.name(), + })) + .header('Content-Type', 'application/json') + .header('Accept', '*/*'); + req.timeout = 10000; + req.error(fail); + req.submit(function (response) { + var data = response.getResultAsString(); + if (data) { + var result = JSON.parse(data); + if (!result || typeof result != "object") + fail("Invalid fish player data"); + resolve(result); + } + else { + resolve(null); + } + }); + return promise; +} +/** Pushes fish player data to the backend. */ +function setFishPlayerData(data, repeats, ignoreActivelySyncedFields) { + var _a = promise_1.Promise.withResolvers(), promise = _a.promise, resolve = _a.resolve, reject = _a.reject; + if (config_1.Mode.noBackend) { + resolve(); + return promise; + } + var req = Http.post("http://".concat(config_1.backendIP, "/api/fish-player/set"), JSON.stringify({ + player: data, + gamemode: config_1.Gamemode.name(), + ignoreActivelySyncedFields: ignoreActivelySyncedFields, + })) + .header('Content-Type', 'application/json') + .header('Accept', '*/*'); + req.timeout = 10000; + req.error(function (err) { + var _a, _b; + Log.err("[API] Network error when trying to call api.setFishPlayerData(), repeats=".concat(repeats)); + Log.err(err); + if (err === null || err === void 0 ? void 0 : err.response) + Log.err(err.response.getResultAsString()); + if (repeats > 0 && !(((_a = err.status) === null || _a === void 0 ? void 0 : _a.code) >= 400 && ((_b = err.status) === null || _b === void 0 ? void 0 : _b.code) <= 499)) + setFishPlayerData(data, repeats - 1, ignoreActivelySyncedFields).then(resolve).catch(reject); + else + reject(err); + }); + req.submit(function (response) { + resolve(); + }); + return promise; +} diff --git a/build/scripts/commands/aggregate.js b/build/scripts/commands/aggregate.js index c8d305d9..ed0e4481 100644 --- a/build/scripts/commands/aggregate.js +++ b/build/scripts/commands/aggregate.js @@ -1,54 +1,54 @@ -"use strict"; -var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { - if (k2 === undefined) k2 = k; - var desc = Object.getOwnPropertyDescriptor(m, k); - if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { - desc = { enumerable: true, get: function() { return m[k]; } }; - } - Object.defineProperty(o, k2, desc); -}) : (function(o, m, k, k2) { - if (k2 === undefined) k2 = k; - o[k2] = m[k]; -})); -var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { - Object.defineProperty(o, "default", { enumerable: true, value: v }); -}) : function(o, v) { - o["default"] = v; -}); -var __importStar = (this && this.__importStar) || (function () { - var ownKeys = function(o) { - ownKeys = Object.getOwnPropertyNames || function (o) { - var ar = []; - for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k; - return ar; - }; - return ownKeys(o); - }; - return function (mod) { - if (mod && mod.__esModule) return mod; - var result = {}; - if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]); - __setModuleDefault(result, mod); - return result; - }; -})(); -Object.defineProperty(exports, "__esModule", { value: true }); -exports.registerAll = registerAll; -/* -Copyright © BalaM314, 2026. All Rights Reserved. -This file combines all the commands defined in various files into one function. -*/ -var console_1 = require("/commands/console"); -var general_1 = require("/commands/general"); -var member_1 = require("/commands/member"); -var staff_1 = require("/commands/staff"); -var commands = __importStar(require("/frameworks/commands")); -var packetHandlers_1 = require("/packetHandlers"); -function registerAll(clientHandler, serverHandler) { - commands.register(staff_1.commands, clientHandler, serverHandler); - commands.register(general_1.commands, clientHandler, serverHandler); - commands.register(member_1.commands, clientHandler, serverHandler); - commands.register(packetHandlers_1.commands, clientHandler, serverHandler); - commands.registerConsole(console_1.commands, serverHandler); - commands.initialize(); -} +"use strict"; +var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + var desc = Object.getOwnPropertyDescriptor(m, k); + if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { + desc = { enumerable: true, get: function() { return m[k]; } }; + } + Object.defineProperty(o, k2, desc); +}) : (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + o[k2] = m[k]; +})); +var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { + Object.defineProperty(o, "default", { enumerable: true, value: v }); +}) : function(o, v) { + o["default"] = v; +}); +var __importStar = (this && this.__importStar) || (function () { + var ownKeys = function(o) { + ownKeys = Object.getOwnPropertyNames || function (o) { + var ar = []; + for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k; + return ar; + }; + return ownKeys(o); + }; + return function (mod) { + if (mod && mod.__esModule) return mod; + var result = {}; + if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]); + __setModuleDefault(result, mod); + return result; + }; +})(); +Object.defineProperty(exports, "__esModule", { value: true }); +exports.registerAll = registerAll; +/* +Copyright © BalaM314, 2026. All Rights Reserved. +This file combines all the commands defined in various files into one function. +*/ +var console_1 = require("/commands/console"); +var general_1 = require("/commands/general"); +var member_1 = require("/commands/member"); +var staff_1 = require("/commands/staff"); +var commands = __importStar(require("/frameworks/commands")); +var packetHandlers_1 = require("/packetHandlers"); +function registerAll(clientHandler, serverHandler) { + commands.register(staff_1.commands, clientHandler, serverHandler); + commands.register(general_1.commands, clientHandler, serverHandler); + commands.register(member_1.commands, clientHandler, serverHandler); + commands.register(packetHandlers_1.commands, clientHandler, serverHandler); + commands.registerConsole(console_1.commands, serverHandler); + commands.initialize(); +} diff --git a/build/scripts/commands/console.js b/build/scripts/commands/console.js index 350e93ae..4930730e 100644 --- a/build/scripts/commands/console.js +++ b/build/scripts/commands/console.js @@ -1,1129 +1,1129 @@ -"use strict"; -/* -Copyright © BalaM314, 2026. All Rights Reserved. -This file contains all the console commands, which can be run through the server console. -*/ -var __makeTemplateObject = (this && this.__makeTemplateObject) || function (cooked, raw) { - if (Object.defineProperty) { Object.defineProperty(cooked, "raw", { value: raw }); } else { cooked.raw = raw; } - return cooked; -}; -var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { - if (k2 === undefined) k2 = k; - var desc = Object.getOwnPropertyDescriptor(m, k); - if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { - desc = { enumerable: true, get: function() { return m[k]; } }; - } - Object.defineProperty(o, k2, desc); -}) : (function(o, m, k, k2) { - if (k2 === undefined) k2 = k; - o[k2] = m[k]; -})); -var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { - Object.defineProperty(o, "default", { enumerable: true, value: v }); -}) : function(o, v) { - o["default"] = v; -}); -var __importStar = (this && this.__importStar) || (function () { - var ownKeys = function(o) { - ownKeys = Object.getOwnPropertyNames || function (o) { - var ar = []; - for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k; - return ar; - }; - return ownKeys(o); - }; - return function (mod) { - if (mod && mod.__esModule) return mod; - var result = {}; - if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]); - __setModuleDefault(result, mod); - return result; - }; -})(); -var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { - function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } - return new (P || (P = Promise))(function (resolve, reject) { - function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } - function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } - function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } - step((generator = generator.apply(thisArg, _arguments || [])).next()); - }); -}; -var __generator = (this && this.__generator) || function (thisArg, body) { - var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g = Object.create((typeof Iterator === "function" ? Iterator : Object).prototype); - return g.next = verb(0), g["throw"] = verb(1), g["return"] = verb(2), typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g; - function verb(n) { return function (v) { return step([n, v]); }; } - function step(op) { - if (f) throw new TypeError("Generator is already executing."); - while (g && (g = 0, op[0] && (_ = 0)), _) try { - if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t; - if (y = 0, t) op = [op[0] & 2, t.value]; - switch (op[0]) { - case 0: case 1: t = op; break; - case 4: _.label++; return { value: op[1], done: false }; - case 5: _.label++; y = op[1]; op = [0]; continue; - case 7: op = _.ops.pop(); _.trys.pop(); continue; - default: - if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; } - if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; } - if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; } - if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; } - if (t[2]) _.ops.pop(); - _.trys.pop(); continue; - } - op = body.call(thisArg, _); - } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; } - if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true }; - } -}; -var __read = (this && this.__read) || function (o, n) { - var m = typeof Symbol === "function" && o[Symbol.iterator]; - if (!m) return o; - var i = m.call(o), r, ar = [], e; - try { - while ((n === void 0 || n-- > 0) && !(r = i.next()).done) ar.push(r.value); - } - catch (error) { e = { error: error }; } - finally { - try { - if (r && !r.done && (m = i["return"])) m.call(i); - } - finally { if (e) throw e.error; } - } - return ar; -}; -var __values = (this && this.__values) || function(o) { - var s = typeof Symbol === "function" && Symbol.iterator, m = s && o[s], i = 0; - if (m) return m.call(o); - if (o && typeof o.length === "number") return { - next: function () { - if (o && i >= o.length) o = void 0; - return { value: o && o[i++], done: !o }; - } - }; - throw new TypeError(s ? "Object is not iterable." : "Symbol.iterator is not defined."); -}; -var __spreadArray = (this && this.__spreadArray) || function (to, from, pack) { - if (pack || arguments.length === 2) for (var i = 0, l = from.length, ar; i < l; i++) { - if (ar || !(i in from)) { - if (!ar) ar = Array.prototype.slice.call(from, 0, i); - ar[i] = from[i]; - } - } - return to.concat(ar || Array.prototype.slice.call(from)); -}; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.commands = void 0; -var api = __importStar(require("/api")); -var config_1 = require("/config"); -var files_1 = require("/files"); -var fjsContext = __importStar(require("/fjsContext")); -var commands_1 = require("/frameworks/commands"); -var funcs_1 = require("/funcs"); -var globals_1 = require("/globals"); -var players_1 = require("/players"); -var ranks_1 = require("/ranks"); -var utils_1 = require("/utils"); -exports.commands = (0, commands_1.consoleCommandList)({ - setrank: { - args: ["player:player", "rank:rank"], - description: "Set a player's rank.", - handler: function (_a) { - return __awaiter(this, arguments, void 0, function (_b) { - var args = _b.args, outputSuccess = _b.outputSuccess, f = _b.f; - return __generator(this, function (_c) { - switch (_c.label) { - case 0: - if (args.rank == ranks_1.Rank.pi && !config_1.Mode.localDebug) - (0, commands_1.fail)(f(templateObject_1 || (templateObject_1 = __makeTemplateObject(["Rank ", " is immutable."], ["Rank ", " is immutable."])), args.rank)); - if (args.player.immutable() && !config_1.Mode.localDebug) - (0, commands_1.fail)(f(templateObject_2 || (templateObject_2 = __makeTemplateObject(["Player ", " is immutable."], ["Player ", " is immutable."])), args.player)); - return [4 /*yield*/, args.player.setRank(args.rank)]; - case 1: - _c.sent(); - (0, utils_1.logAction)("set rank to ".concat(args.rank.name, " for"), "console", args.player); - outputSuccess(f(templateObject_3 || (templateObject_3 = __makeTemplateObject(["Set rank of player ", " to ", ""], ["Set rank of player ", " to ", ""])), args.player, args.rank)); - return [2 /*return*/]; - } - }); - }); - } - }, - admin: { - args: ["nothing:string?"], - description: "Use the setrank command instead.", - handler: function () { - (0, commands_1.fail)("Use the \"setrank\" command instead. Hint: \"setrank player admin\""); - } - }, - setflag: { - args: ["player:player", "flag:roleflag", "value:boolean"], - description: "Set a player's role flags.", - handler: function (_a) { - return __awaiter(this, arguments, void 0, function (_b) { - var args = _b.args, outputSuccess = _b.outputSuccess, f = _b.f; - return __generator(this, function (_c) { - switch (_c.label) { - case 0: return [4 /*yield*/, args.player.setFlag(args.flag, args.value)]; - case 1: - _c.sent(); - (0, utils_1.logAction)("set roleflag ".concat(args.flag.name, " to ").concat(args.value, " for"), "console", args.player); - outputSuccess(f(templateObject_4 || (templateObject_4 = __makeTemplateObject(["Set role flag ", " of player ", " to ", ""], ["Set role flag ", " of player ", " to ", ""])), args.flag, args.player, args.value)); - return [2 /*return*/]; - } - }); - }); - } - }, - savePlayers: { - args: [], - description: "Runs FishPlayer.save()", - handler: function (_a) { - var outputSuccess = _a.outputSuccess; - players_1.FishPlayer.saveAll(); - outputSuccess("Successfully wrote fish player data."); - } - }, - info: { - args: ["player:string"], - description: "Find player info(s). Displays all names and ips of a player.", - handler: function (_a) { - return __awaiter(this, arguments, void 0, function (_b) { - function display(infoList) { - var e_2, _a; - var outputString = [""]; - var _loop_1 = function (playerInfo, fishP) { - var flagsText = [ - (fishP === null || fishP === void 0 ? void 0 : fishP.marked()) && (globals_1.maxTime - fishP.unmarkTime < 20000 ? - "&lris marked forever&fr" - : "&lris marked&fr until ".concat((0, utils_1.formatTimeRelative)(fishP.unmarkTime))), - (fishP === null || fishP === void 0 ? void 0 : fishP.muted) && "&lris muted&fr", - (fishP === null || fishP === void 0 ? void 0 : fishP.hasFlag("member")) && "&lmis member&fr", - (fishP === null || fishP === void 0 ? void 0 : fishP.autoflagged) && "&lris autoflagged&fr", - playerInfo.banned && "&bris UUID banned&fr", - ].filter(Boolean).join(", "); - var lastJoinedColor = (fishP === null || fishP === void 0 ? void 0 : fishP.lastJoined) && fishP.lastJoined !== -1 ? (function () { - var timeSinceLastJoin = (Date.now() - fishP.lastJoined) / 1000; - if (timeSinceLastJoin < 3600) - return "&br"; - if (timeSinceLastJoin < 24 * 3600) - return "&by"; - if (timeSinceLastJoin < 7 * 24 * 3600) - return "&lw"; - return "&lk"; - })() : "&fr"; - outputString.push([ - "".concat(lastJoinedColor, "Trace info for player &fr&y").concat(playerInfo.id, "&fr").concat(lastJoinedColor, " / &c\"").concat((0, funcs_1.escapeStringColorsServer)(Strings.stripColors(playerInfo.lastName)), "\" &lk(").concat((0, funcs_1.escapeStringColorsServer)(playerInfo.lastName), ")&fr"), - playerInfo.names.size > 1 && "all names used: ".concat(playerInfo.names.map(funcs_1.escapeStringColorsServer).map(function (n) { return "&c\"".concat(n, "\"&fr"); }).items.join(', ')), - "all IPs used: ".concat(playerInfo.ips.map(function (n) { return (n == playerInfo.lastIP ? '&c' : '&w') + n + '&fr'; }).items.join(", ")), - "joined &c".concat(playerInfo.timesJoined, "&fr times, kicked &c").concat(playerInfo.timesKicked, "&fr times"), - fishP && fishP.lastJoined !== -1 && "Last joined: ".concat((0, utils_1.formatTimeRelative)(fishP.lastJoined)), - fishP && fishP.firstJoined !== -1 && (0, utils_1.formatTimeRelative)(fishP.firstJoined), - fishP && "USID: &c".concat(fishP.usid, "&fr"), - fishP && fishP.rank !== ranks_1.Rank.player && "Rank: &c".concat(fishP.rank.name, "&fr"), - flagsText, - ].filter(Boolean).map(function (l, i) { return i == 0 ? l : '\t' + l; }).join("\n")); - }; - try { - for (var infoList_1 = __values(infoList), infoList_1_1 = infoList_1.next(); !infoList_1_1.done; infoList_1_1 = infoList_1.next()) { - var _b = __read(infoList_1_1.value, 2), playerInfo = _b[0], fishP = _b[1]; - _loop_1(playerInfo, fishP); - } - } - catch (e_2_1) { e_2 = { error: e_2_1 }; } - finally { - try { - if (infoList_1_1 && !infoList_1_1.done && (_a = infoList_1.return)) _a.call(infoList_1); - } - finally { if (e_2) throw e_2.error; } - } - output(outputString.join("\n")); - } - var infoList, playersToFetch, _c, _d, batch, e_1_1, err_1; - var e_1, _e; - var _this = this; - var args = _b.args, output = _b.output, admins = _b.admins; - return __generator(this, function (_f) { - switch (_f.label) { - case 0: - infoList = admins.findByName(args.player) - .toSeq().toArray() - .map(function (p) { return [p, players_1.FishPlayer.getById(p.id)]; }); - if (infoList.length == 0) - (0, commands_1.fail)("No players found."); - playersToFetch = infoList.filter(function (_a) { - var _b = __read(_a, 2), a = _b[0], b = _b[1]; - return !b; - }).map(function (_a) { - var _b = __read(_a, 2), a = _b[0], b = _b[1]; - return a; - }); - if (!(playersToFetch.length == 0)) return [3 /*break*/, 1]; - display(infoList); - return [3 /*break*/, 13]; - case 1: - //Attempt to fetch data - //If there are too many players, give up - if (playersToFetch.length > 50) - display(infoList); - output("Fetching data..."); - _f.label = 2; - case 2: - _f.trys.push([2, 11, , 12]); - _f.label = 3; - case 3: - _f.trys.push([3, 8, 9, 10]); - _c = __values((0, funcs_1.to2DArray)(playersToFetch, 10)), _d = _c.next(); - _f.label = 4; - case 4: - if (!!_d.done) return [3 /*break*/, 7]; - batch = _d.value; - return [4 /*yield*/, Promise.all(batch.map(function (info) { return __awaiter(_this, void 0, void 0, function () { - var data, fishP; - return __generator(this, function (_a) { - switch (_a.label) { - case 0: return [4 /*yield*/, api.getFishPlayerData(info.id)]; - case 1: - data = _a.sent(); - if (data) { - fishP = players_1.FishPlayer.createFromInfo(info); - fishP.updateData(data); - players_1.FishPlayer.cachedPlayers[info.id] = fishP; - } - return [2 /*return*/]; - } - }); - }); }))]; - case 5: - _f.sent(); - _f.label = 6; - case 6: - _d = _c.next(); - return [3 /*break*/, 4]; - case 7: return [3 /*break*/, 10]; - case 8: - e_1_1 = _f.sent(); - e_1 = { error: e_1_1 }; - return [3 /*break*/, 10]; - case 9: - try { - if (_d && !_d.done && (_e = _c.return)) _e.call(_c); - } - finally { if (e_1) throw e_1.error; } - return [7 /*endfinally*/]; - case 10: return [3 /*break*/, 12]; - case 11: - err_1 = _f.sent(); - Log.err(err_1); - return [3 /*break*/, 12]; - case 12: - infoList = admins.findByName(args.player) - .toSeq().toArray() - .map(function (p) { return [p, players_1.FishPlayer.getById(p.id)]; }); - display(infoList); - _f.label = 13; - case 13: return [2 /*return*/]; - } - }); - }); - } - }, - infoonline: { - args: ["player:string"], - description: "Display information about an online player.", - handler: function (_a) { - var e_3, _b; - var args = _a.args, output = _a.output, admins = _a.admins; - var infoList = args.player == "*" ? players_1.FishPlayer.getAllOnline() : players_1.FishPlayer.getAllByName(args.player, false); - if (infoList.length == 0) - (0, commands_1.fail)("Nobody with that name could be found."); - var outputString = [""]; - var _loop_2 = function (player) { - var playerInfo = admins.getInfo(player.uuid); - outputString.push("Info for player &c\"".concat(player.cleanedName, "\" &lk(").concat(player.name, ")&fr\n\tUUID: &c\"").concat(playerInfo.id, "\"&fr\n\tUSID: &c").concat(player.usid ? "\"".concat(player.usid, "\"") : "unknown", "&fr\n\tall names used: ").concat(playerInfo.names.map(function (n) { return "&c\"".concat(n, "\"&fr"); }).items.join(', '), "\n\tall IPs used: ").concat(playerInfo.ips.map(function (n) { return (n == playerInfo.lastIP ? '&c' : '&w') + n + '&fr'; }).items.join(", "), "\n\tjoined &c").concat(playerInfo.timesJoined, "&fr times, kicked &c").concat(playerInfo.timesKicked, "&fr times\n\trank: &c").concat(player.rank.name, "&fr").concat((player.marked() ? ", &lris marked&fr" : "") + (player.muted ? ", &lris muted&fr" : "") + (player.hasFlag("member") ? ", &lmis member&fr" : "") + (player.autoflagged ? ", &lris autoflagged&fr" : ""))); - }; - try { - for (var infoList_2 = __values(infoList), infoList_2_1 = infoList_2.next(); !infoList_2_1.done; infoList_2_1 = infoList_2.next()) { - var player = infoList_2_1.value; - _loop_2(player); - } - } - catch (e_3_1) { e_3 = { error: e_3_1 }; } - finally { - try { - if (infoList_2_1 && !infoList_2_1.done && (_b = infoList_2.return)) _b.call(infoList_2); - } - finally { if (e_3) throw e_3.error; } - } - output(outputString.join("\n")); - } - }, - unblacklist: { - args: ["ip:string"], - description: "Unblacklists an ip from the DOS blacklist.", - handler: function (_a) { - var args = _a.args, output = _a.output, admins = _a.admins; - if (args.ip === '*') { - var size = admins.dosBlacklist.size; - if (size == 0) - (0, commands_1.fail)('DOS blacklist is already empty.'); - admins.dosBlacklist.clear(); - output("Cleared ".concat(size, " IPs from the DOS blacklist.")); - } - else { - if (admins.dosBlacklist.remove(args.ip)) - output("Removed ".concat(args.ip, " from the DOS blacklist.")); - else - (0, commands_1.fail)("IP address ".concat(args.ip, " is not DOS blacklisted.")); - } - } - }, - blacklist: { - args: ["verbose:boolean?"], - description: "Allows you to view the DOS blacklist.", - handler: function (_a) { - var args = _a.args, output = _a.output, admins = _a.admins; - var blacklist = admins.dosBlacklist; - if (blacklist.isEmpty()) - (0, commands_1.fail)("The blacklist is empty"); - if (args.verbose) { - var outputString_1 = ["DOS Blacklist:"]; - blacklist.each(function (ip) { - var info = admins.findByIP(ip); - if (info) { - outputString_1.push("IP: &c".concat(ip, "&fr UUID: &c\"").concat(info.id, "\"&fr Last name used: &c\"").concat(info.plainLastName(), "\"&fr")); - } - }); - output(outputString_1.join("\n")); - output("".concat(blacklist.size, " blacklisted IPs")); - } - else { - output(blacklist.toString()); - output("".concat(blacklist.size, " blacklisted IPs")); - } - } - }, - whack: { - args: ["target:string"], - description: "Whacks (ipbans) a player.", - handler: function (_a) { - var args = _a.args, output = _a.output, outputFail = _a.outputFail, admins = _a.admins; - var range; - if (globals_1.ipPattern.test(args.target)) { - //target is an ip - api.ban({ ip: args.target }); - var info = admins.findByIP(args.target); - if (info) - (0, utils_1.logAction)("whacked", "console", info); - else - (0, utils_1.logAction)("console ip-whacked ".concat(args.target)); - if (admins.isIPBanned(args.target)) { - output("IP &c\"".concat(args.target, "\"&fr is already banned. Ban was synced to other servers.")); - } - else { - admins.banPlayerIP(args.target); - output("&lrIP &c\"".concat(args.target, "\"&lr was banned. Ban was synced to other servers.")); - } - } - else if ((range = (0, utils_1.getIPRange)(args.target)) != null) { - if (admins.subnetBans.contains(boolf(function (ip) { return ip.replace(/\.$/, "") == range; }))) { - output("Subnet &c\"".concat(range, "\"&fr is already banned.")); - } - else { - admins.subnetBans.add(range); - output("&lrIP range &c\"".concat(range, "\"&lr was banned. Subnet bans are not synced.")); - } - } - else if (globals_1.uuidPattern.test(args.target)) { - var info = admins.getInfoOptional(args.target); - if (info) - (0, utils_1.logAction)("whacked", "console", info); - else - (0, utils_1.logAction)("console ip-whacked ".concat(args.target)); - if (admins.isIDBanned(args.target)) { - api.ban({ uuid: args.target }); - output("UUID &c\"".concat(args.target, "\"&fr is already banned. Ban was synced to other servers.")); - } - else { - admins.banPlayerID(args.target); - if (info) { - admins.banPlayerIP(info.lastIP); - api.ban({ uuid: args.target, ip: info.lastIP }); - output("&lrUUID &c\"".concat(args.target, "\" &lrwas banned. IP &c\"").concat(info.lastIP, "\"&lr was banned. Ban was synced to other servers.")); - } - else { - api.ban({ uuid: args.target }); - output("&lrUUID &c\"".concat(args.target, "\" &lrwas banned. Ban was synced to other servers. Warning: no stored info for this UUID, player may not exist. Unable to determine IP.")); - } - } - } - else { - var player = players_1.FishPlayer.getOneMindustryPlayerByName(args.target); - if (player === "none") { - outputFail("Could not find a player name matching &c\"".concat(args.target, "\"")); - } - else if (player === "multiple") { - outputFail("Name &c\"".concat(args.target, "\"&fr could refer to more than one player.")); - } - else { - if (player.admin) - (0, commands_1.fail)("Player &c\"".concat(player.name, "\"&fr is an admin, you probably don't want to ban them.")); - var ip = player.ip(); - var uuid = player.uuid(); - admins.banPlayerID(uuid); - admins.banPlayerIP(ip); - (0, utils_1.logAction)("console whacked ".concat(Strings.stripColors(player.name), " (`").concat(uuid, "`/`").concat(ip, "`)")); - api.ban({ uuid: uuid, ip: ip }); - output("&lrIP &c\"".concat(ip, "\"&lr was banned. UUID &c\"").concat(uuid, "\"&lr was banned. Ban was synced to other servers.")); - } - } - (0, utils_1.updateBans)(function (player) { return "[scarlet]Player [yellow]".concat(player.name, "[scarlet] has been whacked."); }); - } - }, - unwhack: { - args: ["target:string"], - description: "Unbans a player.", - handler: function (_a) { - var args = _a.args, output = _a.output, admins = _a.admins; - var range; - if (globals_1.ipPattern.test(args.target)) { - //target is an ip - if (players_1.FishPlayer.removePunishedIP(args.target)) { - output("Removed IP &c\"".concat(args.target, "\"&fr from the anti-evasion list.")); - } - if (admins.kickedIPs.remove(args.target)) { - output("Removed temporary kick for IP &c\"".concat(args.target, "\"&fr.")); - } - output("Checking ban status..."); - api.getBanned({ ip: args.target }, function (banned) { - if (banned) { - api.unban({ ip: args.target }); - (0, utils_1.logAction)("console unbanned ip `".concat(args.target, "`")); - output("IP &c\"".concat(args.target, "\"&fr has been globally unbanned.")); - } - else { - output("IP &c\"".concat(args.target, "\"&fr is not globally banned.")); - } - if (admins.isIPBanned(args.target)) { - admins.unbanPlayerIP(args.target); - output("IP &c\"".concat(args.target, "\"&fr has been locally unbanned.")); - } - else { - output("IP &c\"".concat(args.target, "\"&fr was not locally banned.")); - } - var size = admins.subnetBans.size; - admins.subnetBans.removeAll(function (r) { return args.target.startsWith(r); }); - if (admins.subnetBans.size < size) { - output("Unbanned IP ranges affecting this IP."); - } - }); - } - else if ((range = (0, utils_1.getIPRange)(args.target)) != null) { - if (admins.subnetBans.remove(function (b) { return b.replace(/\.$/, ".") == range.replace(/\.$/, "."); })) { - output("IP range &c\"".concat(range, "\"&fr was unbanned.")); - } - else { - output("IP range &c\"".concat(range, "\"&fr was not banned.")); - } - } - else if (globals_1.uuidPattern.test(args.target)) { - if (players_1.FishPlayer.removePunishedUUID(args.target)) { - output("Removed UUID &c\"".concat(args.target, "\"&fr from the anti-evasion list.")); - } - output("Checking ban status..."); - var info_1 = admins.findByIP(args.target); - api.getBanned({ uuid: args.target }, function (banned) { - if (banned) { - api.unban({ uuid: args.target }); - (0, utils_1.logAction)("console unbanned uuid `".concat(args.target, "`")); - output("UUID &c\"".concat(args.target, "\"&fr has been globally unbanned.")); - } - else { - output("UUID &c\"".concat(args.target, "\"&fr is not globally banned.")); - } - if (admins.isIDBanned(args.target)) { - admins.unbanPlayerID(args.target); - output("UUID &c\"".concat(args.target, "\"&fr has been locally unbanned.")); - } - else { - output("UUID &c\"".concat(args.target, "\"&fr was not locally banned.")); - } - if (info_1) { - if (info_1.lastKicked > 0) { - info_1.lastKicked = 0; - output("Removed temporary kick for UUID &c\"".concat(args.target, "\"&fr.")); - } - output("You may also want to consider unbanning the IP \"".concat(info_1.lastIP, "\".")); - } - }); - } - else { - (0, commands_1.fail)("Cannot unban by name; please use the info or search commands to find the IP and UUID of the player you are looking for."); - } - } - }, - ban: { - args: ["any:string"], - description: "Please use the whack command instead.", - handler: function () { - (0, commands_1.fail)("Use the whack command instead."); - } - }, - unban: { - args: ["any:string"], - description: "Please use the unwhack command instead.", - handler: function () { - (0, commands_1.fail)("Use the unwhack command instead."); - } - }, - "subnet-ban": { - args: ["any:string?", "anyb:string?"], - description: "Please use the whack and unwhack commands instead.", - handler: function (_a) { - var args = _a.args, output = _a.output, admins = _a.admins; - if (args.any && args.any != "list") - (0, commands_1.fail)("Use the whack and unwhack commands instead."); - output("List of all subnet bans:"); - output(admins.subnetBans.toString("\n")); - } - }, - joinbell: { - args: ["on:boolean?"], - description: "Toggles the join bell function.", - handler: function (_a) { - var _b = _a.args.on, on = _b === void 0 ? !globals_1.fishState.joinBell : _b; - globals_1.fishState.joinBell = on; - if (globals_1.fishState.joinBell) { - Log.info("Enabled sound on new player join. Run \"joinbell\" again to turn it off."); - } - else { - Log.info("Disabled sound on new player join."); - } - } - }, - loadfishplayerdata: { - args: ["areyousure:boolean", "fishplayerdata:string"], - description: "Overwrites current fish player data.", - handler: function (_a) { - var args = _a.args, output = _a.output; - if (args.areyousure) { - var before = Object.keys(players_1.FishPlayer.cachedPlayers).length; - players_1.FishPlayer.loadAll(args.fishplayerdata); - output("Loaded fish player data. before:".concat(before, ", after:").concat(Object.keys(players_1.FishPlayer.cachedPlayers).length)); - } - } - }, - resetauth: { - args: ["player:string"], - description: "Removes the USID of the player provided, use this if they are getting kicked with the message \"Authorization failure!\". Specify \"last\" to use the last player that got kicked.", - handler: function (_a) { - var _b, _c; - var args = _a.args, outputSuccess = _a.outputSuccess, outputFail = _a.outputFail, admins = _a.admins; - var player = args.player == "last" ? ((_b = players_1.FishPlayer.lastAuthKicked) !== null && _b !== void 0 ? _b : (0, commands_1.fail)("Nobody has been kicked for authorization failure since the last restart.")) : - (_c = players_1.FishPlayer.getById(args.player)) !== null && _c !== void 0 ? _c : (0, commands_1.fail)(admins.getInfoOptional(args.player) - ? "Player ".concat(args.player, " has joined the server, but their info was not cached, most likely because they have no rank, so there is no stored USID.") - : "Unknown player ".concat(args.player)); - if (player.ranksAtLeast("admin")) - (0, commands_1.fail)("Please use the setusid command instead."); - var oldusid = player.usid; - player.usid = null; - api.setFishPlayerData(player.getData(), 1, true).then(function () { - outputSuccess("Removed the usid of player ".concat(player.name, "/").concat(player.uuid, " (was ").concat(oldusid, ")")); - }).catch(function (err) { - Log.err(err); - outputFail("Failed to remove the usid, please try running the command again."); - }); - } - }, - setusid: { - args: ["uuid:string", "usid:string"], - description: "Sets the USID of a player.", - handler: function (_a) { - var _b; - var args = _a.args, outputSuccess = _a.outputSuccess, outputFail = _a.outputFail, f = _a.f; - if (args.usid.length !== 12) - (0, commands_1.fail)("Invalid USID: should be 12 characters ending with an equal sign"); - var player = (_b = players_1.FishPlayer.lastAuthKicked) !== null && _b !== void 0 ? _b : (0, commands_1.fail)("No authorization failures have occurred since the last restart."); - var oldusid = player.usid; - player.usid = args.usid; - api.setFishPlayerData(player.getData(), 1, true).then(function () { - outputSuccess("Set the usid of player ".concat(player.name, "/").concat(player.uuid, " to ").concat(args.usid, " (was ").concat(oldusid, ")")); - }).catch(function (err) { - Log.err(err); - outputFail("Failed to remove the usid, please try running the command again."); - }); - } - }, - update: { - args: ["branch:string?"], - description: "Updates the plugin.", - handler: function (_a) { - var args = _a.args, output = _a.output, outputSuccess = _a.outputSuccess, outputFail = _a.outputFail; - if (config_1.Mode.localDebug) - (0, commands_1.fail)("Cannot update in local debug mode."); - output("Updating..."); - var path = (0, utils_1.fishCommandsRootDirPath)().toString(); - Threads.thread(function () { - var _a, _b; - try { - var initialVersion = OS.exec("git", "-C", path, "rev-parse", "HEAD"); - var gitFetch = new ProcessBuilder("git", "-C", path, "fetch", "origin") - .redirectErrorStream(true) - .redirectOutput(ProcessBuilder.Redirect.INHERIT) - .start(); - gitFetch.waitFor(); - if (gitFetch.exitValue() == 0) { - outputSuccess("Fetched data, updating files..."); - } - else { - outputFail("Update failed!"); - return; - } - var newVersion = OS.exec("git", "-C", path, "rev-parse", "origin/".concat((_a = args.branch) !== null && _a !== void 0 ? _a : "master")); - if (initialVersion == newVersion) { - outputSuccess("Already up to date."); - return; - } - var gitCheckout = new ProcessBuilder("git", "-C", path, "checkout", "-q", "-f", "origin/".concat((_b = args.branch) !== null && _b !== void 0 ? _b : "master")) - .redirectErrorStream(true) - .redirectOutput(ProcessBuilder.Redirect.INHERIT) - .start(); - gitCheckout.waitFor(); - if (gitCheckout.exitValue() == 0) { - outputSuccess("Updated successfully from ".concat(initialVersion, " to ").concat(newVersion, ". Restart to apply changes.")); - } - else { - outputFail("Update failed!"); - return; - } - } - catch (err) { - Log.err(err); - outputFail("Update failed!"); - } - }); - } - }, - restart: { - args: ["time:number?"], - description: "Restarts the server.", - handler: function (_a) { - var _b; - var time = _a.args.time; - (_b = globals_1.fishState.restartLoopTask) === null || _b === void 0 ? void 0 : _b.cancel(); - var timeInferred = time == undefined; - if (Groups.player.isEmpty()) { - if (time == undefined) { - Log.info("Restarting immediately as no players are online."); - time !== null && time !== void 0 ? time : (time = 0); - } - } - else if (config_1.Gamemode.pvp()) { - time !== null && time !== void 0 ? time : (time = -1); - } - else { - time !== null && time !== void 0 ? time : (time = 60); - } - if (time == -1) { - Call.sendMessage("[accent]---[[[coral]+++[]]---\n[accent]Server restart queued. The server will restart after the current match is over.[]\n[accent]---[[[coral]+++[]]---"); - if (config_1.Gamemode.pvp() && timeInferred) - Log.info("PVP: restart will occur at the end of the current game. Specify a time to override, but &rthat would interrupt the current pvp match, and players would lose their teams.&fr"); - else - Log.info("Restarting once the current game ends."); - globals_1.fishState.restartQueued = true; - } - else { - if (time < 0 || time > 100) - (0, commands_1.fail)("Invalid time: out of valid range."); - (0, utils_1.serverRestartLoop)(time); - if (time == 0) - Log.info("Restarting now."); - else - Log.info("Restarting in ".concat(time, " second").concat(time == 1 ? "" : "s", ".")); - if (config_1.Gamemode.pvp()) { - Call.sendMessage("[accent]---[[[coral]+++[]]---\n[accent]Server restart imminent. [green]We'll be back after 20 seconds.[]\n[accent]---[[[coral]+++[]]---"); - } - else { - Call.sendMessage("[accent]---[[[coral]+++[]]---\n[accent]Server restart imminent. [green]We'll be back after 20 seconds, and all progress will be saved.[]\n[accent]---[[[coral]+++[]]---"); - } - } - } - }, - restartcancel: { - args: [], - description: "Cancels a planned server restart.", - handler: function (_a) { - var _b; - var outputSuccess = _a.outputSuccess; - var task = (_b = globals_1.fishState.restartLoopTask) !== null && _b !== void 0 ? _b : (0, commands_1.fail)("No restart scheduled."); - Call.sendMessage("[scarlet]Aborting..."); - task.cancel(); - Call.sendMessage("[scarlet]Server restart canceled."); - outputSuccess("Canceled restart."); - } - }, - rename: { - args: ["player:player", "newname:string"], - description: "Changes the name of a player.", - handler: function (_a) { - var args = _a.args, f = _a.f, outputSuccess = _a.outputSuccess; - if (args.player.hasPerm("blockTrolling")) - (0, commands_1.fail)(f(templateObject_5 || (templateObject_5 = __makeTemplateObject(["Operation aborted: Player ", " is insufficiently trollable."], ["Operation aborted: Player ", " is insufficiently trollable."])), args.player)); - var oldName = args.player.name; - args.player.player.name = args.player.prefixedName = args.newname; - args.player.shouldUpdateName = false; - outputSuccess("Renamed ".concat(oldName, " to ").concat(args.newname, ".")); - } - }, - fjs: { - args: ["js:string"], - description: "Executes arbitrary javascript code, but has access to fish-commands's variables.", - handler: function (_a) { - var args = _a.args; - fjsContext.runJS(args.js); - } - }, - checkmem: { - args: [], - description: "Checks memory usage of various objects.", - handler: function (_a) { - var output = _a.output; - output("Memory usage:\nTotal: ".concat(Math.round(Core.app.getJavaHeap() / (Math.pow(2, 10))), " KB\nNumber of cached fish players: ").concat(Object.keys(players_1.FishPlayer.cachedPlayers).length, " (stored locally: ").concat(Object.values(players_1.FishPlayer.cachedPlayers).filter(function (p) { return p.shouldCache(); }).length, ")\nFish player data string length: ").concat(players_1.FishPlayer.getFishPlayersString.length, " (").concat(Core.settings.getInt("fish-subkeys"), " subkeys)\nLength of tilelog entries: ").concat(Math.round(Object.values(globals_1.tileHistory).reduce(function (acc, a) { return acc + a.length; }, 0) / (Math.pow(2, 10))), " KB")); - } - }, - stopplayer: { - args: ['player:player', "time:time?", "message:string?"], - description: 'Stops a player.', - handler: function (_a) { - return __awaiter(this, arguments, void 0, function (_b) { - var previousTime, time; - var _c, _d, _e; - var args = _b.args, f = _b.f, outputSuccess = _b.outputSuccess; - return __generator(this, function (_f) { - switch (_f.label) { - case 0: - if (!args.player.marked()) return [3 /*break*/, 2]; - //overload: overwrite stoptime - if (!args.time) - (0, commands_1.fail)(f(templateObject_6 || (templateObject_6 = __makeTemplateObject(["Player ", " is already marked."], ["Player ", " is already marked."])), args.player)); - previousTime = (0, utils_1.formatTime)(args.player.unmarkTime - Date.now()); - return [4 /*yield*/, args.player.updateStopTime(args.time)]; - case 1: - _f.sent(); - outputSuccess(f(templateObject_7 || (templateObject_7 = __makeTemplateObject(["Player ", "'s stop time has been updated to ", " (was ", ")."], ["Player ", "'s stop time has been updated to ", " (was ", ")."])), args.player, (0, utils_1.formatTime)(args.time), previousTime)); - return [2 /*return*/]; - case 2: - time = (_c = args.time) !== null && _c !== void 0 ? _c : funcs_1.Duration.days(7); - if (time + Date.now() > globals_1.maxTime) - (0, commands_1.fail)("Error: time too high."); - return [4 /*yield*/, args.player.stop("console", time, (_d = args.message) !== null && _d !== void 0 ? _d : undefined)]; - case 3: - _f.sent(); - (0, utils_1.logAction)('stopped', "console", args.player, (_e = args.message) !== null && _e !== void 0 ? _e : undefined, time); - Call.sendMessage("[scarlet]Player \"".concat(args.player.prefixedName, "[scarlet]\" has been marked for ").concat((0, utils_1.formatTime)(time)).concat(args.message ? " with reason: [white]".concat(args.message, "[]") : "", ".")); - return [2 /*return*/]; - } - }); - }); - } - }, - stopoffline: { - args: ["uuid:uuid", "time:time?"], - description: "Stops a player by uuid.", - handler: function (_a) { - return __awaiter(this, arguments, void 0, function (_b) { - var stopTime, info, fishP; - var _c = _b.args, uuid = _c.uuid, time = _c.time, outputSuccess = _b.outputSuccess, admins = _b.admins; - return __generator(this, function (_d) { - switch (_d.label) { - case 0: - stopTime = time !== null && time !== void 0 ? time : (globals_1.maxTime - Date.now() - 10000); - info = admins.getInfoOptional(uuid); - if (info == null) - (0, commands_1.fail)("Unknown player ".concat(uuid)); - fishP = players_1.FishPlayer.getFromInfo(info); - return [4 /*yield*/, fishP.stop("console", stopTime)]; - case 1: - _d.sent(); - (0, utils_1.logAction)('stopped', "console", info, undefined, stopTime); - outputSuccess("Player \"".concat(info.lastName, "\" was marked for ").concat((0, utils_1.formatTime)(stopTime), ".")); - return [2 /*return*/]; - } - }); - }); - } - }, - clearfire: { - args: [], - description: "Clears all the fires.", - handler: function (_a) { - var output = _a.output, outputSuccess = _a.outputSuccess; - output("Removing fires..."); - var totalRemoved = 0; - Call.sendMessage("[scarlet][[Fire Department]:[yellow] Fires were reported. Trucks are en-route. Removing all fires shortly."); - Timer.schedule(function () { - totalRemoved += Groups.fire.size(); - Groups.fire.each(function (f) { return f.remove(); }); - Groups.fire.clear(); - }, 2, 0.1, 40); - Timer.schedule(function () { - outputSuccess("Removed ".concat(totalRemoved, " fires.")); - Call.sendMessage("[scarlet][[Fire Department]:[yellow] We've extinguished ".concat(totalRemoved, " fires.")); - }, 6.1); - } - }, - status: { - args: [], - description: "Displays server status.", - handler: function (_a) { - var output = _a.output; - if (Vars.state.isMenu()) - (0, commands_1.fail)("Status: Server closed."); - var uptime = Packages.java.lang.management.ManagementFactory.getRuntimeMXBean().getUptime(); - var numStaff = 0; - players_1.FishPlayer.forEachPlayer(function (p) { - if (p.ranksAtLeast("mod")) - numStaff++; - }); - var uptimeColor = uptime < funcs_1.Duration.days(2) ? "" : - uptime < funcs_1.Duration.days(5) ? "&ly" : - uptime < funcs_1.Duration.days(9) ? "&y" : - "&br"; - output("\nStatus:\nPlaying on map &fi".concat(Vars.state.map.plainName(), "&fr for ").concat((0, utils_1.formatTime)(1000 * Vars.state.tick / 60), "\n").concat(Vars.state.rules.waves ? "Wave &c".concat(Vars.state.wave, "&fr, &c").concat(Math.ceil(Vars.state.wavetime / 60), "&fr seconds until next wave.\n") : "", "&c").concat(Groups.unit.size(), "&fr units, &c").concat(Vars.state.enemies, "&fr enemies, &c").concat(Groups.build.size(), "&fr buildings\nTPS: ").concat((0, utils_1.colorNumber)(Core.graphics.getFramesPerSecond(), function (f) { return f > 58 ? "&g" : f > 30 ? "&y" : f > 10 ? "&r" : "&br&w"; }, "server"), ", Memory: &c").concat(Math.round(Core.app.getJavaHeap() / 1048576), "&fr MB\nServer uptime: ").concat(uptimeColor).concat((0, utils_1.formatTime)(uptime), "&fr (since ").concat((0, utils_1.formatTimestampFull)(Date.now() - uptime), ")\n").concat([ - globals_1.fishState.restartQueued ? "&by&lwRestart queued&fr" : "", - globals_1.fishState.restartLoopTask ? "&by&lwRestarting now&fr" : "", - players_1.FishPlayer.antiBotMode() ? "&br&wANTIBOT ACTIVE!&fr" + (0, utils_1.getAntiBotInfo)("server") : "", - ].filter(function (l) { return l.length > 0; }).join("\n"), "\n").concat((0, utils_1.colorNumber)(Groups.player.size(), function (n) { return n > 0 ? "&c" : "&lr"; }, "server"), " players online, ").concat((0, utils_1.colorNumber)(numStaff, function (n) { return n > 0 ? "&c" : "&lr"; }, "server"), " staff members.\n").concat(players_1.FishPlayer.mapPlayers(function (p) { - return "\t".concat(p.rank.shortPrefix, " &c").concat(p.uuid, "&fr &c").concat(p.name, "&fr"); - }).join("\n") || "&lrNo players connected.&fr", "\n")); - } - }, - tmux: { - args: ["attach:string"], - description: "Oopsie", - handler: function () { - (0, commands_1.fail)("You are already in the Mindustry server console. Please regain situational awareness before running any further commands."); - } - }, - BEGIN: { - args: ["transaction:string"], - description: "Oopsie", - handler: function (_a) { - var args = _a.args; - if (args.transaction == "TRANSACTION") - (0, commands_1.fail)("Not possible :( please download and run locally, and make a backup"); - else - (0, commands_1.fail)("Command not found. Did you mean \"BEGIN TRANSACTION\"?"); - } - }, - prune: { - args: ["confirm:boolean?"], - description: "Prunes fish player data", - handler: function (_a) { - var args = _a.args, admins = _a.admins, outputSuccess = _a.outputSuccess, outputFail = _a.outputFail; - var playersToPrune = Object.values(players_1.FishPlayer.cachedPlayers) - .filter(function (player) { - if (player.hasData()) - return false; - var data = admins.getInfoOptional(player.uuid); - return (!data || - data.timesJoined == 1 || - (data.timesJoined < 10 && - (Date.now() - player.lastJoined) > funcs_1.Duration.months(1))); - }); - if (args.confirm) { - outputSuccess("Creating backup..."); - var backupScript = Core.settings.getDataDirectory().child("backup.sh"); - if (!backupScript.exists()) - (0, commands_1.fail)("./backup.sh does not exist! aborting"); - var backupProcess_1 = new ProcessBuilder(backupScript.absolutePath()) - .directory(Core.settings.getDataDirectory().file()) - .redirectErrorStream(true) - .redirectOutput(ProcessBuilder.Redirect.INHERIT) - .start(); - Threads.daemon(function () { - backupProcess_1.waitFor(); - if (backupProcess_1.exitValue() == 0) { - outputSuccess("Successfully created a backup."); - Core.app.post(function () { - playersToPrune.forEach(function (u) { delete players_1.FishPlayer.cachedPlayers[u.uuid]; }); - outputSuccess("Pruned ".concat(playersToPrune.length, " players.")); - }); - } - else { - outputFail("Backup failed!"); - } - }); - } - else { - outputSuccess("Pruning would remove fish data for ".concat(playersToPrune.length, " players with no data and (1 join or inactive with <10 joins). (Mindustry data will remain.)\nRun \"prune y\" to prune data.")); - } - } - }, - backup: { - args: [], - description: "Creates a backup of the settings.bin file.", - handler: function (_a) { - var output = _a.output, outputFail = _a.outputFail, outputSuccess = _a.outputSuccess; - output("Creating backup..."); - var backupScript = Core.settings.getDataDirectory().child("backup.sh"); - if (!backupScript.exists()) - (0, commands_1.fail)("./backup.sh does not exist! aborting"); - var backupProcess = new ProcessBuilder(backupScript.absolutePath()) - .directory(Core.settings.getDataDirectory().file()) - .redirectErrorStream(true) - .redirectOutput(ProcessBuilder.Redirect.INHERIT) - .start(); - Threads.daemon(function () { - backupProcess.waitFor(); - if (backupProcess.exitValue() == 0) - outputSuccess("Successfully created a backup."); - else - outputFail("Backup failed!"); - }); - } - }, - updateMaps: { - args: [], - description: 'Attempt to fetch and update all map files', - handler: function (_a) { - var output = _a.output, outputSuccess = _a.outputSuccess, outputFail = _a.outputFail; - output("Updating maps... (this may take a while)"); - (0, files_1.updateMaps)() - .then(function (changed) { return outputSuccess(changed ? "Maps were updated." : "Map update completed, already up to date."); }) - .catch(function (message) { return outputFail("Map update failed: ".concat(String(message))); }); - }, - }, - switchall: { - args: ["server:string"], - description: "Forces all currently online players to another server.", - handler: function (_a) { - var _b; - var args = _a.args; - if (globals_1.ipPortPattern.test(args.server)) { - Groups.player.each(function (target) { - //direct connect - Call.connect.apply(Call, __spreadArray([target.con], __read(args.server.split(":")), false)); - }); - } - else { - var server_1 = (_b = config_1.FishServer.byName(args.server)) !== null && _b !== void 0 ? _b : (0, commands_1.fail)("Unknown server ".concat(args.server, ". Valid options: ").concat(config_1.FishServer.all.map(function (s) { return s.name; }).join(", "))); - Groups.player.each(function (target) { - Call.connect(target.con, server_1.ip, server_1.port); - }); - } - } - }, - mute: { - args: ['player:player'], - description: 'Stops a player from chatting.', - handler: function (_a) { - return __awaiter(this, arguments, void 0, function (_b) { - var args = _b.args, outputSuccess = _b.outputSuccess, f = _b.f; - return __generator(this, function (_c) { - switch (_c.label) { - case 0: - if (args.player.muted) - (0, commands_1.fail)(f(templateObject_8 || (templateObject_8 = __makeTemplateObject(["Player ", " is already muted."], ["Player ", " is already muted."])), args.player)); - return [4 /*yield*/, args.player.mute("console")]; - case 1: - _c.sent(); - (0, utils_1.logAction)('muted', "console", args.player); - outputSuccess(f(templateObject_9 || (templateObject_9 = __makeTemplateObject(["Muted player ", "."], ["Muted player ", "."])), args.player)); - return [2 /*return*/]; - } - }); - }); - } - }, - unmute: { - args: ['player:player'], - description: 'Unmutes a player', - handler: function (_a) { - return __awaiter(this, arguments, void 0, function (_b) { - var args = _b.args, outputSuccess = _b.outputSuccess, f = _b.f; - return __generator(this, function (_c) { - switch (_c.label) { - case 0: - if (!args.player.muted && args.player.autoflagged) - (0, commands_1.fail)(f(templateObject_10 || (templateObject_10 = __makeTemplateObject(["Player ", " is not muted, but they are autoflagged. You probably want to free them with /free."], ["Player ", " is not muted, but they are autoflagged. You probably want to free them with /free."])), args.player)); - if (!args.player.muted) - (0, commands_1.fail)(f(templateObject_11 || (templateObject_11 = __makeTemplateObject(["Player ", " is not muted."], ["Player ", " is not muted."])), args.player)); - return [4 /*yield*/, args.player.unmute("console")]; - case 1: - _c.sent(); - (0, utils_1.logAction)('unmuted', "console", args.player); - outputSuccess(f(templateObject_12 || (templateObject_12 = __makeTemplateObject(["Unmuted player ", "."], ["Unmuted player ", "."])), args.player)); - return [2 /*return*/]; - } - }); - }); - } - }, - free: { - args: ['player:player'], - description: 'Frees a player.', - handler: function (_a) { - return __awaiter(this, arguments, void 0, function (_b) { - var args = _b.args, outputSuccess = _b.outputSuccess, outputFail = _b.outputFail, f = _b.f; - return __generator(this, function (_c) { - switch (_c.label) { - case 0: - if (!args.player.marked()) return [3 /*break*/, 2]; - return [4 /*yield*/, args.player.free("console")]; - case 1: - _c.sent(); - (0, utils_1.logAction)('freed', "console", args.player); - outputSuccess(f(templateObject_13 || (templateObject_13 = __makeTemplateObject(["Player ", " has been unmarked."], ["Player ", " has been unmarked."])), args.player)); - return [3 /*break*/, 3]; - case 2: - if (args.player.autoflagged) { - args.player.autoflagged = false; - if (args.player.connected()) { - args.player.sendMessage("[yellow]You have been unflagged."); - args.player.updateName(); - args.player.forceRespawn(); - } - outputSuccess(f(templateObject_14 || (templateObject_14 = __makeTemplateObject(["Player ", " has been unflagged."], ["Player ", " has been unflagged."])), args.player)); - } - else { - outputFail(f(templateObject_15 || (templateObject_15 = __makeTemplateObject(["Player ", " is not marked or autoflagged."], ["Player ", " is not marked or autoflagged."])), args.player)); - } - _c.label = 3; - case 3: return [2 /*return*/]; - } - }); - }); - } - }, - say: { - args: ["message:string"], - description: "Sends a message to the in-game chat.", - handler: function (_a) { - var message = _a.args.message; - Call.sendMessage("[scarlet][[Server]:[] ".concat(message)); - Log.info("&fi&lcServer: &fr&lw".concat(message)); - globals_1.FishEvents.fire("serverSays", []); - } - }, - whitelist: { - args: ["_:string?"], - description: "Disabled to prevent accidental lag", - handler: function () { - (0, commands_1.fail)("This command has been disabled to prevent lag. Fish servers do not use a whitelist."); - } - }, - loglevel: { - args: ["duration:time?"], - description: "Sets log level to debug", - handler: function (_a) { - var _b = _a.args.duration, duration = _b === void 0 ? funcs_1.Duration.minutes(5) : _b, outputSuccess = _a.outputSuccess; - Log.level = Log.LogLevel.debug; - Timer.schedule(function () { - Log.level = Log.LogLevel.info; - }, duration / 1000); - outputSuccess("Set log level to debug for ".concat((0, utils_1.formatTime)(duration))); - } - } -}); -var templateObject_1, templateObject_2, templateObject_3, templateObject_4, templateObject_5, templateObject_6, templateObject_7, templateObject_8, templateObject_9, templateObject_10, templateObject_11, templateObject_12, templateObject_13, templateObject_14, templateObject_15; +"use strict"; +/* +Copyright © BalaM314, 2026. All Rights Reserved. +This file contains all the console commands, which can be run through the server console. +*/ +var __makeTemplateObject = (this && this.__makeTemplateObject) || function (cooked, raw) { + if (Object.defineProperty) { Object.defineProperty(cooked, "raw", { value: raw }); } else { cooked.raw = raw; } + return cooked; +}; +var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + var desc = Object.getOwnPropertyDescriptor(m, k); + if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { + desc = { enumerable: true, get: function() { return m[k]; } }; + } + Object.defineProperty(o, k2, desc); +}) : (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + o[k2] = m[k]; +})); +var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { + Object.defineProperty(o, "default", { enumerable: true, value: v }); +}) : function(o, v) { + o["default"] = v; +}); +var __importStar = (this && this.__importStar) || (function () { + var ownKeys = function(o) { + ownKeys = Object.getOwnPropertyNames || function (o) { + var ar = []; + for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k; + return ar; + }; + return ownKeys(o); + }; + return function (mod) { + if (mod && mod.__esModule) return mod; + var result = {}; + if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]); + __setModuleDefault(result, mod); + return result; + }; +})(); +var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { + function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } + return new (P || (P = Promise))(function (resolve, reject) { + function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } + function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } + function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } + step((generator = generator.apply(thisArg, _arguments || [])).next()); + }); +}; +var __generator = (this && this.__generator) || function (thisArg, body) { + var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g = Object.create((typeof Iterator === "function" ? Iterator : Object).prototype); + return g.next = verb(0), g["throw"] = verb(1), g["return"] = verb(2), typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g; + function verb(n) { return function (v) { return step([n, v]); }; } + function step(op) { + if (f) throw new TypeError("Generator is already executing."); + while (g && (g = 0, op[0] && (_ = 0)), _) try { + if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t; + if (y = 0, t) op = [op[0] & 2, t.value]; + switch (op[0]) { + case 0: case 1: t = op; break; + case 4: _.label++; return { value: op[1], done: false }; + case 5: _.label++; y = op[1]; op = [0]; continue; + case 7: op = _.ops.pop(); _.trys.pop(); continue; + default: + if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; } + if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; } + if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; } + if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; } + if (t[2]) _.ops.pop(); + _.trys.pop(); continue; + } + op = body.call(thisArg, _); + } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; } + if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true }; + } +}; +var __read = (this && this.__read) || function (o, n) { + var m = typeof Symbol === "function" && o[Symbol.iterator]; + if (!m) return o; + var i = m.call(o), r, ar = [], e; + try { + while ((n === void 0 || n-- > 0) && !(r = i.next()).done) ar.push(r.value); + } + catch (error) { e = { error: error }; } + finally { + try { + if (r && !r.done && (m = i["return"])) m.call(i); + } + finally { if (e) throw e.error; } + } + return ar; +}; +var __values = (this && this.__values) || function(o) { + var s = typeof Symbol === "function" && Symbol.iterator, m = s && o[s], i = 0; + if (m) return m.call(o); + if (o && typeof o.length === "number") return { + next: function () { + if (o && i >= o.length) o = void 0; + return { value: o && o[i++], done: !o }; + } + }; + throw new TypeError(s ? "Object is not iterable." : "Symbol.iterator is not defined."); +}; +var __spreadArray = (this && this.__spreadArray) || function (to, from, pack) { + if (pack || arguments.length === 2) for (var i = 0, l = from.length, ar; i < l; i++) { + if (ar || !(i in from)) { + if (!ar) ar = Array.prototype.slice.call(from, 0, i); + ar[i] = from[i]; + } + } + return to.concat(ar || Array.prototype.slice.call(from)); +}; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.commands = void 0; +var api = __importStar(require("/api")); +var config_1 = require("/config"); +var files_1 = require("/files"); +var fjsContext = __importStar(require("/fjsContext")); +var commands_1 = require("/frameworks/commands"); +var funcs_1 = require("/funcs"); +var globals_1 = require("/globals"); +var players_1 = require("/players"); +var ranks_1 = require("/ranks"); +var utils_1 = require("/utils"); +exports.commands = (0, commands_1.consoleCommandList)({ + setrank: { + args: ["player:player", "rank:rank"], + description: "Set a player's rank.", + handler: function (_a) { + return __awaiter(this, arguments, void 0, function (_b) { + var args = _b.args, outputSuccess = _b.outputSuccess, f = _b.f; + return __generator(this, function (_c) { + switch (_c.label) { + case 0: + if (args.rank == ranks_1.Rank.pi && !config_1.Mode.localDebug) + (0, commands_1.fail)(f(templateObject_1 || (templateObject_1 = __makeTemplateObject(["Rank ", " is immutable."], ["Rank ", " is immutable."])), args.rank)); + if (args.player.immutable() && !config_1.Mode.localDebug) + (0, commands_1.fail)(f(templateObject_2 || (templateObject_2 = __makeTemplateObject(["Player ", " is immutable."], ["Player ", " is immutable."])), args.player)); + return [4 /*yield*/, args.player.setRank(args.rank)]; + case 1: + _c.sent(); + (0, utils_1.logAction)("set rank to ".concat(args.rank.name, " for"), "console", args.player); + outputSuccess(f(templateObject_3 || (templateObject_3 = __makeTemplateObject(["Set rank of player ", " to ", ""], ["Set rank of player ", " to ", ""])), args.player, args.rank)); + return [2 /*return*/]; + } + }); + }); + } + }, + admin: { + args: ["nothing:string?"], + description: "Use the setrank command instead.", + handler: function () { + (0, commands_1.fail)("Use the \"setrank\" command instead. Hint: \"setrank player admin\""); + } + }, + setflag: { + args: ["player:player", "flag:roleflag", "value:boolean"], + description: "Set a player's role flags.", + handler: function (_a) { + return __awaiter(this, arguments, void 0, function (_b) { + var args = _b.args, outputSuccess = _b.outputSuccess, f = _b.f; + return __generator(this, function (_c) { + switch (_c.label) { + case 0: return [4 /*yield*/, args.player.setFlag(args.flag, args.value)]; + case 1: + _c.sent(); + (0, utils_1.logAction)("set roleflag ".concat(args.flag.name, " to ").concat(args.value, " for"), "console", args.player); + outputSuccess(f(templateObject_4 || (templateObject_4 = __makeTemplateObject(["Set role flag ", " of player ", " to ", ""], ["Set role flag ", " of player ", " to ", ""])), args.flag, args.player, args.value)); + return [2 /*return*/]; + } + }); + }); + } + }, + savePlayers: { + args: [], + description: "Runs FishPlayer.save()", + handler: function (_a) { + var outputSuccess = _a.outputSuccess; + players_1.FishPlayer.saveAll(); + outputSuccess("Successfully wrote fish player data."); + } + }, + info: { + args: ["player:string"], + description: "Find player info(s). Displays all names and ips of a player.", + handler: function (_a) { + return __awaiter(this, arguments, void 0, function (_b) { + function display(infoList) { + var e_2, _a; + var outputString = [""]; + var _loop_1 = function (playerInfo, fishP) { + var flagsText = [ + (fishP === null || fishP === void 0 ? void 0 : fishP.marked()) && (globals_1.maxTime - fishP.unmarkTime < 20000 ? + "&lris marked forever&fr" + : "&lris marked&fr until ".concat((0, utils_1.formatTimeRelative)(fishP.unmarkTime))), + (fishP === null || fishP === void 0 ? void 0 : fishP.muted) && "&lris muted&fr", + (fishP === null || fishP === void 0 ? void 0 : fishP.hasFlag("member")) && "&lmis member&fr", + (fishP === null || fishP === void 0 ? void 0 : fishP.autoflagged) && "&lris autoflagged&fr", + playerInfo.banned && "&bris UUID banned&fr", + ].filter(Boolean).join(", "); + var lastJoinedColor = (fishP === null || fishP === void 0 ? void 0 : fishP.lastJoined) && fishP.lastJoined !== -1 ? (function () { + var timeSinceLastJoin = (Date.now() - fishP.lastJoined) / 1000; + if (timeSinceLastJoin < 3600) + return "&br"; + if (timeSinceLastJoin < 24 * 3600) + return "&by"; + if (timeSinceLastJoin < 7 * 24 * 3600) + return "&lw"; + return "&lk"; + })() : "&fr"; + outputString.push([ + "".concat(lastJoinedColor, "Trace info for player &fr&y").concat(playerInfo.id, "&fr").concat(lastJoinedColor, " / &c\"").concat((0, funcs_1.escapeStringColorsServer)(Strings.stripColors(playerInfo.lastName)), "\" &lk(").concat((0, funcs_1.escapeStringColorsServer)(playerInfo.lastName), ")&fr"), + playerInfo.names.size > 1 && "all names used: ".concat(playerInfo.names.map(funcs_1.escapeStringColorsServer).map(function (n) { return "&c\"".concat(n, "\"&fr"); }).items.join(', ')), + "all IPs used: ".concat(playerInfo.ips.map(function (n) { return (n == playerInfo.lastIP ? '&c' : '&w') + n + '&fr'; }).items.join(", ")), + "joined &c".concat(playerInfo.timesJoined, "&fr times, kicked &c").concat(playerInfo.timesKicked, "&fr times"), + fishP && fishP.lastJoined !== -1 && "Last joined: ".concat((0, utils_1.formatTimeRelative)(fishP.lastJoined)), + fishP && fishP.firstJoined !== -1 && (0, utils_1.formatTimeRelative)(fishP.firstJoined), + fishP && "USID: &c".concat(fishP.usid, "&fr"), + fishP && fishP.rank !== ranks_1.Rank.player && "Rank: &c".concat(fishP.rank.name, "&fr"), + flagsText, + ].filter(Boolean).map(function (l, i) { return i == 0 ? l : '\t' + l; }).join("\n")); + }; + try { + for (var infoList_1 = __values(infoList), infoList_1_1 = infoList_1.next(); !infoList_1_1.done; infoList_1_1 = infoList_1.next()) { + var _b = __read(infoList_1_1.value, 2), playerInfo = _b[0], fishP = _b[1]; + _loop_1(playerInfo, fishP); + } + } + catch (e_2_1) { e_2 = { error: e_2_1 }; } + finally { + try { + if (infoList_1_1 && !infoList_1_1.done && (_a = infoList_1.return)) _a.call(infoList_1); + } + finally { if (e_2) throw e_2.error; } + } + output(outputString.join("\n")); + } + var infoList, playersToFetch, _c, _d, batch, e_1_1, err_1; + var e_1, _e; + var _this = this; + var args = _b.args, output = _b.output, admins = _b.admins; + return __generator(this, function (_f) { + switch (_f.label) { + case 0: + infoList = admins.findByName(args.player) + .toSeq().toArray() + .map(function (p) { return [p, players_1.FishPlayer.getById(p.id)]; }); + if (infoList.length == 0) + (0, commands_1.fail)("No players found."); + playersToFetch = infoList.filter(function (_a) { + var _b = __read(_a, 2), a = _b[0], b = _b[1]; + return !b; + }).map(function (_a) { + var _b = __read(_a, 2), a = _b[0], b = _b[1]; + return a; + }); + if (!(playersToFetch.length == 0)) return [3 /*break*/, 1]; + display(infoList); + return [3 /*break*/, 13]; + case 1: + //Attempt to fetch data + //If there are too many players, give up + if (playersToFetch.length > 50) + display(infoList); + output("Fetching data..."); + _f.label = 2; + case 2: + _f.trys.push([2, 11, , 12]); + _f.label = 3; + case 3: + _f.trys.push([3, 8, 9, 10]); + _c = __values((0, funcs_1.to2DArray)(playersToFetch, 10)), _d = _c.next(); + _f.label = 4; + case 4: + if (!!_d.done) return [3 /*break*/, 7]; + batch = _d.value; + return [4 /*yield*/, Promise.all(batch.map(function (info) { return __awaiter(_this, void 0, void 0, function () { + var data, fishP; + return __generator(this, function (_a) { + switch (_a.label) { + case 0: return [4 /*yield*/, api.getFishPlayerData(info.id)]; + case 1: + data = _a.sent(); + if (data) { + fishP = players_1.FishPlayer.createFromInfo(info); + fishP.updateData(data); + players_1.FishPlayer.cachedPlayers[info.id] = fishP; + } + return [2 /*return*/]; + } + }); + }); }))]; + case 5: + _f.sent(); + _f.label = 6; + case 6: + _d = _c.next(); + return [3 /*break*/, 4]; + case 7: return [3 /*break*/, 10]; + case 8: + e_1_1 = _f.sent(); + e_1 = { error: e_1_1 }; + return [3 /*break*/, 10]; + case 9: + try { + if (_d && !_d.done && (_e = _c.return)) _e.call(_c); + } + finally { if (e_1) throw e_1.error; } + return [7 /*endfinally*/]; + case 10: return [3 /*break*/, 12]; + case 11: + err_1 = _f.sent(); + Log.err(err_1); + return [3 /*break*/, 12]; + case 12: + infoList = admins.findByName(args.player) + .toSeq().toArray() + .map(function (p) { return [p, players_1.FishPlayer.getById(p.id)]; }); + display(infoList); + _f.label = 13; + case 13: return [2 /*return*/]; + } + }); + }); + } + }, + infoonline: { + args: ["player:string"], + description: "Display information about an online player.", + handler: function (_a) { + var e_3, _b; + var args = _a.args, output = _a.output, admins = _a.admins; + var infoList = args.player == "*" ? players_1.FishPlayer.getAllOnline() : players_1.FishPlayer.getAllByName(args.player, false); + if (infoList.length == 0) + (0, commands_1.fail)("Nobody with that name could be found."); + var outputString = [""]; + var _loop_2 = function (player) { + var playerInfo = admins.getInfo(player.uuid); + outputString.push("Info for player &c\"".concat(player.cleanedName, "\" &lk(").concat(player.name, ")&fr\n\tUUID: &c\"").concat(playerInfo.id, "\"&fr\n\tUSID: &c").concat(player.usid ? "\"".concat(player.usid, "\"") : "unknown", "&fr\n\tall names used: ").concat(playerInfo.names.map(function (n) { return "&c\"".concat(n, "\"&fr"); }).items.join(', '), "\n\tall IPs used: ").concat(playerInfo.ips.map(function (n) { return (n == playerInfo.lastIP ? '&c' : '&w') + n + '&fr'; }).items.join(", "), "\n\tjoined &c").concat(playerInfo.timesJoined, "&fr times, kicked &c").concat(playerInfo.timesKicked, "&fr times\n\trank: &c").concat(player.rank.name, "&fr").concat((player.marked() ? ", &lris marked&fr" : "") + (player.muted ? ", &lris muted&fr" : "") + (player.hasFlag("member") ? ", &lmis member&fr" : "") + (player.autoflagged ? ", &lris autoflagged&fr" : ""))); + }; + try { + for (var infoList_2 = __values(infoList), infoList_2_1 = infoList_2.next(); !infoList_2_1.done; infoList_2_1 = infoList_2.next()) { + var player = infoList_2_1.value; + _loop_2(player); + } + } + catch (e_3_1) { e_3 = { error: e_3_1 }; } + finally { + try { + if (infoList_2_1 && !infoList_2_1.done && (_b = infoList_2.return)) _b.call(infoList_2); + } + finally { if (e_3) throw e_3.error; } + } + output(outputString.join("\n")); + } + }, + unblacklist: { + args: ["ip:string"], + description: "Unblacklists an ip from the DOS blacklist.", + handler: function (_a) { + var args = _a.args, output = _a.output, admins = _a.admins; + if (args.ip === '*') { + var size = admins.dosBlacklist.size; + if (size == 0) + (0, commands_1.fail)('DOS blacklist is already empty.'); + admins.dosBlacklist.clear(); + output("Cleared ".concat(size, " IPs from the DOS blacklist.")); + } + else { + if (admins.dosBlacklist.remove(args.ip)) + output("Removed ".concat(args.ip, " from the DOS blacklist.")); + else + (0, commands_1.fail)("IP address ".concat(args.ip, " is not DOS blacklisted.")); + } + } + }, + blacklist: { + args: ["verbose:boolean?"], + description: "Allows you to view the DOS blacklist.", + handler: function (_a) { + var args = _a.args, output = _a.output, admins = _a.admins; + var blacklist = admins.dosBlacklist; + if (blacklist.isEmpty()) + (0, commands_1.fail)("The blacklist is empty"); + if (args.verbose) { + var outputString_1 = ["DOS Blacklist:"]; + blacklist.each(function (ip) { + var info = admins.findByIP(ip); + if (info) { + outputString_1.push("IP: &c".concat(ip, "&fr UUID: &c\"").concat(info.id, "\"&fr Last name used: &c\"").concat(info.plainLastName(), "\"&fr")); + } + }); + output(outputString_1.join("\n")); + output("".concat(blacklist.size, " blacklisted IPs")); + } + else { + output(blacklist.toString()); + output("".concat(blacklist.size, " blacklisted IPs")); + } + } + }, + whack: { + args: ["target:string"], + description: "Whacks (ipbans) a player.", + handler: function (_a) { + var args = _a.args, output = _a.output, outputFail = _a.outputFail, admins = _a.admins; + var range; + if (globals_1.ipPattern.test(args.target)) { + //target is an ip + api.ban({ ip: args.target }); + var info = admins.findByIP(args.target); + if (info) + (0, utils_1.logAction)("whacked", "console", info); + else + (0, utils_1.logAction)("console ip-whacked ".concat(args.target)); + if (admins.isIPBanned(args.target)) { + output("IP &c\"".concat(args.target, "\"&fr is already banned. Ban was synced to other servers.")); + } + else { + admins.banPlayerIP(args.target); + output("&lrIP &c\"".concat(args.target, "\"&lr was banned. Ban was synced to other servers.")); + } + } + else if ((range = (0, utils_1.getIPRange)(args.target)) != null) { + if (admins.subnetBans.contains(boolf(function (ip) { return ip.replace(/\.$/, "") == range; }))) { + output("Subnet &c\"".concat(range, "\"&fr is already banned.")); + } + else { + admins.subnetBans.add(range); + output("&lrIP range &c\"".concat(range, "\"&lr was banned. Subnet bans are not synced.")); + } + } + else if (globals_1.uuidPattern.test(args.target)) { + var info = admins.getInfoOptional(args.target); + if (info) + (0, utils_1.logAction)("whacked", "console", info); + else + (0, utils_1.logAction)("console ip-whacked ".concat(args.target)); + if (admins.isIDBanned(args.target)) { + api.ban({ uuid: args.target }); + output("UUID &c\"".concat(args.target, "\"&fr is already banned. Ban was synced to other servers.")); + } + else { + admins.banPlayerID(args.target); + if (info) { + admins.banPlayerIP(info.lastIP); + api.ban({ uuid: args.target, ip: info.lastIP }); + output("&lrUUID &c\"".concat(args.target, "\" &lrwas banned. IP &c\"").concat(info.lastIP, "\"&lr was banned. Ban was synced to other servers.")); + } + else { + api.ban({ uuid: args.target }); + output("&lrUUID &c\"".concat(args.target, "\" &lrwas banned. Ban was synced to other servers. Warning: no stored info for this UUID, player may not exist. Unable to determine IP.")); + } + } + } + else { + var player = players_1.FishPlayer.getOneMindustryPlayerByName(args.target); + if (player === "none") { + outputFail("Could not find a player name matching &c\"".concat(args.target, "\"")); + } + else if (player === "multiple") { + outputFail("Name &c\"".concat(args.target, "\"&fr could refer to more than one player.")); + } + else { + if (player.admin) + (0, commands_1.fail)("Player &c\"".concat(player.name, "\"&fr is an admin, you probably don't want to ban them.")); + var ip = player.ip(); + var uuid = player.uuid(); + admins.banPlayerID(uuid); + admins.banPlayerIP(ip); + (0, utils_1.logAction)("console whacked ".concat(Strings.stripColors(player.name), " (`").concat(uuid, "`/`").concat(ip, "`)")); + api.ban({ uuid: uuid, ip: ip }); + output("&lrIP &c\"".concat(ip, "\"&lr was banned. UUID &c\"").concat(uuid, "\"&lr was banned. Ban was synced to other servers.")); + } + } + (0, utils_1.updateBans)(function (player) { return "[scarlet]Player [yellow]".concat(player.name, "[scarlet] has been whacked."); }); + } + }, + unwhack: { + args: ["target:string"], + description: "Unbans a player.", + handler: function (_a) { + var args = _a.args, output = _a.output, admins = _a.admins; + var range; + if (globals_1.ipPattern.test(args.target)) { + //target is an ip + if (players_1.FishPlayer.removePunishedIP(args.target)) { + output("Removed IP &c\"".concat(args.target, "\"&fr from the anti-evasion list.")); + } + if (admins.kickedIPs.remove(args.target)) { + output("Removed temporary kick for IP &c\"".concat(args.target, "\"&fr.")); + } + output("Checking ban status..."); + api.getBanned({ ip: args.target }, function (banned) { + if (banned) { + api.unban({ ip: args.target }); + (0, utils_1.logAction)("console unbanned ip `".concat(args.target, "`")); + output("IP &c\"".concat(args.target, "\"&fr has been globally unbanned.")); + } + else { + output("IP &c\"".concat(args.target, "\"&fr is not globally banned.")); + } + if (admins.isIPBanned(args.target)) { + admins.unbanPlayerIP(args.target); + output("IP &c\"".concat(args.target, "\"&fr has been locally unbanned.")); + } + else { + output("IP &c\"".concat(args.target, "\"&fr was not locally banned.")); + } + var size = admins.subnetBans.size; + admins.subnetBans.removeAll(function (r) { return args.target.startsWith(r); }); + if (admins.subnetBans.size < size) { + output("Unbanned IP ranges affecting this IP."); + } + }); + } + else if ((range = (0, utils_1.getIPRange)(args.target)) != null) { + if (admins.subnetBans.remove(function (b) { return b.replace(/\.$/, ".") == range.replace(/\.$/, "."); })) { + output("IP range &c\"".concat(range, "\"&fr was unbanned.")); + } + else { + output("IP range &c\"".concat(range, "\"&fr was not banned.")); + } + } + else if (globals_1.uuidPattern.test(args.target)) { + if (players_1.FishPlayer.removePunishedUUID(args.target)) { + output("Removed UUID &c\"".concat(args.target, "\"&fr from the anti-evasion list.")); + } + output("Checking ban status..."); + var info_1 = admins.findByIP(args.target); + api.getBanned({ uuid: args.target }, function (banned) { + if (banned) { + api.unban({ uuid: args.target }); + (0, utils_1.logAction)("console unbanned uuid `".concat(args.target, "`")); + output("UUID &c\"".concat(args.target, "\"&fr has been globally unbanned.")); + } + else { + output("UUID &c\"".concat(args.target, "\"&fr is not globally banned.")); + } + if (admins.isIDBanned(args.target)) { + admins.unbanPlayerID(args.target); + output("UUID &c\"".concat(args.target, "\"&fr has been locally unbanned.")); + } + else { + output("UUID &c\"".concat(args.target, "\"&fr was not locally banned.")); + } + if (info_1) { + if (info_1.lastKicked > 0) { + info_1.lastKicked = 0; + output("Removed temporary kick for UUID &c\"".concat(args.target, "\"&fr.")); + } + output("You may also want to consider unbanning the IP \"".concat(info_1.lastIP, "\".")); + } + }); + } + else { + (0, commands_1.fail)("Cannot unban by name; please use the info or search commands to find the IP and UUID of the player you are looking for."); + } + } + }, + ban: { + args: ["any:string"], + description: "Please use the whack command instead.", + handler: function () { + (0, commands_1.fail)("Use the whack command instead."); + } + }, + unban: { + args: ["any:string"], + description: "Please use the unwhack command instead.", + handler: function () { + (0, commands_1.fail)("Use the unwhack command instead."); + } + }, + "subnet-ban": { + args: ["any:string?", "anyb:string?"], + description: "Please use the whack and unwhack commands instead.", + handler: function (_a) { + var args = _a.args, output = _a.output, admins = _a.admins; + if (args.any && args.any != "list") + (0, commands_1.fail)("Use the whack and unwhack commands instead."); + output("List of all subnet bans:"); + output(admins.subnetBans.toString("\n")); + } + }, + joinbell: { + args: ["on:boolean?"], + description: "Toggles the join bell function.", + handler: function (_a) { + var _b = _a.args.on, on = _b === void 0 ? !globals_1.fishState.joinBell : _b; + globals_1.fishState.joinBell = on; + if (globals_1.fishState.joinBell) { + Log.info("Enabled sound on new player join. Run \"joinbell\" again to turn it off."); + } + else { + Log.info("Disabled sound on new player join."); + } + } + }, + loadfishplayerdata: { + args: ["areyousure:boolean", "fishplayerdata:string"], + description: "Overwrites current fish player data.", + handler: function (_a) { + var args = _a.args, output = _a.output; + if (args.areyousure) { + var before = Object.keys(players_1.FishPlayer.cachedPlayers).length; + players_1.FishPlayer.loadAll(args.fishplayerdata); + output("Loaded fish player data. before:".concat(before, ", after:").concat(Object.keys(players_1.FishPlayer.cachedPlayers).length)); + } + } + }, + resetauth: { + args: ["player:string"], + description: "Removes the USID of the player provided, use this if they are getting kicked with the message \"Authorization failure!\". Specify \"last\" to use the last player that got kicked.", + handler: function (_a) { + var _b, _c; + var args = _a.args, outputSuccess = _a.outputSuccess, outputFail = _a.outputFail, admins = _a.admins; + var player = args.player == "last" ? ((_b = players_1.FishPlayer.lastAuthKicked) !== null && _b !== void 0 ? _b : (0, commands_1.fail)("Nobody has been kicked for authorization failure since the last restart.")) : + (_c = players_1.FishPlayer.getById(args.player)) !== null && _c !== void 0 ? _c : (0, commands_1.fail)(admins.getInfoOptional(args.player) + ? "Player ".concat(args.player, " has joined the server, but their info was not cached, most likely because they have no rank, so there is no stored USID.") + : "Unknown player ".concat(args.player)); + if (player.ranksAtLeast("admin")) + (0, commands_1.fail)("Please use the setusid command instead."); + var oldusid = player.usid; + player.usid = null; + api.setFishPlayerData(player.getData(), 1, true).then(function () { + outputSuccess("Removed the usid of player ".concat(player.name, "/").concat(player.uuid, " (was ").concat(oldusid, ")")); + }).catch(function (err) { + Log.err(err); + outputFail("Failed to remove the usid, please try running the command again."); + }); + } + }, + setusid: { + args: ["uuid:string", "usid:string"], + description: "Sets the USID of a player.", + handler: function (_a) { + var _b; + var args = _a.args, outputSuccess = _a.outputSuccess, outputFail = _a.outputFail, f = _a.f; + if (args.usid.length !== 12) + (0, commands_1.fail)("Invalid USID: should be 12 characters ending with an equal sign"); + var player = (_b = players_1.FishPlayer.lastAuthKicked) !== null && _b !== void 0 ? _b : (0, commands_1.fail)("No authorization failures have occurred since the last restart."); + var oldusid = player.usid; + player.usid = args.usid; + api.setFishPlayerData(player.getData(), 1, true).then(function () { + outputSuccess("Set the usid of player ".concat(player.name, "/").concat(player.uuid, " to ").concat(args.usid, " (was ").concat(oldusid, ")")); + }).catch(function (err) { + Log.err(err); + outputFail("Failed to remove the usid, please try running the command again."); + }); + } + }, + update: { + args: ["branch:string?"], + description: "Updates the plugin.", + handler: function (_a) { + var args = _a.args, output = _a.output, outputSuccess = _a.outputSuccess, outputFail = _a.outputFail; + if (config_1.Mode.localDebug) + (0, commands_1.fail)("Cannot update in local debug mode."); + output("Updating..."); + var path = (0, utils_1.fishCommandsRootDirPath)().toString(); + Threads.thread(function () { + var _a, _b; + try { + var initialVersion = OS.exec("git", "-C", path, "rev-parse", "HEAD"); + var gitFetch = new ProcessBuilder("git", "-C", path, "fetch", "origin") + .redirectErrorStream(true) + .redirectOutput(ProcessBuilder.Redirect.INHERIT) + .start(); + gitFetch.waitFor(); + if (gitFetch.exitValue() == 0) { + outputSuccess("Fetched data, updating files..."); + } + else { + outputFail("Update failed!"); + return; + } + var newVersion = OS.exec("git", "-C", path, "rev-parse", "origin/".concat((_a = args.branch) !== null && _a !== void 0 ? _a : "master")); + if (initialVersion == newVersion) { + outputSuccess("Already up to date."); + return; + } + var gitCheckout = new ProcessBuilder("git", "-C", path, "checkout", "-q", "-f", "origin/".concat((_b = args.branch) !== null && _b !== void 0 ? _b : "master")) + .redirectErrorStream(true) + .redirectOutput(ProcessBuilder.Redirect.INHERIT) + .start(); + gitCheckout.waitFor(); + if (gitCheckout.exitValue() == 0) { + outputSuccess("Updated successfully from ".concat(initialVersion, " to ").concat(newVersion, ". Restart to apply changes.")); + } + else { + outputFail("Update failed!"); + return; + } + } + catch (err) { + Log.err(err); + outputFail("Update failed!"); + } + }); + } + }, + restart: { + args: ["time:number?"], + description: "Restarts the server.", + handler: function (_a) { + var _b; + var time = _a.args.time; + (_b = globals_1.fishState.restartLoopTask) === null || _b === void 0 ? void 0 : _b.cancel(); + var timeInferred = time == undefined; + if (Groups.player.isEmpty()) { + if (time == undefined) { + Log.info("Restarting immediately as no players are online."); + time !== null && time !== void 0 ? time : (time = 0); + } + } + else if (config_1.Gamemode.pvp()) { + time !== null && time !== void 0 ? time : (time = -1); + } + else { + time !== null && time !== void 0 ? time : (time = 60); + } + if (time == -1) { + Call.sendMessage("[accent]---[[[coral]+++[]]---\n[accent]Server restart queued. The server will restart after the current match is over.[]\n[accent]---[[[coral]+++[]]---"); + if (config_1.Gamemode.pvp() && timeInferred) + Log.info("PVP: restart will occur at the end of the current game. Specify a time to override, but &rthat would interrupt the current pvp match, and players would lose their teams.&fr"); + else + Log.info("Restarting once the current game ends."); + globals_1.fishState.restartQueued = true; + } + else { + if (time < 0 || time > 100) + (0, commands_1.fail)("Invalid time: out of valid range."); + (0, utils_1.serverRestartLoop)(time); + if (time == 0) + Log.info("Restarting now."); + else + Log.info("Restarting in ".concat(time, " second").concat(time == 1 ? "" : "s", ".")); + if (config_1.Gamemode.pvp()) { + Call.sendMessage("[accent]---[[[coral]+++[]]---\n[accent]Server restart imminent. [green]We'll be back after 20 seconds.[]\n[accent]---[[[coral]+++[]]---"); + } + else { + Call.sendMessage("[accent]---[[[coral]+++[]]---\n[accent]Server restart imminent. [green]We'll be back after 20 seconds, and all progress will be saved.[]\n[accent]---[[[coral]+++[]]---"); + } + } + } + }, + restartcancel: { + args: [], + description: "Cancels a planned server restart.", + handler: function (_a) { + var _b; + var outputSuccess = _a.outputSuccess; + var task = (_b = globals_1.fishState.restartLoopTask) !== null && _b !== void 0 ? _b : (0, commands_1.fail)("No restart scheduled."); + Call.sendMessage("[scarlet]Aborting..."); + task.cancel(); + Call.sendMessage("[scarlet]Server restart canceled."); + outputSuccess("Canceled restart."); + } + }, + rename: { + args: ["player:player", "newname:string"], + description: "Changes the name of a player.", + handler: function (_a) { + var args = _a.args, f = _a.f, outputSuccess = _a.outputSuccess; + if (args.player.hasPerm("blockTrolling")) + (0, commands_1.fail)(f(templateObject_5 || (templateObject_5 = __makeTemplateObject(["Operation aborted: Player ", " is insufficiently trollable."], ["Operation aborted: Player ", " is insufficiently trollable."])), args.player)); + var oldName = args.player.name; + args.player.player.name = args.player.prefixedName = args.newname; + args.player.shouldUpdateName = false; + outputSuccess("Renamed ".concat(oldName, " to ").concat(args.newname, ".")); + } + }, + fjs: { + args: ["js:string"], + description: "Executes arbitrary javascript code, but has access to fish-commands's variables.", + handler: function (_a) { + var args = _a.args; + fjsContext.runJS(args.js); + } + }, + checkmem: { + args: [], + description: "Checks memory usage of various objects.", + handler: function (_a) { + var output = _a.output; + output("Memory usage:\nTotal: ".concat(Math.round(Core.app.getJavaHeap() / (Math.pow(2, 10))), " KB\nNumber of cached fish players: ").concat(Object.keys(players_1.FishPlayer.cachedPlayers).length, " (stored locally: ").concat(Object.values(players_1.FishPlayer.cachedPlayers).filter(function (p) { return p.shouldCache(); }).length, ")\nFish player data string length: ").concat(players_1.FishPlayer.getFishPlayersString.length, " (").concat(Core.settings.getInt("fish-subkeys"), " subkeys)\nLength of tilelog entries: ").concat(Math.round(Object.values(globals_1.tileHistory).reduce(function (acc, a) { return acc + a.length; }, 0) / (Math.pow(2, 10))), " KB")); + } + }, + stopplayer: { + args: ['player:player', "time:time?", "message:string?"], + description: 'Stops a player.', + handler: function (_a) { + return __awaiter(this, arguments, void 0, function (_b) { + var previousTime, time; + var _c, _d, _e; + var args = _b.args, f = _b.f, outputSuccess = _b.outputSuccess; + return __generator(this, function (_f) { + switch (_f.label) { + case 0: + if (!args.player.marked()) return [3 /*break*/, 2]; + //overload: overwrite stoptime + if (!args.time) + (0, commands_1.fail)(f(templateObject_6 || (templateObject_6 = __makeTemplateObject(["Player ", " is already marked."], ["Player ", " is already marked."])), args.player)); + previousTime = (0, utils_1.formatTime)(args.player.unmarkTime - Date.now()); + return [4 /*yield*/, args.player.updateStopTime(args.time)]; + case 1: + _f.sent(); + outputSuccess(f(templateObject_7 || (templateObject_7 = __makeTemplateObject(["Player ", "'s stop time has been updated to ", " (was ", ")."], ["Player ", "'s stop time has been updated to ", " (was ", ")."])), args.player, (0, utils_1.formatTime)(args.time), previousTime)); + return [2 /*return*/]; + case 2: + time = (_c = args.time) !== null && _c !== void 0 ? _c : funcs_1.Duration.days(7); + if (time + Date.now() > globals_1.maxTime) + (0, commands_1.fail)("Error: time too high."); + return [4 /*yield*/, args.player.stop("console", time, (_d = args.message) !== null && _d !== void 0 ? _d : undefined)]; + case 3: + _f.sent(); + (0, utils_1.logAction)('stopped', "console", args.player, (_e = args.message) !== null && _e !== void 0 ? _e : undefined, time); + Call.sendMessage("[scarlet]Player \"".concat(args.player.prefixedName, "[scarlet]\" has been marked for ").concat((0, utils_1.formatTime)(time)).concat(args.message ? " with reason: [white]".concat(args.message, "[]") : "", ".")); + return [2 /*return*/]; + } + }); + }); + } + }, + stopoffline: { + args: ["uuid:uuid", "time:time?"], + description: "Stops a player by uuid.", + handler: function (_a) { + return __awaiter(this, arguments, void 0, function (_b) { + var stopTime, info, fishP; + var _c = _b.args, uuid = _c.uuid, time = _c.time, outputSuccess = _b.outputSuccess, admins = _b.admins; + return __generator(this, function (_d) { + switch (_d.label) { + case 0: + stopTime = time !== null && time !== void 0 ? time : (globals_1.maxTime - Date.now() - 10000); + info = admins.getInfoOptional(uuid); + if (info == null) + (0, commands_1.fail)("Unknown player ".concat(uuid)); + fishP = players_1.FishPlayer.getFromInfo(info); + return [4 /*yield*/, fishP.stop("console", stopTime)]; + case 1: + _d.sent(); + (0, utils_1.logAction)('stopped', "console", info, undefined, stopTime); + outputSuccess("Player \"".concat(info.lastName, "\" was marked for ").concat((0, utils_1.formatTime)(stopTime), ".")); + return [2 /*return*/]; + } + }); + }); + } + }, + clearfire: { + args: [], + description: "Clears all the fires.", + handler: function (_a) { + var output = _a.output, outputSuccess = _a.outputSuccess; + output("Removing fires..."); + var totalRemoved = 0; + Call.sendMessage("[scarlet][[Fire Department]:[yellow] Fires were reported. Trucks are en-route. Removing all fires shortly."); + Timer.schedule(function () { + totalRemoved += Groups.fire.size(); + Groups.fire.each(function (f) { return f.remove(); }); + Groups.fire.clear(); + }, 2, 0.1, 40); + Timer.schedule(function () { + outputSuccess("Removed ".concat(totalRemoved, " fires.")); + Call.sendMessage("[scarlet][[Fire Department]:[yellow] We've extinguished ".concat(totalRemoved, " fires.")); + }, 6.1); + } + }, + status: { + args: [], + description: "Displays server status.", + handler: function (_a) { + var output = _a.output; + if (Vars.state.isMenu()) + (0, commands_1.fail)("Status: Server closed."); + var uptime = Packages.java.lang.management.ManagementFactory.getRuntimeMXBean().getUptime(); + var numStaff = 0; + players_1.FishPlayer.forEachPlayer(function (p) { + if (p.ranksAtLeast("mod")) + numStaff++; + }); + var uptimeColor = uptime < funcs_1.Duration.days(2) ? "" : + uptime < funcs_1.Duration.days(5) ? "&ly" : + uptime < funcs_1.Duration.days(9) ? "&y" : + "&br"; + output("\nStatus:\nPlaying on map &fi".concat(Vars.state.map.plainName(), "&fr for ").concat((0, utils_1.formatTime)(1000 * Vars.state.tick / 60), "\n").concat(Vars.state.rules.waves ? "Wave &c".concat(Vars.state.wave, "&fr, &c").concat(Math.ceil(Vars.state.wavetime / 60), "&fr seconds until next wave.\n") : "", "&c").concat(Groups.unit.size(), "&fr units, &c").concat(Vars.state.enemies, "&fr enemies, &c").concat(Groups.build.size(), "&fr buildings\nTPS: ").concat((0, utils_1.colorNumber)(Core.graphics.getFramesPerSecond(), function (f) { return f > 58 ? "&g" : f > 30 ? "&y" : f > 10 ? "&r" : "&br&w"; }, "server"), ", Memory: &c").concat(Math.round(Core.app.getJavaHeap() / 1048576), "&fr MB\nServer uptime: ").concat(uptimeColor).concat((0, utils_1.formatTime)(uptime), "&fr (since ").concat((0, utils_1.formatTimestampFull)(Date.now() - uptime), ")\n").concat([ + globals_1.fishState.restartQueued ? "&by&lwRestart queued&fr" : "", + globals_1.fishState.restartLoopTask ? "&by&lwRestarting now&fr" : "", + players_1.FishPlayer.antiBotMode() ? "&br&wANTIBOT ACTIVE!&fr" + (0, utils_1.getAntiBotInfo)("server") : "", + ].filter(function (l) { return l.length > 0; }).join("\n"), "\n").concat((0, utils_1.colorNumber)(Groups.player.size(), function (n) { return n > 0 ? "&c" : "&lr"; }, "server"), " players online, ").concat((0, utils_1.colorNumber)(numStaff, function (n) { return n > 0 ? "&c" : "&lr"; }, "server"), " staff members.\n").concat(players_1.FishPlayer.mapPlayers(function (p) { + return "\t".concat(p.rank.shortPrefix, " &c").concat(p.uuid, "&fr &c").concat(p.name, "&fr"); + }).join("\n") || "&lrNo players connected.&fr", "\n")); + } + }, + tmux: { + args: ["attach:string"], + description: "Oopsie", + handler: function () { + (0, commands_1.fail)("You are already in the Mindustry server console. Please regain situational awareness before running any further commands."); + } + }, + BEGIN: { + args: ["transaction:string"], + description: "Oopsie", + handler: function (_a) { + var args = _a.args; + if (args.transaction == "TRANSACTION") + (0, commands_1.fail)("Not possible :( please download and run locally, and make a backup"); + else + (0, commands_1.fail)("Command not found. Did you mean \"BEGIN TRANSACTION\"?"); + } + }, + prune: { + args: ["confirm:boolean?"], + description: "Prunes fish player data", + handler: function (_a) { + var args = _a.args, admins = _a.admins, outputSuccess = _a.outputSuccess, outputFail = _a.outputFail; + var playersToPrune = Object.values(players_1.FishPlayer.cachedPlayers) + .filter(function (player) { + if (player.hasData()) + return false; + var data = admins.getInfoOptional(player.uuid); + return (!data || + data.timesJoined == 1 || + (data.timesJoined < 10 && + (Date.now() - player.lastJoined) > funcs_1.Duration.months(1))); + }); + if (args.confirm) { + outputSuccess("Creating backup..."); + var backupScript = Core.settings.getDataDirectory().child("backup.sh"); + if (!backupScript.exists()) + (0, commands_1.fail)("./backup.sh does not exist! aborting"); + var backupProcess_1 = new ProcessBuilder(backupScript.absolutePath()) + .directory(Core.settings.getDataDirectory().file()) + .redirectErrorStream(true) + .redirectOutput(ProcessBuilder.Redirect.INHERIT) + .start(); + Threads.daemon(function () { + backupProcess_1.waitFor(); + if (backupProcess_1.exitValue() == 0) { + outputSuccess("Successfully created a backup."); + Core.app.post(function () { + playersToPrune.forEach(function (u) { delete players_1.FishPlayer.cachedPlayers[u.uuid]; }); + outputSuccess("Pruned ".concat(playersToPrune.length, " players.")); + }); + } + else { + outputFail("Backup failed!"); + } + }); + } + else { + outputSuccess("Pruning would remove fish data for ".concat(playersToPrune.length, " players with no data and (1 join or inactive with <10 joins). (Mindustry data will remain.)\nRun \"prune y\" to prune data.")); + } + } + }, + backup: { + args: [], + description: "Creates a backup of the settings.bin file.", + handler: function (_a) { + var output = _a.output, outputFail = _a.outputFail, outputSuccess = _a.outputSuccess; + output("Creating backup..."); + var backupScript = Core.settings.getDataDirectory().child("backup.sh"); + if (!backupScript.exists()) + (0, commands_1.fail)("./backup.sh does not exist! aborting"); + var backupProcess = new ProcessBuilder(backupScript.absolutePath()) + .directory(Core.settings.getDataDirectory().file()) + .redirectErrorStream(true) + .redirectOutput(ProcessBuilder.Redirect.INHERIT) + .start(); + Threads.daemon(function () { + backupProcess.waitFor(); + if (backupProcess.exitValue() == 0) + outputSuccess("Successfully created a backup."); + else + outputFail("Backup failed!"); + }); + } + }, + updateMaps: { + args: [], + description: 'Attempt to fetch and update all map files', + handler: function (_a) { + var output = _a.output, outputSuccess = _a.outputSuccess, outputFail = _a.outputFail; + output("Updating maps... (this may take a while)"); + (0, files_1.updateMaps)() + .then(function (changed) { return outputSuccess(changed ? "Maps were updated." : "Map update completed, already up to date."); }) + .catch(function (message) { return outputFail("Map update failed: ".concat(String(message))); }); + }, + }, + switchall: { + args: ["server:string"], + description: "Forces all currently online players to another server.", + handler: function (_a) { + var _b; + var args = _a.args; + if (globals_1.ipPortPattern.test(args.server)) { + Groups.player.each(function (target) { + //direct connect + Call.connect.apply(Call, __spreadArray([target.con], __read(args.server.split(":")), false)); + }); + } + else { + var server_1 = (_b = config_1.FishServer.byName(args.server)) !== null && _b !== void 0 ? _b : (0, commands_1.fail)("Unknown server ".concat(args.server, ". Valid options: ").concat(config_1.FishServer.all.map(function (s) { return s.name; }).join(", "))); + Groups.player.each(function (target) { + Call.connect(target.con, server_1.ip, server_1.port); + }); + } + } + }, + mute: { + args: ['player:player'], + description: 'Stops a player from chatting.', + handler: function (_a) { + return __awaiter(this, arguments, void 0, function (_b) { + var args = _b.args, outputSuccess = _b.outputSuccess, f = _b.f; + return __generator(this, function (_c) { + switch (_c.label) { + case 0: + if (args.player.muted) + (0, commands_1.fail)(f(templateObject_8 || (templateObject_8 = __makeTemplateObject(["Player ", " is already muted."], ["Player ", " is already muted."])), args.player)); + return [4 /*yield*/, args.player.mute("console")]; + case 1: + _c.sent(); + (0, utils_1.logAction)('muted', "console", args.player); + outputSuccess(f(templateObject_9 || (templateObject_9 = __makeTemplateObject(["Muted player ", "."], ["Muted player ", "."])), args.player)); + return [2 /*return*/]; + } + }); + }); + } + }, + unmute: { + args: ['player:player'], + description: 'Unmutes a player', + handler: function (_a) { + return __awaiter(this, arguments, void 0, function (_b) { + var args = _b.args, outputSuccess = _b.outputSuccess, f = _b.f; + return __generator(this, function (_c) { + switch (_c.label) { + case 0: + if (!args.player.muted && args.player.autoflagged) + (0, commands_1.fail)(f(templateObject_10 || (templateObject_10 = __makeTemplateObject(["Player ", " is not muted, but they are autoflagged. You probably want to free them with /free."], ["Player ", " is not muted, but they are autoflagged. You probably want to free them with /free."])), args.player)); + if (!args.player.muted) + (0, commands_1.fail)(f(templateObject_11 || (templateObject_11 = __makeTemplateObject(["Player ", " is not muted."], ["Player ", " is not muted."])), args.player)); + return [4 /*yield*/, args.player.unmute("console")]; + case 1: + _c.sent(); + (0, utils_1.logAction)('unmuted', "console", args.player); + outputSuccess(f(templateObject_12 || (templateObject_12 = __makeTemplateObject(["Unmuted player ", "."], ["Unmuted player ", "."])), args.player)); + return [2 /*return*/]; + } + }); + }); + } + }, + free: { + args: ['player:player'], + description: 'Frees a player.', + handler: function (_a) { + return __awaiter(this, arguments, void 0, function (_b) { + var args = _b.args, outputSuccess = _b.outputSuccess, outputFail = _b.outputFail, f = _b.f; + return __generator(this, function (_c) { + switch (_c.label) { + case 0: + if (!args.player.marked()) return [3 /*break*/, 2]; + return [4 /*yield*/, args.player.free("console")]; + case 1: + _c.sent(); + (0, utils_1.logAction)('freed', "console", args.player); + outputSuccess(f(templateObject_13 || (templateObject_13 = __makeTemplateObject(["Player ", " has been unmarked."], ["Player ", " has been unmarked."])), args.player)); + return [3 /*break*/, 3]; + case 2: + if (args.player.autoflagged) { + args.player.autoflagged = false; + if (args.player.connected()) { + args.player.sendMessage("[yellow]You have been unflagged."); + args.player.updateName(); + args.player.forceRespawn(); + } + outputSuccess(f(templateObject_14 || (templateObject_14 = __makeTemplateObject(["Player ", " has been unflagged."], ["Player ", " has been unflagged."])), args.player)); + } + else { + outputFail(f(templateObject_15 || (templateObject_15 = __makeTemplateObject(["Player ", " is not marked or autoflagged."], ["Player ", " is not marked or autoflagged."])), args.player)); + } + _c.label = 3; + case 3: return [2 /*return*/]; + } + }); + }); + } + }, + say: { + args: ["message:string"], + description: "Sends a message to the in-game chat.", + handler: function (_a) { + var message = _a.args.message; + Call.sendMessage("[scarlet][[Server]:[] ".concat(message)); + Log.info("&fi&lcServer: &fr&lw".concat(message)); + globals_1.FishEvents.fire("serverSays", []); + } + }, + whitelist: { + args: ["_:string?"], + description: "Disabled to prevent accidental lag", + handler: function () { + (0, commands_1.fail)("This command has been disabled to prevent lag. Fish servers do not use a whitelist."); + } + }, + loglevel: { + args: ["duration:time?"], + description: "Sets log level to debug", + handler: function (_a) { + var _b = _a.args.duration, duration = _b === void 0 ? funcs_1.Duration.minutes(5) : _b, outputSuccess = _a.outputSuccess; + Log.level = Log.LogLevel.debug; + Timer.schedule(function () { + Log.level = Log.LogLevel.info; + }, duration / 1000); + outputSuccess("Set log level to debug for ".concat((0, utils_1.formatTime)(duration))); + } + } +}); +var templateObject_1, templateObject_2, templateObject_3, templateObject_4, templateObject_5, templateObject_6, templateObject_7, templateObject_8, templateObject_9, templateObject_10, templateObject_11, templateObject_12, templateObject_13, templateObject_14, templateObject_15; diff --git a/build/scripts/commands/general.js b/build/scripts/commands/general.js index 8bda3c4b..011f92e0 100644 --- a/build/scripts/commands/general.js +++ b/build/scripts/commands/general.js @@ -1,1434 +1,1554 @@ -"use strict"; -/* -Copyright © BalaM314, 2026. All Rights Reserved. -This file contains most in-game chat commands that can be run by untrusted players. -*/ -var __makeTemplateObject = (this && this.__makeTemplateObject) || function (cooked, raw) { - if (Object.defineProperty) { Object.defineProperty(cooked, "raw", { value: raw }); } else { cooked.raw = raw; } - return cooked; -}; -var __assign = (this && this.__assign) || function () { - __assign = Object.assign || function(t) { - for (var s, i = 1, n = arguments.length; i < n; i++) { - s = arguments[i]; - for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p)) - t[p] = s[p]; - } - return t; - }; - return __assign.apply(this, arguments); -}; -var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { - if (k2 === undefined) k2 = k; - var desc = Object.getOwnPropertyDescriptor(m, k); - if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { - desc = { enumerable: true, get: function() { return m[k]; } }; - } - Object.defineProperty(o, k2, desc); -}) : (function(o, m, k, k2) { - if (k2 === undefined) k2 = k; - o[k2] = m[k]; -})); -var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { - Object.defineProperty(o, "default", { enumerable: true, value: v }); -}) : function(o, v) { - o["default"] = v; -}); -var __importStar = (this && this.__importStar) || (function () { - var ownKeys = function(o) { - ownKeys = Object.getOwnPropertyNames || function (o) { - var ar = []; - for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k; - return ar; - }; - return ownKeys(o); - }; - return function (mod) { - if (mod && mod.__esModule) return mod; - var result = {}; - if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]); - __setModuleDefault(result, mod); - return result; - }; -})(); -var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { - function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } - return new (P || (P = Promise))(function (resolve, reject) { - function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } - function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } - function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } - step((generator = generator.apply(thisArg, _arguments || [])).next()); - }); -}; -var __generator = (this && this.__generator) || function (thisArg, body) { - var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g = Object.create((typeof Iterator === "function" ? Iterator : Object).prototype); - return g.next = verb(0), g["throw"] = verb(1), g["return"] = verb(2), typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g; - function verb(n) { return function (v) { return step([n, v]); }; } - function step(op) { - if (f) throw new TypeError("Generator is already executing."); - while (g && (g = 0, op[0] && (_ = 0)), _) try { - if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t; - if (y = 0, t) op = [op[0] & 2, t.value]; - switch (op[0]) { - case 0: case 1: t = op; break; - case 4: _.label++; return { value: op[1], done: false }; - case 5: _.label++; y = op[1]; op = [0]; continue; - case 7: op = _.ops.pop(); _.trys.pop(); continue; - default: - if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; } - if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; } - if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; } - if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; } - if (t[2]) _.ops.pop(); - _.trys.pop(); continue; - } - op = body.call(thisArg, _); - } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; } - if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true }; - } -}; -var __read = (this && this.__read) || function (o, n) { - var m = typeof Symbol === "function" && o[Symbol.iterator]; - if (!m) return o; - var i = m.call(o), r, ar = [], e; - try { - while ((n === void 0 || n-- > 0) && !(r = i.next()).done) ar.push(r.value); - } - catch (error) { e = { error: error }; } - finally { - try { - if (r && !r.done && (m = i["return"])) m.call(i); - } - finally { if (e) throw e.error; } - } - return ar; -}; -var __spreadArray = (this && this.__spreadArray) || function (to, from, pack) { - if (pack || arguments.length === 2) for (var i = 0, l = from.length, ar; i < l; i++) { - if (ar || !(i in from)) { - if (!ar) ar = Array.prototype.slice.call(from, 0, i); - ar[i] = from[i]; - } - } - return to.concat(ar || Array.prototype.slice.call(from)); -}; -var __values = (this && this.__values) || function(o) { - var s = typeof Symbol === "function" && Symbol.iterator, m = s && o[s], i = 0; - if (m) return m.call(o); - if (o && typeof o.length === "number") return { - next: function () { - if (o && i >= o.length) o = void 0; - return { value: o && o[i++], done: !o }; - } - }; - throw new TypeError(s ? "Object is not iterable." : "Symbol.iterator is not defined."); -}; -var _a; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.commands = void 0; -var achievements_1 = require("/achievements"); -var api = __importStar(require("/api")); -var config_1 = require("/config"); -var commands_1 = require("/frameworks/commands"); -var menus_1 = require("/frameworks/menus"); -var funcs_1 = require("/funcs"); -var globals_1 = require("/globals"); -var maps_1 = require("/maps"); -var players_1 = require("/players"); -var ranks_1 = require("/ranks"); -var utils_1 = require("/utils"); -var votes_1 = require("/votes"); -exports.commands = (0, commands_1.commandList)(__assign(__assign({ about: { - args: [], - description: 'Prints information about the plugin.', - perm: commands_1.Perm.none, - handler: function (_a) { - var _b, _c; - var output = _a.output; - output("[accent][cyan]fish-commands[] is the monolithic plugin used for the Fish servers' features.\n[accent]==========\n[accent]Source code available at: [cyan]https://github.com/Fish-Community/fish-commands/\n[accent]Current plugin version: [cyan]".concat((_c = (_b = globals_1.fishPlugin.version) === null || _b === void 0 ? void 0 : _b.slice(0, 8)) !== null && _c !== void 0 ? _c : "[scarlet]null[]", "[]")); - } - }, unpause: (0, commands_1.command)({ - args: [], - description: 'Unpauses the game.', - perm: commands_1.Perm.trusted, - requirements: [commands_1.Req.mode('pvp')], - init: function () { - var data = { unpaused: false }; - Events.on(EventType.PlayEvent, function () { - if (data.unpaused) { - data.unpaused = false; - Vars.state.rules.pvpAutoPause = true; - } - }); - return data; - }, - handler: function (_a) { - var data = _a.data, outputSuccess = _a.outputSuccess; - Vars.state.rules.pvpAutoPause = false; - data.unpaused = true; - Core.app.post(function () { return Vars.state.set(GameState.State.playing); }); - outputSuccess("Unpaused."); - }, - }), tp: { - args: ['player:player'], - description: 'Teleport to another player.', - perm: commands_1.Perm.play, - requirements: [commands_1.Req.modeNot("pvp")], - handler: function (_a) { - var _b, _c, _d; - var args = _a.args, sender = _a.sender; - if (!((_b = sender.unit()) === null || _b === void 0 ? void 0 : _b.spawnedByCore)) - (0, commands_1.fail)("Can only teleport while in a core unit."); - if (sender.team() !== args.player.team()) - (0, commands_1.fail)("Cannot teleport to players on another team."); - if ((_d = (_c = sender.unit()).hasPayload) === null || _d === void 0 ? void 0 : _d.call(_c)) - (0, commands_1.fail)("Cannot teleport to players while holding a payload."); - (0, utils_1.teleportPlayer)(sender.player, args.player.player); - }, - }, clean: (0, commands_1.command)({ - args: [], - description: 'Removes all boulders from the map.', - perm: commands_1.Perm.play, - requirements: [], - data: { lastRanMapStartTime: (_a = maps_1.PartialMapRun.current) === null || _a === void 0 ? void 0 : _a.startTime }, - handler: function (_a) { - return __awaiter(this, arguments, void 0, function (_b) { - var array, removed, i, t; - var sender = _b.sender, outputSuccess = _b.outputSuccess, data = _b.data; - return __generator(this, function (_c) { - switch (_c.label) { - case 0: - if (!maps_1.PartialMapRun.current) - (0, commands_1.fail)("This game is already over."); - if (data.lastRanMapStartTime == maps_1.PartialMapRun.current.startTime) - (0, commands_1.fail)("This command was already run on this map."); - data.lastRanMapStartTime = maps_1.PartialMapRun.current.startTime; - Timer.schedule(function () { return Call.sound(sender.con, Sounds.rockBreak, 1, 1, 0); }, 0, 0.05, 10); - array = ArcReflect.get(Vars.world.tiles, "array"); - removed = 0; - i = 0; - _c.label = 1; - case 1: - if (!(i < array.length)) return [3 /*break*/, 4]; - t = array[i]; - if (!(t.breakable() && t.block() instanceof Prop)) return [3 /*break*/, 3]; - t.removeNet(); - removed++; - if (!(removed % 500 == 0)) return [3 /*break*/, 3]; - return [4 /*yield*/, (0, funcs_1.delay)(100)]; - case 2: - _c.sent(); - _c.label = 3; - case 3: - i++; - return [3 /*break*/, 1]; - case 4: - outputSuccess("Cleared the map of boulders."); - return [2 /*return*/]; - } - }); - }); - } - }), die: { - args: [], - description: 'Kills your unit.', - perm: commands_1.Perm.mod.exceptModes({ - sandbox: commands_1.Perm.play - }, "You do not have permission to die."), - handler: function (_a) { - var _b; - var sender = _a.sender; - (_b = sender.unit()) === null || _b === void 0 ? void 0 : _b.kill(); - }, - }, discord: { - args: [], - description: 'Takes you to our discord.', - perm: commands_1.Perm.none, - handler: function (_a) { - var sender = _a.sender; - Call.openURI(sender.con, config_1.text.discordURL); - }, - }, tilelog: (0, commands_1.command)({ - args: ['persist:boolean?', 'showUUID:boolean?'], - description: 'Checks the history of a tile.', - perm: commands_1.Perm.none, - data: { showUUID: true }, - handler: function (_a) { - var args = _a.args, output = _a.output, outputSuccess = _a.outputSuccess, currentTapMode = _a.currentTapMode, handleTaps = _a.handleTaps, sender = _a.sender, data = _a.data; - var changed = args.showUUID !== undefined && args.showUUID != data.showUUID; - if (args.showUUID !== undefined) { - if (!sender.hasPerm("viewUUIDs")) - (0, commands_1.fail)("You do not have permission to show UUIDs."); - data.showUUID = args.showUUID; - } - if (args.persist && currentTapMode !== "on") { - outputSuccess("Tilelog mode enabled. Click tiles to check their recent history. Run /tilelog to disable."); - handleTaps("on"); - } - else if (args.persist && changed) { - outputSuccess("".concat(data.showUUID ? "Now showing UUIDs." : "No longer showing UUIDs.", " Click tiles to check their recent history. Run /tilelog to disable.")); - handleTaps("on"); - } - else if (currentTapMode == "off" || changed) { - handleTaps("once"); - output("Click on a tile to check its recent history..."); - } - else { - handleTaps("off"); - outputSuccess("Tilelog disabled."); - } - }, - tapped: function (_a) { - var _b; - var tile = _a.tile, x = _a.x, y = _a.y, output = _a.output, sender = _a.sender, admins = _a.admins, data = _a.data; - var historyData = (_b = globals_1.tileHistory["".concat(x, ",").concat(y)]) !== null && _b !== void 0 ? _b : (0, commands_1.fail)("There is no recorded history for the selected tile (".concat(tile.x, ", ").concat(tile.y, ").")); - var history = funcs_1.StringIO.read(historyData, function (str) { return str.readArray(function (d) { return ({ - action: d.readString(2), - uuid: d.readString(3), - time: d.readNumber(16), - type: d.readString(2), - }); }, 1); }); - output("[yellow]Tile history for tile (".concat(tile.x, ", ").concat(tile.y, "):\n") + history.map(function (e) { - var _a, _b; - return globals_1.uuidPattern.test(e.uuid) - ? (sender.hasPerm("viewUUIDs") && data.showUUID - ? "[yellow]".concat((_a = admins.getInfoOptional(e.uuid)) === null || _a === void 0 ? void 0 : _a.plainLastName(), "[lightgray](").concat(e.uuid, ")[yellow] ").concat(e.action, " a [cyan]").concat(e.type, "[] ").concat((0, utils_1.formatTimeRelative)(e.time)) - : "[yellow]".concat((_b = admins.getInfoOptional(e.uuid)) === null || _b === void 0 ? void 0 : _b.plainLastName(), " ").concat(e.action, " a [cyan]").concat(e.type, "[] ").concat((0, utils_1.formatTimeRelative)(e.time))) - : "[yellow]".concat(e.uuid, "[yellow] ").concat(e.action, " a [cyan]").concat(e.type, "[] ").concat((0, utils_1.formatTimeRelative)(e.time)); - }).join('\n')); - } - }), aoelog: (0, commands_1.command)(function () { - var allowedActions = [ - "built", "broke", "rotated", "killed", "configured", "pay-dropped", "picked up", "controlled" - ]; - var cachedPointMap = Object.create(null); - return { - args: ['persist:boolean?', 'amount:number?', 'action:string?'], - description: 'Checks the history of all tiles in the selected region. Can be filtered by action.', - perm: commands_1.Perm.none, - handler: function (_a) { - var args = _a.args, sender = _a.sender, outputSuccess = _a.outputSuccess, currentTapMode = _a.currentTapMode, handleTaps = _a.handleTaps; - if (currentTapMode === "off" || args.action || args.amount) { - if (args.action && !allowedActions.includes(args.action)) - (0, commands_1.fail)("Invalid action. Allowed actions: ".concat(allowedActions.join(", "))); - if (args.amount && args.amount > 100) - (0, commands_1.fail)("Limit cannot be greater than 100."); - cachedPointMap[sender.uuid] = undefined; - handleTaps("on"); - outputSuccess("Aoelog mode enabled. To see the recent history of all tiles in a rectangular region, tap opposite corners of the rectangle. Run /aoelog with no arguments to disable."); - } - else { - handleTaps("off"); - outputSuccess("Aoelog disabled."); - } - }, - tapped: function (_a) { - var x = _a.x, y = _a.y, output = _a.output, outputFail = _a.outputFail, sender = _a.sender, admins = _a.admins, handleTaps = _a.handleTaps, args = _a.args; - function handleArea(p1, p2) { - var minX = Math.min(p1[0], p2[0]); - var maxX = Math.max(p1[0], p2[0]); - var minY = Math.min(p1[1], p2[1]); - var maxY = Math.max(p1[1], p2[1]); - var limitTiles = 0; - var amount = args.amount != null ? Math.floor(Math.abs(args.amount)) : 10; - outer: for (var i = minX; i <= maxX; i++) { - for (var j = minY; j <= maxY; j++) { - var tileData = globals_1.tileHistory["".concat(i, ",").concat(j)]; - if (!tileData) - continue; - var history = funcs_1.StringIO.read(globals_1.tileHistory["".concat(i, ",").concat(j)], function (str) { return str.readArray(function (d) { - var _a, _b, _c; - return ({ - action: (_a = d.readString(2)) !== null && _a !== void 0 ? _a : "??", - uuid: (_b = d.readString(3)) !== null && _b !== void 0 ? _b : "??", - time: d.readNumber(16), - type: (_c = d.readString(2)) !== null && _c !== void 0 ? _c : "??", - }); - }, 1); }); - if (args.action) - history = history.filter(function (e) { return e.action === args.action; }); - if (history.length == 0) - continue; - output("[yellow]Tile history for tile (".concat(i, ", ").concat(j, "):\n") + history.map(function (e) { - var _a, _b; - if (globals_1.uuidPattern.test(e.uuid)) { - if (sender.hasPerm("viewUUIDs")) - return "[yellow]".concat((_a = admins.getInfoOptional(e.uuid)) === null || _a === void 0 ? void 0 : _a.plainLastName(), "[lightgray](").concat(e.uuid, ")[yellow] ").concat(e.action, " a [cyan]").concat(e.type, "[] ").concat((0, utils_1.formatTimeRelative)(e.time)); - else - return "[yellow]".concat((_b = admins.getInfoOptional(e.uuid)) === null || _b === void 0 ? void 0 : _b.plainLastName(), " ").concat(e.action, " a [cyan]").concat(e.type, "[] ").concat((0, utils_1.formatTimeRelative)(e.time)); - } - else - return "[yellow]".concat(e.uuid, "[yellow] ").concat(e.action, " a [cyan]").concat(e.type, "[] ").concat((0, utils_1.formatTimeRelative)(e.time)); - }).join('\n')); - limitTiles++; - if (limitTiles === amount) - break outer; - } - } - if (limitTiles == 0) { - if (args.action) - outputFail("There is no recorded history for the selected region matching the provided filters."); - else - outputFail("There is no recorded history for the selected region."); - } - if (limitTiles == amount) - output("Displaying first ".concat(limitTiles, " entries. To show other entries, increase the limit or select a smaller area.")); - } - var p1 = cachedPointMap[sender.uuid]; - if (!p1) { - cachedPointMap[sender.uuid] = [x, y]; - output("1st point set at (".concat(x, ",").concat(y, ")")); - } - else { - var p2 = [x, y]; - output("2nd point set at (".concat(x, ", ").concat(y, ")")); - var width = Math.abs(p1[0] - p2[0]); - var height = Math.abs(p1[1] - p2[1]); - if (width > 50 || height > 50) - (0, commands_1.fail)("Selection too large: width/height cannot be more than 50."); - handleArea(p1, p2); - cachedPointMap[sender.uuid] = undefined; - if (!args.persist) - handleTaps("off"); - } - }, - }; - }), afk: { - args: [], - description: 'Toggles your afk status.', - perm: commands_1.Perm.none, - handler: function (_a) { - var sender = _a.sender, outputSuccess = _a.outputSuccess; - sender.manualAfk = !sender.manualAfk; - sender.updateName(); - if (sender.manualAfk) - outputSuccess("You are now marked as AFK."); - else - outputSuccess("You are no longer marked as AFK."); - }, - }, vanish: { - args: ['target:player?'], - description: "Toggles visibility of your rank and flags.", - perm: commands_1.Perm.vanish, - handler: function (_a) { - var sender = _a.sender, _b = _a.args.target, target = _b === void 0 ? sender : _b, outputSuccess = _a.outputSuccess; - if (sender.stelled()) - (0, commands_1.fail)("Marked players may not hide flags."); - if (sender.muted) - (0, commands_1.fail)("Muted players may not hide flags."); - if (sender != target && target.hasPerm("blockTrolling")) - (0, commands_1.fail)("Target is insufficentlly trollable."); - if (sender != target && !sender.ranksAtLeast("mod")) - (0, commands_1.fail)("You do not have permission to vanish other players."); - target.showRankPrefix = !target.showRankPrefix; - outputSuccess("".concat(target == sender ? "Your" : "".concat(target.name, "'s"), " rank prefix is now ").concat(target.showRankPrefix ? "visible" : "hidden", ".")); - }, - }, tileid: { - args: [], - description: 'Checks id of a tile.', - perm: commands_1.Perm.none, - handler: function (_a) { - var output = _a.output, handleTaps = _a.handleTaps; - handleTaps("once"); - output("Click a tile to see its id..."); - }, - tapped: function (_a) { - var output = _a.output, f = _a.f, tile = _a.tile; - output(f(templateObject_1 || (templateObject_1 = __makeTemplateObject(["ID is ", ""], ["ID is ", ""])), tile.block().id)); - } - } }, Object.fromEntries(config_1.FishServer.all.map(function (server) { return [ - server.name, - { - args: [], - description: "Switches to the ".concat(server.name, " server."), - perm: server.requiredPerm ? commands_1.Perm.getByName(server.requiredPerm) : commands_1.Perm.none, - isHidden: true, - handler: function (_a) { - var sender = _a.sender, lastUsedSuccessfullySender = _a.lastUsedSuccessfullySender; - if (Date.now() - lastUsedSuccessfullySender > funcs_1.Duration.minutes(1)) - players_1.FishPlayer.messageAllWithPerm(server.requiredPerm, "".concat(sender.name, "[magenta] has gone to the ").concat(server.name, " server. Use [cyan]/").concat(server.name, " [magenta]to join them!")); - Call.connect(sender.con, server.ip, server.port); - }, - }, -]; }))), { switch: { - args: ["server:string", "target:player?"], - description: "Switches to another server.", - perm: commands_1.Perm.play, - handler: function (_a) { - var _b, _c; - var args = _a.args, sender = _a.sender, f = _a.f, lastUsedSuccessfullySender = _a.lastUsedSuccessfullySender; - if (args.target != null && args.target != sender && !sender.canModerate(args.target, true, "admin", true)) - (0, commands_1.fail)(f(templateObject_2 || (templateObject_2 = __makeTemplateObject(["You do not have permission to switch player ", "."], ["You do not have permission to switch player ", "."])), args.target)); - var target = (_b = args.target) !== null && _b !== void 0 ? _b : sender; - if (globals_1.ipPortPattern.test(args.server) && sender.hasPerm("admin")) { - //direct connect - Call.connect.apply(Call, __spreadArray([target.con], __read(args.server.split(":")), false)); - } - else { - var unknownServerMessage = "Unknown server ".concat(args.server, ". Valid options: ").concat(config_1.FishServer.all.filter(function (s) { return !s.requiredPerm || sender.hasPerm(s.requiredPerm); }).map(function (s) { return s.name; }).join(", ")); - var server = (_c = config_1.FishServer.byName(args.server)) !== null && _c !== void 0 ? _c : (0, commands_1.fail)(unknownServerMessage); - //Pretend the server doesn't exist - if (server.requiredPerm && !sender.hasPerm(server.requiredPerm)) - (0, commands_1.fail)(unknownServerMessage); - if (target == sender && Date.now() - lastUsedSuccessfullySender > funcs_1.Duration.minutes(1)) - players_1.FishPlayer.messageAllWithPerm(server.requiredPerm, "".concat(sender.name, "[magenta] has gone to the ").concat(server.name, " server. Use [cyan]/").concat(server.name, " [magenta]to join them!")); - Call.connect(target.con, server.ip, server.port); - } - } - }, s: { - args: ['message:string'], - description: "Sends a message to staff only.", - perm: commands_1.Perm.chat, - handler: function (_a) { - var sender = _a.sender, args = _a.args, outputSuccess = _a.outputSuccess, outputFail = _a.outputFail, lastUsedSender = _a.lastUsedSender; - if (!sender.hasPerm("mod")) { - if (Date.now() - lastUsedSender < 4000) - (0, commands_1.fail)("This command was used recently and is on cooldown. [orange]Misuse of this command may result in a mute."); - } - api.sendStaffMessage(args.message, sender.name, sender.hasPerm("mod"), function (sent) { - if (!sender.hasPerm("mod")) { - if (sent) { - outputSuccess("Message sent to [orange]all online staff."); - } - else { - var wasReceived = players_1.FishPlayer.messageStaff(sender.prefixedName, args.message); - if (wasReceived) - outputSuccess("Message sent to staff."); - else - outputFail("No staff were online to receive your message."); - } - } - }); - }, - }, - /** - * This command is mostly for mobile (or players without foos). - * - * Since the player's unit follows the camera and we are moving the - * camera, we need to keep setting the players real position to the - * spot the command was made. This is pretty buggy but otherwise the - * player will be up the target player's butt - */ - watch: (0, commands_1.command)({ - args: ['player:player?'], - description: "Watch/unwatch a player.", - perm: commands_1.Perm.none, - data: new Set, - handler: function (_a) { - var _b; - var args = _a.args, data = _a.data, sender = _a.sender, outputSuccess = _a.outputSuccess, outputFail = _a.outputFail; - if (data.has(sender.uuid)) { - outputSuccess("No longer watching a player."); - data.delete(sender.uuid); - } - else if (args.player) { - data.add(sender.uuid); - var senderUnit_1 = (_b = sender.unit()) !== null && _b !== void 0 ? _b : (0, commands_1.fail)("You do not have a unit."); - var stayX_1 = senderUnit_1.x; - var stayY_1 = senderUnit_1.y; - var target_1 = args.player.player; - (function watch() { - var _a, _b; - var unit = target_1.unit(); - if (data.has(sender.uuid) && unit) { - // Self.X+(172.5-Self.X)/10 - Call.setCameraPosition(sender.con, unit.x, unit.y); - if (senderUnit_1) - (_b = (_a = sender.unit()) === null || _a === void 0 ? void 0 : _a.set) === null || _b === void 0 ? void 0 : _b.call(_a, stayX_1, stayY_1); - Timer.schedule(function () { return watch(); }, 0.1, 0.1, 0); - } - else { - Call.setCameraPosition(sender.con, stayX_1, stayY_1); - } - })(); - } - else { - outputFail("No player to unwatch."); - } - }, - }), spectate: (0, commands_1.command)(function () { - //TODO revise code - /** Mapping between player and original team */ - var spectators = new Map(); - function spectate(target) { - spectators.set(target, target.team()); - target.forceRespawn(); - target.setTeam(Team.derelict); - target.forceRespawn(); - } - function resume(target) { - if (spectators.get(target) == null) - return; // this state is possible for a person who left not in spectate - target.setTeam(spectators.get(target)); - spectators.delete(target); - target.forceRespawn(); - } - Events.on(EventType.GameOverEvent, function () { return spectators.clear(); }); - Events.on(EventType.PlayerLeave, function (_a) { - var player = _a.player; - return resume(players_1.FishPlayer.get(player)); - }); - return { - args: ["target:player?"], - description: "Toggles spectator mode in PVP games.", - perm: commands_1.Perm.play, - requirements: [commands_1.Req.gameRunning], - handler: function (_a) { - var sender = _a.sender, _b = _a.args.target, target = _b === void 0 ? sender : _b, outputSuccess = _a.outputSuccess, f = _a.f; - if (!config_1.Gamemode.pvp() && !sender.hasPerm("mod")) - (0, commands_1.fail)("You do not have permission to spectate on a non-pvp server."); - if (target !== sender && target.hasPerm("blockTrolling")) - (0, commands_1.fail)("Target player is insufficiently trollable."); - if (target !== sender && !sender.ranksAtLeast("admin")) - (0, commands_1.fail)("You do not have permission to force other players to spectate."); - if (spectators.has(target)) { - resume(target); - outputSuccess(target == sender - ? f(templateObject_3 || (templateObject_3 = __makeTemplateObject(["Rejoining game as team ", "."], ["Rejoining game as team ", "."])), target.team()) : f(templateObject_4 || (templateObject_4 = __makeTemplateObject(["Forced ", " out of spectator mode."], ["Forced ", " out of spectator mode."])), target)); - } - else { - spectate(target); - outputSuccess(target == sender - ? f(templateObject_5 || (templateObject_5 = __makeTemplateObject(["Now spectating. Run /spectate again to resume gameplay."], ["Now spectating. Run /spectate again to resume gameplay."]))) : f(templateObject_6 || (templateObject_6 = __makeTemplateObject(["Forced ", " into spectator mode."], ["Forced ", " into spectator mode."])), target)); - } - } - }; - }), help: { - args: ['name:string?'], - description: 'Displays a list of all commands.', - perm: commands_1.Perm.none, - handler: function (_a) { - var _b; - var args = _a.args, output = _a.output, sender = _a.sender, allCommands = _a.allCommands; - var formatCommand = function (name, color) { - return new funcs_1.StringBuilder() - .add("".concat(color, "/").concat(name)) - .chunk("[white]".concat(allCommands[name].args.map(commands_1.formatArg).join(' '))) - .chunk("[lightgray]- ".concat(allCommands[name].description)).str; - }; - var formatList = function (commandList, color) { return commandList.map(function (c) { return formatCommand(c, color); }).join('\n'); }; - if (args.name && isNaN(parseInt(args.name)) && !['mod', 'admin', 'member'].includes(args.name)) { - //name is not a number or a category, therefore it is probably a command name - if (args.name in allCommands && (!allCommands[args.name].isHidden || allCommands[args.name].perm.check(sender))) { - if (args.name == "help") - achievements_1.Achievements.help_help.grantTo(sender, false); - output("Help for command ".concat(args.name, ":\n\t").concat(allCommands[args.name].description, "\n\tUsage: [sky]/").concat(args.name, " [white]").concat(allCommands[args.name].args.map(commands_1.formatArg).join(' '), "\n\tPermission required: ").concat(allCommands[args.name].perm.name)); - } - else - (0, commands_1.fail)("Command \"".concat(args.name, "\" does not exist.")); - } - else { - var commands_2 = { - player: [], - mod: [], - admin: [], - member: [], - }; - //TODO change this to category, not perm - Object.entries(allCommands).forEach(function (_a) { - var _b = __read(_a, 2), name = _b[0], data = _b[1]; - return (data.perm === commands_1.Perm.admin ? commands_2.admin : data.perm === commands_1.Perm.mod ? commands_2.mod : data.perm === commands_1.Perm.member ? commands_2.member : commands_2.player).push(name); - }); - var chunkedPlayerCommands = (0, funcs_1.to2DArray)(commands_2.player, 15); - switch (args.name) { - case 'admin': - output("".concat(commands_1.Perm.admin.color, "-- Admin commands --\n") + formatList(commands_2.admin, commands_1.Perm.admin.color)); - break; - case 'mod': - output("".concat(commands_1.Perm.mod.color, "-- Mod commands --\n") + formatList(commands_2.mod, commands_1.Perm.mod.color)); - break; - case 'member': - output("".concat(commands_1.Perm.member.color, "-- Member commands --\n") + formatList(commands_2.member, commands_1.Perm.member.color)); - break; - default: { - var pageNumber = args.name != undefined ? parseInt(args.name) : 1; - var page = (_b = chunkedPlayerCommands[pageNumber - 1]) !== null && _b !== void 0 ? _b : (0, commands_1.fail)("\"".concat(args.name, "\" is an invalid page number.")); - output("[sky]-- Commands page [lightgrey]".concat(pageNumber, "/").concat(chunkedPlayerCommands.length, "[sky] --\n") + formatList(page, '[sky]')); - } - } - } - }, - }, msg: { - args: ['player:player', 'message:string'], - description: 'Send a message to only one player.', - perm: commands_1.Perm.chat, - handler: function (_a) { - var args = _a.args, sender = _a.sender, output = _a.output, f = _a.f; - globals_1.recentWhispers[args.player.uuid] = sender.uuid; - args.player.sendMessage("".concat(sender.prefixedName, "[lightgray] whispered:[#BBBBBB] ").concat(args.message)); - output(f(templateObject_7 || (templateObject_7 = __makeTemplateObject(["[lightgray]Whispered to ", "[lightgray]:[#BBBBBB] ", ""], ["[lightgray]Whispered to ", "[lightgray]:[#BBBBBB] ", ""])), args.player, args.message)); - }, - }, r: { - args: ['message:string'], - description: 'Reply to the most recent message.', - perm: commands_1.Perm.chat, - handler: function (_a) { - var _b; - var args = _a.args, sender = _a.sender, output = _a.output, f = _a.f; - var recipient = players_1.FishPlayer.getById((_b = globals_1.recentWhispers[sender.uuid]) !== null && _b !== void 0 ? _b : (0, commands_1.fail)("It doesn't look like someone has messaged you recently. Try whispering to them with [white]\"/msg \"")); - if (!(recipient === null || recipient === void 0 ? void 0 : recipient.connected())) - (0, commands_1.fail)("The person who last messaged you doesn't seem to exist anymore. Try whispering to someone with [white]\"/msg \""); - globals_1.recentWhispers[globals_1.recentWhispers[sender.uuid]] = sender.uuid; - recipient.sendMessage("".concat(sender.name, "[lightgray] whispered:[#BBBBBB] ").concat(args.message)); - output(f(templateObject_8 || (templateObject_8 = __makeTemplateObject(["[lightgray]Whispered to ", "[lightgray]:[#BBBBBB] ", ""], ["[lightgray]Whispered to ", "[lightgray]:[#BBBBBB] ", ""])), recipient, args.message)); - }, - }, trail: { - args: ['type:string?', 'color:string?'], - description: 'Use command to see options and toggle trail on/off.', - perm: commands_1.Perm.none, - handler: function (_a) { - var args = _a.args, sender = _a.sender, output = _a.output, outputFail = _a.outputFail, outputSuccess = _a.outputSuccess; - //overload 1: type not specified - if (!args.type) { - if (sender.trail != null) { - sender.trail = null; - outputSuccess("Trail turned off."); - } - else { - output("Available types:[yellow]\n1 - fluxVapor (flowing smoke, long lasting)\n2 - overclocked (diamonds)\n3 - overdriven (squares)\n4 - shieldBreak (smol)\n5 - upgradeCoreBloom (square, long lasting, only orange)\n6 - electrified (tiny spiratic diamonds, but only green)\n7 - unitDust (same as above but round, and can change colors)\n[white]Usage: [orange]/trail [lightgrey] [color/#hex/r,g,b]"); - } - return; - } - //overload 2: type specified - var trailTypes = { - "1": 'fluxVapor', - "2": 'overclocked', - "3": 'overdriven', - "4": 'shieldBreak', - "5": 'upgradeCoreBloom', - "6": 'electrified', - "7": 'unitDust', - }; - var selectedType = trailTypes[args.type]; - if (!selectedType) { - if (Object.values(trailTypes).includes(args.type)) - (0, commands_1.fail)("Please use the numeric id to refer to a trail type."); - else - (0, commands_1.fail)("\"".concat(args.type, "\" is not an available type.")); - } - var color = args.color ? (0, utils_1.getColor)(args.color) : Color.white; - if (color instanceof Color) { - sender.trail = { - type: selectedType, - color: color, - }; - } - else { - outputFail("[scarlet]Sorry, \"".concat(args.color, "\" is not a valid color.\n[yellow]Color can be in the following formats:\n[pink]pink [white]| [gray]#696969 [white]| 255,0,0.")); - } - }, - }, ohno: (0, commands_1.command)({ - args: [], - description: 'Spawns an ohno.', - perm: commands_1.Perm.play, - init: function () { - var Ohnos = { - enabled: true, - ohnos: new Array(), - makeOhno: function (team, x, y) { - var ohno = UnitTypes.atrax.create(team); - ohno.set(x, y); - ohno.type = UnitTypes.alpha; - ohno.apply(StatusEffects.disarmed, Number.MAX_SAFE_INTEGER); - ohno.resetController(); //does this work? - ohno.add(); - this.ohnos.push(ohno); - return ohno; - }, - updateLength: function () { - this.ohnos = this.ohnos.filter(function (o) { return o && o.isAdded() && !o.dead; }); - }, - checkAchievement: function () { - var e_1, _a; - try { - for (var _b = __values(this.ohnos), _c = _b.next(); !_c.done; _c = _b.next()) { - var ohno = _c.value; - var player = ohno.getPlayer(); - if (player) - achievements_1.Achievements.ohno.grantTo(players_1.FishPlayer.get(player), false); - } - } - catch (e_1_1) { e_1 = { error: e_1_1 }; } - finally { - try { - if (_c && !_c.done && (_a = _b.return)) _a.call(_b); - } - finally { if (e_1) throw e_1.error; } - } - }, - killAll: function () { - this.ohnos.forEach(function (ohno) { var _a; return (_a = ohno === null || ohno === void 0 ? void 0 : ohno.kill) === null || _a === void 0 ? void 0 : _a.call(ohno); }); - this.ohnos = []; - }, - amount: function () { - return this.ohnos.length; - }, - }; - Events.on(EventType.GameOverEvent, function (e) { - Ohnos.killAll(); - }); - Timer.schedule(function () { return Ohnos.checkAchievement(); }, 1, 2); - return Ohnos; - }, - requirements: [ - commands_1.Req.gameRunning, commands_1.Req.modeNot("pvp"), - commands_1.Req.unitExists("You cannot spawn ohnos while dead.") - ], - handler: function (_a) { - var sender = _a.sender, Ohnos = _a.data; - if (!Ohnos.enabled) - (0, commands_1.fail)("Ohnos have been temporarily disabled."); - Ohnos.updateLength(); - if (Ohnos.ohnos.length >= (Groups.player.size() + 1) || - sender.team().data().countType(UnitTypes.alpha) >= Units.getCap(sender.team())) - (0, commands_1.fail)("Sorry, the max number of ohno units has been reached."); - if ((0, utils_1.nearbyEnemyTile)((sender.unit()), 6) != null) - (0, commands_1.fail)("Too close to an enemy building!"); - if (!UnitTypes.alpha.supportsEnv(Vars.state.rules.env)) - (0, commands_1.fail)("Ohnos cannot survive in this map."); - Ohnos.makeOhno(sender.team(), sender.player.x, sender.player.y); - }, - }), ranks: { - args: [], - description: 'Displays information about all ranks.', - perm: commands_1.Perm.none, - handler: function (_a) { - var output = _a.output; - output("List of ranks:\n" + - Object.values(ranks_1.Rank.ranks) - .map(function (rank) { return "".concat(rank.prefix, " ").concat(rank.color).concat((0, funcs_1.capitalizeText)(rank.name), "[]: ").concat(rank.color).concat(rank.description, "[]\n"); }) - .join("") + - "List of flags:\n" + - Object.values(ranks_1.RoleFlag.flags) - .map(function (flag) { return "".concat(flag.prefix, " ").concat(flag.color).concat((0, funcs_1.capitalizeText)(flag.name), "[]: ").concat(flag.color).concat(flag.description, "[]\n"); }) - .join("")); - }, - }, rules: { - args: ['player:player?'], - description: 'Displays the server rules.', - perm: commands_1.Perm.none, - handler: function (_a) { - var _b; - var args = _a.args, sender = _a.sender, output = _a.output, outputSuccess = _a.outputSuccess, f = _a.f; - var target = (_b = args.player) !== null && _b !== void 0 ? _b : sender; - if (target !== sender) { - if (!sender.hasPerm("warn")) - (0, commands_1.fail)("You do not have permission to show rules to other players."); - if (!sender.canModerate(target)) - commands_1.Req.cooldown(funcs_1.Duration.minutes(10)); - if (target.hasPerm("blockTrolling")) - (0, commands_1.fail)(f(templateObject_9 || (templateObject_9 = __makeTemplateObject(["Player ", " is insufficiently trollable."], ["Player ", " is insufficiently trollable."])), args.player)); - } - void menus_1.Menu.menu("Rules for [#0000ff]>|||> FISH [white]servers", config_1.rules.join("\n\n"), ["[green]I agree to abide by these rules[]", "No"], target, { onCancel: "null" }).then(function (option) { - if (option == "No") { - target.kick("You must agree to the rules to play on this server. Rejoin to agree to the rules.", 1); - outputSuccess('Player rejected the rules and was kicked.'); - } - else if (option == null) { - output('Player closed the menu.'); - } - else { - outputSuccess('Player acknowledged the rules.'); - } - }); - if (target !== sender) - outputSuccess(f(templateObject_10 || (templateObject_10 = __makeTemplateObject(["Reminded ", " of the rules."], ["Reminded ", " of the rules."])), target)); - }, - }, void: { - args: ["player:player?"], - description: 'Warns other players about power voids.', - perm: commands_1.Perm.play, - requirements: function (_a) { - var args = _a.args; - return [ - commands_1.Req.mode("attack"), - args.player ? commands_1.Req.cooldown(20000) : commands_1.Req.cooldownGlobal(10000) - ]; - }, - handler: function (_a) { - var args = _a.args, sender = _a.sender, outputSuccess = _a.outputSuccess, f = _a.f; - if (args.player) { - if (!sender.hasPerm("trusted")) - (0, commands_1.fail)("You do not have permission to show popups to other players, please run /void with no arguments to send a chat message to everyone."); - if (args.player !== sender && args.player.hasPerm("blockTrolling")) - (0, commands_1.fail)("Target player is insufficiently trollable."); - void menus_1.Menu.menu("\uf83f [scarlet]WARNING[] \uf83f", "[white]Don't break the Power Void (\uF83F), it's a trap!\nPower voids disable anything they are connected to.\nIf you break it, [scarlet]you will get attacked[] by enemy units.\nPlease stop attacking and [lime]build defenses[] first!", ["I understand"], args.player, { onCancel: 'null' }).then(function () { return outputSuccess(f(templateObject_11 || (templateObject_11 = __makeTemplateObject(["Player ", " acknowledged the warning."], ["Player ", " acknowledged the warning."])), args.player)); }); - (0, utils_1.logAction)("showed void warning", sender, args.player); - outputSuccess(f(templateObject_12 || (templateObject_12 = __makeTemplateObject(["Warned ", " about power voids with a popup message."], ["Warned ", " about power voids with a popup message."])), args.player)); - } - else { - Call.sendMessage("[white]Don't break the Power Void (\uF83F), it's a trap!\nPower voids disable anything they are connected to. If you break it, [scarlet]you will get attacked[] by enemy units.\nPlease stop attacking and [lime]build defenses[] first!"); - } - }, - }, team: { - args: ['team:team', 'reason:string?'], - description: 'Changes your team.', - perm: commands_1.Perm.changeTeam, - handler: function (_a) { - var _b; - var sender = _a.sender, _c = _a.args, team = _c.team, reason = _c.reason, outputSuccess = _a.outputSuccess, f = _a.f; - if (config_1.Gamemode.sandbox() && globals_1.fishState.peacefulMode && !sender.hasPerm("admin")) - (0, commands_1.fail)("You do not have permission to change teams because peaceful mode is on."); - if (config_1.Gamemode.sandbox() && team === Vars.state.rules.waveTeam && !sender.hasPerm("admin")) - (0, commands_1.fail)("You do not have permission to change to the wave team on sandbox."); - if (!(config_1.Gamemode.sandbox() || config_1.Gamemode.testsrv()) && !sender.hasPerm("mod") && !reason) - (0, commands_1.fail)("Please specify a reason for changing teams."); - if (!sender.hasPerm("changeTeamExternal")) { - if (team.data().cores.size <= 0) - (0, commands_1.fail)("You do not have permission to change to a team with no cores."); - if (!sender.player.dead() && !((_b = sender.unit()) === null || _b === void 0 ? void 0 : _b.spawnedByCore)) - sender.forceRespawn(); - } - if (!sender.hasPerm("mod")) - sender.changedTeam = true; - sender.setTeam(team); - outputSuccess(f(templateObject_13 || (templateObject_13 = __makeTemplateObject(["Changed your team to ", "."], ["Changed your team to ", "."])), team)); - if (reason && !config_1.Gamemode.sandbox()) - (0, utils_1.logAction)("changed team to ".concat(team.name, " on ").concat((0, funcs_1.escapeTextDiscord)(Vars.state.map.plainName()), " with reason ").concat((0, funcs_1.escapeTextDiscord)(reason)), sender); - }, - }, teamp: { - args: ['team:team', 'target:player'], - description: 'Changes the team of a player.', - perm: commands_1.Perm.changeTeam, - handler: function (_a) { - var _b; - var sender = _a.sender, _c = _a.args, team = _c.team, target = _c.target, outputSuccess = _a.outputSuccess, f = _a.f; - if (!sender.canModerate(target, true, "mod", true)) - (0, commands_1.fail)(f(templateObject_14 || (templateObject_14 = __makeTemplateObject(["You do not have permission to change the team of ", ""], ["You do not have permission to change the team of ", ""])), target)); - if (config_1.Gamemode.sandbox() && globals_1.fishState.peacefulMode && !sender.hasPerm("admin")) - (0, commands_1.fail)("You do not have permission to change teams because peaceful mode is on."); - if (!sender.hasPerm("changeTeamExternal")) { - if (team.data().cores.size <= 0) - (0, commands_1.fail)("You do not have permission to change to a team with no cores."); - if (!target.player.dead() && !((_b = target.unit()) === null || _b === void 0 ? void 0 : _b.spawnedByCore)) - target.forceRespawn(); - } - target.setTeam(team); - outputSuccess(f(templateObject_15 || (templateObject_15 = __makeTemplateObject(["Changed team of player ", " to ", "."], ["Changed team of player ", " to ", "."])), target, team)); - }, - }, rank: { - args: ['player:player'], - description: 'Displays the rank of a player.', - perm: commands_1.Perm.none, - handler: function (_a) { - var args = _a.args, output = _a.output, f = _a.f; - output(f(templateObject_16 || (templateObject_16 = __makeTemplateObject(["Player ", "'s rank is ", "."], ["Player ", "'s rank is ", "."])), args.player, args.player.rank)); - }, - }, forcevnw: { - args: ["force:boolean?"], - description: 'Force skip to the next wave.', - perm: commands_1.Perm.admin, - handler: function (_a) { - var allCommands = _a.allCommands, sender = _a.sender, _b = _a.args.force, force = _b === void 0 ? true : _b; - if (allCommands.vnw.data.manager.session == null) { - if (force == false) - (0, commands_1.fail)("Cannot clear votes for VNW because no vote is currently ongoing."); - (0, utils_1.skipWaves)(1, true); - } - else { - if (force) - Call.sendMessage("VNW: [green]Vote was forced by admin [yellow]".concat(sender.name, "[green], skipping wave.")); - else - Call.sendMessage("VNW: [red]Votes cleared by admin [yellow]".concat(sender.name, "[red].")); - allCommands.vnw.data.manager.forceVote(force); - } - }, - }, vnw: (0, commands_1.command)({ - args: ["waves:number?"], - description: "Vote to start the next wave.", - perm: commands_1.Perm.play, - init: function () { return ({ - manager: new votes_1.VoteManager(funcs_1.Duration.minutes(1.5)) - .on("success", function (t) { return (0, utils_1.skipWaves)(t.session.data, true); }) - .on("vote passed", function () { return Call.sendMessage('VNW: [green]Vote passed, skipping to next wave.'); }) - .on("vote failed", function () { return Call.sendMessage('VNW: [red]Vote failed.'); }) - .on("player vote change", function (t, player) { return Call.sendMessage("VNW: ".concat(player.name, " [white] has voted on skipping [accent]").concat(t.session.data, "[white] wave(s). [green]").concat(t.currentVotes(), "[white] votes, [green]").concat(t.requiredVotes(), "[white] required.")); }) - .on("player vote removed", function (t, player) { return Call.sendMessage("VNW: ".concat(player.name, " [white] has left. [green]").concat(t.currentVotes(), "[white] votes, [green]").concat(t.requiredVotes(), "[white] required.")); }) - }); }, - requirements: [commands_1.Req.cooldown(3000), commands_1.Req.integerRange("waves", 1, 15), commands_1.Req.mode("survival", "testsrv"), commands_1.Req.gameRunning], - handler: function (_a) { - return __awaiter(this, arguments, void 0, function (_b) { - var _c; - var sender = _b.sender, waves = _b.args.waves, manager = _b.data.manager; - return __generator(this, function (_d) { - switch (_d.label) { - case 0: - if (!!manager.session) return [3 /*break*/, 4]; - if (!(waves !== null && waves !== void 0)) return [3 /*break*/, 1]; - _c = waves; - return [3 /*break*/, 3]; - case 1: return [4 /*yield*/, menus_1.Menu.menu("Start a Next Wave Vote", "Select the amount of waves you would like to skip.", [1, 5, 10], sender, { - includeCancel: true, - optionStringifier: function (n) { return "".concat(n, " waves"); } - })]; - case 2: - _c = (waves = _d.sent()); - _d.label = 3; - case 3: - _c; - if (manager.session) { - //Someone else started a vote - if (manager.session.data != waves) - (0, commands_1.fail)("Someone else started a vote with a different number of waves to skip."); - else - manager.vote(sender, sender.voteWeight(), waves); - } - else { - manager.start(sender, sender.voteWeight(), waves); - } - return [3 /*break*/, 5]; - case 4: - manager.vote(sender, sender.voteWeight(), null); - _d.label = 5; - case 5: return [2 /*return*/]; - } - }); - }); - } - }), forcertv: { - args: ["force:boolean?"], - description: 'Force skip to the next map.', - perm: commands_1.Perm.admin, - handler: function (_a) { - var _b = _a.args.force, force = _b === void 0 ? true : _b, sender = _a.sender, allCommands = _a.allCommands; - if (allCommands.rtv.data.manager.session == null) { - if (force == false) - (0, commands_1.fail)("Cannot clear votes for RTV because no vote is currently ongoing."); - allCommands.rtv.data.manager.forceVote(true); - } - else { - if (force) - Call.sendMessage("RTV: [green]Vote was forced by admin [yellow]".concat(sender.name, "[green].")); - else - Call.sendMessage("RTV: [red]Votes cleared by admin [yellow]".concat(sender.name, "[red].")); - allCommands.rtv.data.manager.forceVote(force); - } - } - }, rtv: (0, commands_1.command)({ - args: [], - description: 'Rock the vote to change map.', - perm: commands_1.Perm.play, - init: function () { return ({ - manager: new votes_1.VoteManager(funcs_1.Duration.minutes(1.5), config_1.Gamemode.hexed() ? ["fractionOfVoters", 1] : undefined) //Require unanimity in Hexed, as it is often 1 v everyone - .on("success", function () { return (0, utils_1.neutralGameover)(); }) - .on("vote passed", function () { return Call.sendMessage("RTV: [green]Vote has passed, changing map."); }) - .on("vote failed", function () { return Call.sendMessage("RTV: [red]Vote failed."); }) - .on("player vote change", function (t, player, oldVote, newVote) { return Call.sendMessage("RTV: ".concat(player.name, "[white] ").concat(oldVote == newVote ? "still " : "", "wants to change the map. [green]").concat(t.currentVotes(), "[white] votes, [green]").concat(t.requiredVotes(), "[white] required.")); }) - .on("player vote removed", function (t, player) { return Call.sendMessage("RTV: ".concat(player.name, "[white] has left the game. [green]").concat(t.currentVotes(), "[white] votes, [green]").concat(t.requiredVotes(), "[white] required.")); }) - }); }, - requirements: [commands_1.Req.cooldown(3000), commands_1.Req.gameRunning], - handler: function (_a) { - var sender = _a.sender, manager = _a.data.manager; - manager.vote(sender, 1, 0); //No weighting for RTV except for removing AFK players - } - }), - // votekick: command({ - // args: ["target:player"], - // description: "Starts a vote to kick a player.", - // perm: Perm.play, - // data: new VoteManager( - // Duration.seconds(20), - // ["absolute", 3], - // (fishP, target) => fishP.team() == target.team() || fishP.hasPerm("voteOtherTeams") - // ), - // handler({args, sender, data: votekickmanager}){ - // if(votekickmanager.session) fail(`There is already a votekick in progress.`); - // votekickmanager.start(sender, 1, args.target); - // } - // }), - // vote: { - // args: ["vote:boolean"], - // description: "Use /votekick instead.", - // perm: Perm.play, - // handler({sender, args, allCommands}){ - // const votekickmanager = allCommands.votekick.data; - // votekickmanager.handleVote(sender, args ? 1 : -1); - // } - // }, - forcenextmap: { - args: ["map:mapOrRandom"], - description: 'Override the next map in queue.', - perm: commands_1.Perm.admin.exceptModes({ - testsrv: commands_1.Perm.play - }), - handler: function (_a) { - var allCommands = _a.allCommands, args = _a.args, sender = _a.sender, outputSuccess = _a.outputSuccess, f = _a.f; - Vars.maps.setNextMapOverride(args.map == "random" ? null : args.map); - if (allCommands.nextmap.data.voteEndTime() > -1) { - //Cancel /nextmap vote if it's ongoing - allCommands.nextmap.data.resetVotes(); - Call.sendMessage("[red]Admin ".concat(sender.name, "[red] has cancelled the vote. The next map will be ").concat(args.map == "random" ? "random" : "[yellow]".concat(args.map.name()), ".")); - } - else { - outputSuccess(f(templateObject_17 || (templateObject_17 = __makeTemplateObject(["Forced the next map to be ", "."], ["Forced the next map to be ", "."])), args.map == "random" ? "random" : "\"".concat(args.map.name(), "\" by ").concat(args.map.author()))); - } - }, - }, maps: { - args: [], - description: 'Lists the available maps.', - perm: commands_1.Perm.none, - handler: function (_a) { - var output = _a.output; - output("[yellow]Use [white]/nextmap [lightgray] [yellow]to vote on a map.\n\n[blue]Available maps:\n_________________________\n".concat(Vars.maps.customMaps().toArray().map(function (map) { - return "[yellow]".concat(map.name()); - }).join("\n"))); - } - }, nextmap: (0, commands_1.command)(function () { - var random = { - name: function () { return "[lightgray]Random"; }, - plainName: function () { return "random"; } - }; - var votes = new Map(); - var lastVoteCount = 0; - var lastVoteTime = 0; - var voteEndTime = -1; - var voteDuration = funcs_1.Duration.minutes(1.5); - var task = null; - function resetVotes() { - votes.clear(); - voteEndTime = -1; - task === null || task === void 0 ? void 0 : task.cancel(); - } - function getMapData() { - return __spreadArray([], __read(votes.values()), false).reduce(function (acc, map) { return (acc.increment(map), acc); }, new ObjectIntMap()).entries().toArray(); - } - function showVotes() { - Call.sendMessage("[green]Current votes:\n------------------------------\n".concat(getMapData().map(function (_a) { - var map = _a.key, votes = _a.value; - return "[cyan]".concat(map.name(), "[yellow]: ").concat(votes); - }).toString("\n"))); - } - function startVote() { - voteEndTime = Date.now() + voteDuration; - task = Timer.schedule(endVote, voteDuration / 1000); - } - function endVote() { - if (voteEndTime == -1) - return; //aborted somehow - if (votes.size == 0) - return; //no votes? - if (votes.size + 2 <= lastVoteCount && (Date.now() - lastVoteTime) < funcs_1.Duration.minutes(10)) { - //If the number of votes is 2 less than the previous number of votes for a vote in the past 10 minutes, abor - Call.sendMessage("[cyan]Next Map Vote: [scarlet]Vote aborted because a previous vote had significantly higher turnout"); - resetVotes(); - return; - } - else { - lastVoteTime = Date.now(); - lastVoteCount = votes.size; - } - var mapData = getMapData(); - var highestVoteCount = mapData.max(floatf(function (e) { return e.value; })).value; - var highestVotedMaps = mapData.select(function (e) { return e.value == highestVoteCount; }); - var winner; - if (highestVotedMaps.size > 1) { - winner = highestVotedMaps.random().key; - Call.sendMessage("[green]There was a tie between the following maps:\n".concat(highestVotedMaps.map(function (_a) { - var map = _a.key, votes = _a.value; - return "[cyan]".concat(map.name(), "[yellow]: ").concat(votes); - }).toString("\n"), "\n[green]Picking random winner: [yellow]").concat(winner.name())); - } - else { - winner = highestVotedMaps.get(0).key; - Call.sendMessage("[green]Map voting complete! The next map will be [yellow]".concat(winner.name(), " [green]with [yellow]").concat(highestVoteCount, "[green] votes.")); - } - Vars.maps.setNextMapOverride(winner == random ? null : null); - resetVotes(); - } - Events.on(EventType.GameOverEvent, resetVotes); - Events.on(EventType.ServerLoadEvent, resetVotes); - return { - args: ['map:mapOrRandom'], - description: 'Allows you to vote for the next map. Use /maps to see all available maps.', - perm: commands_1.Perm.play, - data: { votes: votes, voteEndTime: function () { return voteEndTime; }, resetVotes: resetVotes, endVote: endVote }, - requirements: [commands_1.Req.cooldown(10000)], - handler: function (_a) { - var args = _a.args, sender = _a.sender; - var map = args.map === "random" ? random : args.map; - if (config_1.Gamemode.testsrv()) - (0, commands_1.fail)("Please use /forcenextmap instead."); - if (votes.get(sender)) - (0, commands_1.fail)("You have already voted."); - if (voteEndTime == -1) { - if ((Date.now() - lastVoteTime) < funcs_1.Duration.minutes(1)) - (0, commands_1.fail)("Please wait 1 minute before starting a new map vote."); - startVote(); - votes.set(sender, map); - Call.sendMessage("[cyan]Next Map Vote: ".concat(sender.name, "[cyan] started a map vote, and voted for [yellow]").concat(map.name(), "[cyan]. Use [white]/nextmap ").concat(map.plainName(), "[] to add your vote, or run [white]/maps[] to see other available maps.")); - } - else { - votes.set(sender, map); - Call.sendMessage("[cyan]Next Map Vote: ".concat(sender.name, "[cyan] voted for [yellow]").concat(map.name(), "[cyan]. Time left: [scarlet]").concat((0, utils_1.formatTimeRelative)(voteEndTime, true))); - showVotes(); - } - } - }; - }), surrender: (0, commands_1.command)(function () { - var prefix = "[orange]Surrender[white]: "; - var managers = Team.all.map(function (team) { - return new votes_1.VoteManager(funcs_1.Duration.minutes(1.5), ["fractionOfVoters", config_1.Gamemode.hexed() ? 1 : 3 / 4], function (p) { return p.team() == team; }) - .on("success", function () { return team.cores().copy().each(function (c) { return c.kill(); }); }) - .on("vote passed", function () { return Call.sendMessage(prefix + "Team ".concat(team.coloredName(), " has voted to forfeit this match.")); }) - .on("vote failed", function (t) { return t.messageEligibleVoters(prefix + "Team ".concat(team.coloredName(), " has chosen not to forfeit this match.")); }) - .on("player vote change", function (t, player, oldVote, newVote) { return t.messageEligibleVoters(prefix + "".concat(player.name, "[white] ").concat(oldVote == newVote ? "still " : "", "wants to forfeit this match. [orange]").concat(t.currentVotes(), "[white] votes, [orange]").concat(t.requiredVotes(), "[white] required.")); }) - .on("player vote removed", function (t, player) { return t.messageEligibleVoters(prefix + "Player ".concat(player.name, "[white] has left the game. [orange]").concat(t.currentVotes(), "[white] votes, [orange]").concat(t.requiredVotes(), "[white] required.")); }); - }); - globals_1.FishEvents.on("playerTeamChange", function (_, fishP, previous) { - managers[previous.id].unvote(fishP); - }); - return { - args: ["force:boolean?", "team:team?"], - description: "Vote to surrender to the enemy team.", - perm: commands_1.Perm.play, - requirements: [commands_1.Req.mode("pvp"), commands_1.Req.teamAlive], - data: { managers: managers }, - handler: function (_a) { - return __awaiter(this, arguments, void 0, function (_b) { - var t, manager; - var sender = _b.sender, _c = _b.args, force = _c.force, team = _c.team; - return __generator(this, function (_d) { - switch (_d.label) { - case 0: - t = sender.hasPerm("admin") && team ? team : sender.team(); - manager = managers[t.id]; - if (!(sender.hasPerm("admin") && force != undefined)) return [3 /*break*/, 4]; - if (!force) return [3 /*break*/, 2]; - return [4 /*yield*/, menus_1.Menu.confirmDangerous(sender, "Are you sure you want to force team ".concat(t.coloredName(), "[] to lose?"))]; - case 1: - _d.sent(); - manager.messageEligibleVoters(prefix + "Vote forced by admin ".concat(sender.name, "[white].")); - Call.sendMessage(prefix + "Team ".concat(t.coloredName(), " has voted to forfeit this match.")); - return [3 /*break*/, 3]; - case 2: - manager.messageEligibleVoters(prefix + "Votes cleared by admin ".concat(sender.name, "[white].")); - _d.label = 3; - case 3: - manager.forceVote(force); - return [2 /*return*/]; - case 4: - if (sender.ranksAtLeast("mod")) - commands_1.Req.cooldown(5000); - else - commands_1.Req.cooldown(20000); - manager.vote(sender, 1, 0); - return [2 /*return*/]; - } - }); - }); - }, - }; - }), stats: { - args: ["target:player", "global:boolean?"], - perm: commands_1.Perm.none, - description: "Views a player's stats.", - handler: function (_a) { - var _b = _a.args, target = _b.target, _c = _b.global, global = _c === void 0 ? false : _c, output = _a.output, f = _a.f; - var stats = global ? target.globalStats : target.stats; - output(f(templateObject_18 || (templateObject_18 = __makeTemplateObject(["[accent]Statistics for player ", " ", ":\n(note: we started recording statistics on 22 Jan 2024)\n[white]--------------[]\nBlocks broken: ", "\nBlocks placed: ", "\nChat messages sent: ", "\nGames finished: ", "\nTime in-game: ", "\nWin rate: ", ""], ["[accent]\\\nStatistics for player ", " ", ":\n(note: we started recording statistics on 22 Jan 2024)\n[white]--------------[]\nBlocks broken: ", "\nBlocks placed: ", "\nChat messages sent: ", "\nGames finished: ", "\nTime in-game: ", "\nWin rate: ", ""])), target, global ? "across all servers" : "on this server", stats.blocksBroken, stats.blocksPlaced, stats.chatMessagesSent, stats.gamesFinished, (0, utils_1.formatTime)(stats.timeInGame), stats.gamesWon / stats.gamesFinished)); - } - }, showworld: { - args: ["x:number?", "y:number?", "size:number?"], - perm: commands_1.Perm.none, - description: "Views the world as a 2D scrollable menu.", - requirements: [commands_1.Req.cooldown(4000), commands_1.Req.integerRange("size", 1, 20)], - handler: function (_a) { - var sender = _a.sender, _b = _a.args, _c = _b.size, size = _c === void 0 ? 7 : _c, x = _b.x, y = _b.y; - if (Vars.state.rules.fog) - (0, commands_1.fail)("This command is disabled when fog is enabled."); - var options = (0, funcs_1.to2DArray)(Reflect.get(Vars.world.tiles, "array").map(function (tile) { return ({ - text: tile.block().emoji(), - data: null, - }); }), Vars.world.width()).reverse(); - var height = Vars.world.height(); - void menus_1.Menu.scroll2D(sender, "The World", "Use the arrow keys to navigate around the world. Click a blank square to exit.", options, { - columns: size, - rows: size, - x: x ? x - Math.trunc(size / 2) : 0, - y: height - (y ? y + 1 + Math.trunc(size / 2) : size), - getCenterText: function (x, y) { return "".concat(x, ",").concat(height - y - size); } - }); - } - }, mapinfo: { - args: ["map:map?"], - perm: commands_1.Perm.none, - description: "Displays information about a map.", - handler: function (_a) { - var _b; - var output = _a.output, map = _a.args.map, f = _a.f, sender = _a.sender; - if (map) { - var fmap = (_b = maps_1.FMap.getCreate(map)) !== null && _b !== void 0 ? _b : (0, commands_1.fail)("Map data is still being loaded, try again later."); - output(fmap.displayStats(f)); - } - else { - void menus_1.Menu.textPages(sender, Vars.maps.customMaps().map(function (m) { - return ["Map information", function () { var _a, _b; return (_b = (_a = maps_1.FMap.getCreate(m)) === null || _a === void 0 ? void 0 : _a.displayStats(f)) !== null && _b !== void 0 ? _b : (0, commands_1.fail)("Map data is still being loaded, try again later."); }]; - }).toArray(), [], { - startPage: Vars.maps.customMaps().toArray().indexOf(Vars.state.map), - }); - } - } - }, gamemode: { - args: ["mode:string"], - perm: new commands_1.Perm("changeGamemode", "manager").exceptModes({ - testsrv: commands_1.Perm.play, - }), - description: "Sets the gamemode.", - requirements: [commands_1.Req.cooldownGlobal(10000)], - handler: function (_a) { - var args = _a.args, sender = _a.sender, outputSuccess = _a.outputSuccess, lastUsedSuccessfully = _a.lastUsedSuccessfully; - if (!sender.hasPerm('trusted')) - commands_1.Req.cooldownGlobal(30000)({ lastUsedSuccessfully: lastUsedSuccessfully }); - //Unpause - Vars.state.set(GameState.State.playing); - switch (args.mode) { - case "attack": - Vars.state.rules.attackMode = true; - Vars.state.rules.pvp = false; - Vars.state.rules.infiniteResources = false; - break; - case "survival": - Vars.state.rules.attackMode = false; - Vars.state.rules.waves = true; - Vars.state.rules.pvp = false; - Vars.state.rules.infiniteResources = false; - break; - case "pvp": - Vars.state.rules.attackMode = true; - Vars.state.rules.pvp = true; - Vars.state.rules.waves = false; - Vars.state.rules.infiniteResources = false; - break; - case "sandbox": - Vars.state.rules.attackMode = true; - Vars.state.rules.pvp = false; - Vars.state.rules.waves = false; - Vars.state.rules.infiniteResources = true; - break; - default: (0, commands_1.fail)("Invalid mode, valid modes are: attack, survival, pvp"); - } - var reloader = new WorldReloader(); - Reflect.set(reloader, "wasServer", true); - Reflect.set(reloader, "players", Groups.player.copy()); - Call.worldDataBegin(); - reloader.end(); - Call.sendMessage("[orange]Player ".concat(sender.cleanedName, " changed the gamemode to ").concat(args.mode)); - outputSuccess("Changed mode to ".concat(args.mode)); - } - }, mixunit: { - args: ["type:unittype", "base:unittype"], - description: "Spawns a unit that is made of two unit types mixed together.", - perm: commands_1.Perm.admin.exceptModes({ - sandbox: commands_1.Perm.play - }), - requirements: function (_a) { - var sender = _a.sender; - return [!sender.hasPerm("admin") && commands_1.Req.cooldown(1500), commands_1.Req.unitExists()].filter(Boolean); - }, - handler: function (_a) { - var args = _a.args, sender = _a.sender, f = _a.f, outputSuccess = _a.outputSuccess; - var _b = sender.unit(), team = _b.team, x = _b.x, y = _b.y; - var unit = args.base.create(team); - unit.type = args.type; - unit.maxHealth = args.type.health; //because half-dead units aren't fun - unit.set(x, y); - unit.add(); - outputSuccess(f(templateObject_19 || (templateObject_19 = __makeTemplateObject(["Spawned a ", " that is partly a ", "."], ["Spawned a ", " that is partly a ", "."])), args.type, args.base)); - } - }, achievement: { - args: ["name:string?", "verbose:boolean?"], - description: "Displays information on a specific achievement.", - perm: commands_1.Perm.none, - handler: function (_a) { - return __awaiter(this, arguments, void 0, function (_b) { - var matching, achievement, _c; - var _d = _b.args, _e = _d.name, name = _e === void 0 ? "" : _e, _f = _d.verbose, verbose = _f === void 0 ? false : _f, sender = _b.sender, f = _b.f, output = _b.output; - return __generator(this, function (_g) { - switch (_g.label) { - case 0: - name = Strings.stripColors(name.toLowerCase()); - matching = achievements_1.Achievement.all.filter(function (a) { return Strings.stripColors(a.name).toLowerCase().includes(name); }); - if (matching.length == 0) - (0, commands_1.fail)(f(templateObject_20 || (templateObject_20 = __makeTemplateObject(["No achievements found with name ", ". To view all achievements, run [accent]/achievements[]."], ["No achievements found with name ", ". To view all achievements, run [accent]/achievements[]."])), name)); - if (!(matching.length > 2)) return [3 /*break*/, 2]; - return [4 /*yield*/, menus_1.Menu.pagedList(sender, "Achievement", "Select an achievement to view", matching, { - onCancel: "reject", - columns: 2, - optionStringifier: function (a) { return "".concat(a.icon, "[] ").concat(a.name); } - })]; - case 1: - _c = _g.sent(); - return [3 /*break*/, 3]; - case 2: - _c = matching[0]; - _g.label = 3; - case 3: - achievement = _c; - output(config_1.FColor.achievement(templateObject_21 || (templateObject_21 = __makeTemplateObject(["Achievement ", " ", "\n[white]--------------[]\n", "\nAllowed modes: ", "\nUnlocked: ", "\n", "", "", ""], ["\\\nAchievement ", " ", "\n[white]--------------[]\n", "\nAllowed modes: ", "\nUnlocked: ", "\n", "\\\n", "\\\n", "\\\n"])), achievement.icon, achievement.name, achievement.description + (achievement.extendedDescription ? ("\n" + "[gray]".concat(achievement.extendedDescription)) : ""), achievement.modesText, f.boolGood(achievement.has(sender)), verbose ? "[gray]ID: (".concat(achievement.nid, ")").concat(achievement.sid, "\n") : "", verbose ? "[gray]Notifies: ".concat(achievement.notify, "\n") : "", achievement.hidden ? "This achievement is secret." : "")); - return [2 /*return*/]; - } - }); - }); - } - }, achievementlist: { - args: ["target:player?"], - description: "Shows all achievements in a paged menu.", - perm: commands_1.Perm.none, - handler: function (_a) { - return __awaiter(this, arguments, void 0, function (_b) { - var sender = _b.sender, _c = _b.args.target, target = _c === void 0 ? sender : _c, f = _b.f; - return __generator(this, function (_d) { - switch (_d.label) { - case 0: return [4 /*yield*/, menus_1.Menu.textPages(sender, achievements_1.Achievement.all.filter(function (a) { return !a.hidden || a.has(target); }) - .map(function (a) { return [ - "".concat(a.icon, "[] ").concat(a.name), - function () { return config_1.FColor.achievement(templateObject_22 || (templateObject_22 = __makeTemplateObject(["", "\nAllowed modes: ", "\nUnlocked: ", "\n", ""], ["\\\n", "\nAllowed modes: ", "\nUnlocked: ", "\n", "\\\n"])), a.description + (a.extendedDescription ? ("\n" + "[gray]".concat(a.extendedDescription)) : ""), a.modesText, f.boolGood(a.has(target)), a.hidden ? "This achievement is secret." : ""); } - ]; }))]; - case 1: - _d.sent(); - return [2 /*return*/]; - } - }); - }); - } - }, achievementgrid: { - args: ["target:player?"], - description: "Shows all achievements in a 2D scrolling menu.", - perm: commands_1.Perm.none, - handler: function (_a) { - return __awaiter(this, arguments, void 0, function (_b) { - var visibleAchievements, options, numberAchievements, totalAchievements, x, y, a; - var _c; - var sender = _b.sender, _d = _b.args.target, target = _d === void 0 ? sender : _d, f = _b.f; - return __generator(this, function (_e) { - switch (_e.label) { - case 0: - visibleAchievements = achievements_1.Achievement.all.filter(function (a) { return !a.hidden || a.has(target); }); - options = (0, funcs_1.to2DArray)(visibleAchievements, 7).map(function (row) { return row.map(function (a) { return ({ - data: a, - text: a.has(target) ? a.icon : "[gray]".concat(Strings.stripColors(a.icon)), - }); }); }); - numberAchievements = achievements_1.Achievement.all.filter(function (a) { return a.has(target); }).length; - totalAchievements = visibleAchievements.length; - x = 0, y = 0; - a = null; - _e.label = 1; - case 1: - if (!true) return [3 /*break*/, 3]; - return [4 /*yield*/, menus_1.Menu.scroll2D(sender, "Achievements", a ? config_1.FColor.achievement(templateObject_23 || (templateObject_23 = __makeTemplateObject(["", " ", "\n\n", "\n\nAllowed modes: ", "\nUnlocked: ", "\n", ""], ["\\\n", " ", "\n\n", "\n\nAllowed modes: ", "\nUnlocked: ", "\n", "\\\n"])), a.icon, a.name, a.description + (a.extendedDescription ? ("\n" + "[gray]".concat(a.extendedDescription)) : ""), a.modesText, f.boolGood(a.has(target)), a.hidden ? "This achievement is secret." : "") : - (target == sender ? "You have ".concat(numberAchievements, "/").concat(totalAchievements, " achievements.") - : config_1.FColor.achievement(templateObject_24 || (templateObject_24 = __makeTemplateObject(["Player ", " has ", "/", " achievements."], ["Player ", " has ", "/", " achievements."])), target.prefixedName, numberAchievements, totalAchievements)) - + "\nClick an achievement icon to show more information.", options, { onCancel: "reject", columns: 5, rows: 4, getCenterText: function () { return String.fromCharCode(Iconc.settings); }, x: x, y: y })]; - case 2: - //the loop will be aborted if the menu is cancelled (promise will reject) - _c = __read.apply(void 0, [_e.sent(), 3]), a = _c[0], x = _c[1], y = _c[2]; - if (a == achievements_1.Achievements.click_me && target == sender) - a.grantTo(sender); - return [3 /*break*/, 1]; - case 3: return [2 /*return*/]; - } - }); - }); - } - } })); -var templateObject_1, templateObject_2, templateObject_3, templateObject_4, templateObject_5, templateObject_6, templateObject_7, templateObject_8, templateObject_9, templateObject_10, templateObject_11, templateObject_12, templateObject_13, templateObject_14, templateObject_15, templateObject_16, templateObject_17, templateObject_18, templateObject_19, templateObject_20, templateObject_21, templateObject_22, templateObject_23, templateObject_24; +"use strict"; +/* +Copyright © BalaM314, 2026. All Rights Reserved. +This file contains most in-game chat commands that can be run by untrusted players. +*/ +var __makeTemplateObject = (this && this.__makeTemplateObject) || function (cooked, raw) { + if (Object.defineProperty) { Object.defineProperty(cooked, "raw", { value: raw }); } else { cooked.raw = raw; } + return cooked; +}; +var __assign = (this && this.__assign) || function () { + __assign = Object.assign || function(t) { + for (var s, i = 1, n = arguments.length; i < n; i++) { + s = arguments[i]; + for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p)) + t[p] = s[p]; + } + return t; + }; + return __assign.apply(this, arguments); +}; +var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + var desc = Object.getOwnPropertyDescriptor(m, k); + if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { + desc = { enumerable: true, get: function() { return m[k]; } }; + } + Object.defineProperty(o, k2, desc); +}) : (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + o[k2] = m[k]; +})); +var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { + Object.defineProperty(o, "default", { enumerable: true, value: v }); +}) : function(o, v) { + o["default"] = v; +}); +var __importStar = (this && this.__importStar) || (function () { + var ownKeys = function(o) { + ownKeys = Object.getOwnPropertyNames || function (o) { + var ar = []; + for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k; + return ar; + }; + return ownKeys(o); + }; + return function (mod) { + if (mod && mod.__esModule) return mod; + var result = {}; + if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]); + __setModuleDefault(result, mod); + return result; + }; +})(); +var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { + function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } + return new (P || (P = Promise))(function (resolve, reject) { + function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } + function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } + function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } + step((generator = generator.apply(thisArg, _arguments || [])).next()); + }); +}; +var __generator = (this && this.__generator) || function (thisArg, body) { + var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g = Object.create((typeof Iterator === "function" ? Iterator : Object).prototype); + return g.next = verb(0), g["throw"] = verb(1), g["return"] = verb(2), typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g; + function verb(n) { return function (v) { return step([n, v]); }; } + function step(op) { + if (f) throw new TypeError("Generator is already executing."); + while (g && (g = 0, op[0] && (_ = 0)), _) try { + if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t; + if (y = 0, t) op = [op[0] & 2, t.value]; + switch (op[0]) { + case 0: case 1: t = op; break; + case 4: _.label++; return { value: op[1], done: false }; + case 5: _.label++; y = op[1]; op = [0]; continue; + case 7: op = _.ops.pop(); _.trys.pop(); continue; + default: + if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; } + if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; } + if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; } + if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; } + if (t[2]) _.ops.pop(); + _.trys.pop(); continue; + } + op = body.call(thisArg, _); + } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; } + if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true }; + } +}; +var __read = (this && this.__read) || function (o, n) { + var m = typeof Symbol === "function" && o[Symbol.iterator]; + if (!m) return o; + var i = m.call(o), r, ar = [], e; + try { + while ((n === void 0 || n-- > 0) && !(r = i.next()).done) ar.push(r.value); + } + catch (error) { e = { error: error }; } + finally { + try { + if (r && !r.done && (m = i["return"])) m.call(i); + } + finally { if (e) throw e.error; } + } + return ar; +}; +var __spreadArray = (this && this.__spreadArray) || function (to, from, pack) { + if (pack || arguments.length === 2) for (var i = 0, l = from.length, ar; i < l; i++) { + if (ar || !(i in from)) { + if (!ar) ar = Array.prototype.slice.call(from, 0, i); + ar[i] = from[i]; + } + } + return to.concat(ar || Array.prototype.slice.call(from)); +}; +var __values = (this && this.__values) || function(o) { + var s = typeof Symbol === "function" && Symbol.iterator, m = s && o[s], i = 0; + if (m) return m.call(o); + if (o && typeof o.length === "number") return { + next: function () { + if (o && i >= o.length) o = void 0; + return { value: o && o[i++], done: !o }; + } + }; + throw new TypeError(s ? "Object is not iterable." : "Symbol.iterator is not defined."); +}; +var _a; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.commands = void 0; +var achievements_1 = require("/achievements"); +var api = __importStar(require("/api")); +var config_1 = require("/config"); +var commands_1 = require("/frameworks/commands"); +var menus_1 = require("/frameworks/menus"); +var funcs_1 = require("/funcs"); +var globals_1 = require("/globals"); +var maps_1 = require("/maps"); +var players_1 = require("/players"); +var ranks_1 = require("/ranks"); +var utils_1 = require("/utils"); +var votes_1 = require("/votes"); +exports.commands = (0, commands_1.commandList)(__assign(__assign({ about: { + args: [], + description: 'Prints information about the plugin.', + perm: commands_1.Perm.none, + handler: function (_a) { + var _b, _c; + var output = _a.output; + output("[accent][cyan]fish-commands[] is the monolithic plugin used for the Fish servers' features.\n[accent]==========\n[accent]Source code available at: [cyan]https://github.com/Fish-Community/fish-commands/\n[accent]Current plugin version: [cyan]".concat((_c = (_b = globals_1.fishPlugin.version) === null || _b === void 0 ? void 0 : _b.slice(0, 8)) !== null && _c !== void 0 ? _c : "[scarlet]null[]", "[]")); + } + }, unpause: (0, commands_1.command)({ + args: [], + description: 'Unpauses the game.', + perm: commands_1.Perm.trusted, + requirements: [commands_1.Req.mode('pvp')], + init: function () { + var data = { unpaused: false }; + Events.on(EventType.PlayEvent, function () { + if (data.unpaused) { + data.unpaused = false; + Vars.state.rules.pvpAutoPause = true; + } + }); + return data; + }, + handler: function (_a) { + var data = _a.data, outputSuccess = _a.outputSuccess; + Vars.state.rules.pvpAutoPause = false; + data.unpaused = true; + Core.app.post(function () { return Vars.state.set(GameState.State.playing); }); + outputSuccess("Unpaused."); + }, + }), tp: { + args: ['player:player'], + description: 'Teleport to another player.', + perm: commands_1.Perm.play, + requirements: [commands_1.Req.modeNot("pvp")], + handler: function (_a) { + var _b, _c, _d; + var args = _a.args, sender = _a.sender; + if (!((_b = sender.unit()) === null || _b === void 0 ? void 0 : _b.spawnedByCore)) + (0, commands_1.fail)("Can only teleport while in a core unit."); + if (sender.team() !== args.player.team()) + (0, commands_1.fail)("Cannot teleport to players on another team."); + if ((_d = (_c = sender.unit()).hasPayload) === null || _d === void 0 ? void 0 : _d.call(_c)) + (0, commands_1.fail)("Cannot teleport to players while holding a payload."); + (0, utils_1.teleportPlayer)(sender.player, args.player.player); + }, + }, clean: (0, commands_1.command)({ + args: [], + description: 'Removes all boulders from the map.', + perm: commands_1.Perm.play, + requirements: [], + data: { lastRanMapStartTime: (_a = maps_1.PartialMapRun.current) === null || _a === void 0 ? void 0 : _a.startTime }, + handler: function (_a) { + return __awaiter(this, arguments, void 0, function (_b) { + var array, removed, i, t; + var sender = _b.sender, outputSuccess = _b.outputSuccess, data = _b.data; + return __generator(this, function (_c) { + switch (_c.label) { + case 0: + if (!maps_1.PartialMapRun.current) + (0, commands_1.fail)("This game is already over."); + if (data.lastRanMapStartTime == maps_1.PartialMapRun.current.startTime) + (0, commands_1.fail)("This command was already run on this map."); + data.lastRanMapStartTime = maps_1.PartialMapRun.current.startTime; + Timer.schedule(function () { return Call.sound(sender.con, Sounds.rockBreak, 1, 1, 0); }, 0, 0.05, 10); + array = ArcReflect.get(Vars.world.tiles, "array"); + removed = 0; + i = 0; + _c.label = 1; + case 1: + if (!(i < array.length)) return [3 /*break*/, 4]; + t = array[i]; + if (!(t.breakable() && t.block() instanceof Prop)) return [3 /*break*/, 3]; + t.removeNet(); + removed++; + if (!(removed % 500 == 0)) return [3 /*break*/, 3]; + return [4 /*yield*/, (0, funcs_1.delay)(100)]; + case 2: + _c.sent(); + _c.label = 3; + case 3: + i++; + return [3 /*break*/, 1]; + case 4: + outputSuccess("Cleared the map of boulders."); + return [2 /*return*/]; + } + }); + }); + } + }), die: { + args: [], + description: 'Kills your unit.', + perm: commands_1.Perm.mod.exceptModes({ + sandbox: commands_1.Perm.play + }, "You do not have permission to die."), + handler: function (_a) { + var _b; + var sender = _a.sender; + (_b = sender.unit()) === null || _b === void 0 ? void 0 : _b.kill(); + }, + }, discord: { + args: [], + description: 'Takes you to our discord.', + perm: commands_1.Perm.none, + handler: function (_a) { + var sender = _a.sender; + Call.openURI(sender.con, config_1.text.discordURL); + }, + }, tilelog: (0, commands_1.command)({ + args: ['persist:boolean?', 'showUUID:boolean?'], + description: 'Checks the history of a tile.', + perm: commands_1.Perm.none, + data: { showUUID: true }, + handler: function (_a) { + var args = _a.args, output = _a.output, outputSuccess = _a.outputSuccess, currentTapMode = _a.currentTapMode, handleTaps = _a.handleTaps, sender = _a.sender, data = _a.data; + var changed = args.showUUID !== undefined && args.showUUID != data.showUUID; + if (args.showUUID !== undefined) { + if (!sender.hasPerm("viewUUIDs")) + (0, commands_1.fail)("You do not have permission to show UUIDs."); + data.showUUID = args.showUUID; + } + if (args.persist && currentTapMode !== "on") { + outputSuccess("Tilelog mode enabled. Click tiles to check their recent history. Run /tilelog to disable."); + handleTaps("on"); + } + else if (args.persist && changed) { + outputSuccess("".concat(data.showUUID ? "Now showing UUIDs." : "No longer showing UUIDs.", " Click tiles to check their recent history. Run /tilelog to disable.")); + handleTaps("on"); + } + else if (currentTapMode == "off" || changed) { + handleTaps("once"); + output("Click on a tile to check its recent history..."); + } + else { + handleTaps("off"); + outputSuccess("Tilelog disabled."); + } + }, + tapped: function (_a) { + var _b; + var tile = _a.tile, x = _a.x, y = _a.y, output = _a.output, sender = _a.sender, admins = _a.admins, data = _a.data; + var historyData = (_b = globals_1.tileHistory["".concat(x, ",").concat(y)]) !== null && _b !== void 0 ? _b : (0, commands_1.fail)("There is no recorded history for the selected tile (".concat(tile.x, ", ").concat(tile.y, ").")); + var history = funcs_1.StringIO.read(historyData, function (str) { return str.readArray(function (d) { return ({ + action: d.readString(2), + uuid: d.readString(3), + time: d.readNumber(16), + type: d.readString(2), + }); }, 1); }); + output("[yellow]Tile history for tile (".concat(tile.x, ", ").concat(tile.y, "):\n") + history.map(function (e) { + var _a, _b; + return globals_1.uuidPattern.test(e.uuid) + ? (sender.hasPerm("viewUUIDs") && data.showUUID + ? "[yellow]".concat((_a = admins.getInfoOptional(e.uuid)) === null || _a === void 0 ? void 0 : _a.plainLastName(), "[lightgray](").concat(e.uuid, ")[yellow] ").concat(e.action, " a [cyan]").concat(e.type, "[] ").concat((0, utils_1.formatTimeRelative)(e.time)) + : "[yellow]".concat((_b = admins.getInfoOptional(e.uuid)) === null || _b === void 0 ? void 0 : _b.plainLastName(), " ").concat(e.action, " a [cyan]").concat(e.type, "[] ").concat((0, utils_1.formatTimeRelative)(e.time))) + : "[yellow]".concat(e.uuid, "[yellow] ").concat(e.action, " a [cyan]").concat(e.type, "[] ").concat((0, utils_1.formatTimeRelative)(e.time)); + }).join('\n')); + } + }), aoelog: (0, commands_1.command)(function () { + var allowedActions = [ + "built", "broke", "rotated", "killed", "configured", "pay-dropped", "picked up", "controlled" + ]; + var cachedPointMap = Object.create(null); + return { + args: ['persist:boolean?', 'amount:number?', 'action:string?'], + description: 'Checks the history of all tiles in the selected region. Can be filtered by action.', + perm: commands_1.Perm.none, + handler: function (_a) { + var args = _a.args, sender = _a.sender, outputSuccess = _a.outputSuccess, currentTapMode = _a.currentTapMode, handleTaps = _a.handleTaps; + if (currentTapMode === "off" || args.action || args.amount) { + if (args.action && !allowedActions.includes(args.action)) + (0, commands_1.fail)("Invalid action. Allowed actions: ".concat(allowedActions.join(", "))); + if (args.amount && args.amount > 100) + (0, commands_1.fail)("Limit cannot be greater than 100."); + cachedPointMap[sender.uuid] = undefined; + handleTaps("on"); + outputSuccess("Aoelog mode enabled. To see the recent history of all tiles in a rectangular region, tap opposite corners of the rectangle. Run /aoelog with no arguments to disable."); + } + else { + handleTaps("off"); + outputSuccess("Aoelog disabled."); + } + }, + tapped: function (_a) { + var x = _a.x, y = _a.y, output = _a.output, outputFail = _a.outputFail, sender = _a.sender, admins = _a.admins, handleTaps = _a.handleTaps, args = _a.args; + function handleArea(p1, p2) { + var minX = Math.min(p1[0], p2[0]); + var maxX = Math.max(p1[0], p2[0]); + var minY = Math.min(p1[1], p2[1]); + var maxY = Math.max(p1[1], p2[1]); + var limitTiles = 0; + var amount = args.amount != null ? Math.floor(Math.abs(args.amount)) : 10; + outer: for (var i = minX; i <= maxX; i++) { + for (var j = minY; j <= maxY; j++) { + var tileData = globals_1.tileHistory["".concat(i, ",").concat(j)]; + if (!tileData) + continue; + var history = funcs_1.StringIO.read(globals_1.tileHistory["".concat(i, ",").concat(j)], function (str) { return str.readArray(function (d) { + var _a, _b, _c; + return ({ + action: (_a = d.readString(2)) !== null && _a !== void 0 ? _a : "??", + uuid: (_b = d.readString(3)) !== null && _b !== void 0 ? _b : "??", + time: d.readNumber(16), + type: (_c = d.readString(2)) !== null && _c !== void 0 ? _c : "??", + }); + }, 1); }); + if (args.action) + history = history.filter(function (e) { return e.action === args.action; }); + if (history.length == 0) + continue; + output("[yellow]Tile history for tile (".concat(i, ", ").concat(j, "):\n") + history.map(function (e) { + var _a, _b; + if (globals_1.uuidPattern.test(e.uuid)) { + if (sender.hasPerm("viewUUIDs")) + return "[yellow]".concat((_a = admins.getInfoOptional(e.uuid)) === null || _a === void 0 ? void 0 : _a.plainLastName(), "[lightgray](").concat(e.uuid, ")[yellow] ").concat(e.action, " a [cyan]").concat(e.type, "[] ").concat((0, utils_1.formatTimeRelative)(e.time)); + else + return "[yellow]".concat((_b = admins.getInfoOptional(e.uuid)) === null || _b === void 0 ? void 0 : _b.plainLastName(), " ").concat(e.action, " a [cyan]").concat(e.type, "[] ").concat((0, utils_1.formatTimeRelative)(e.time)); + } + else + return "[yellow]".concat(e.uuid, "[yellow] ").concat(e.action, " a [cyan]").concat(e.type, "[] ").concat((0, utils_1.formatTimeRelative)(e.time)); + }).join('\n')); + limitTiles++; + if (limitTiles === amount) + break outer; + } + } + if (limitTiles == 0) { + if (args.action) + outputFail("There is no recorded history for the selected region matching the provided filters."); + else + outputFail("There is no recorded history for the selected region."); + } + if (limitTiles == amount) + output("Displaying first ".concat(limitTiles, " entries. To show other entries, increase the limit or select a smaller area.")); + } + var p1 = cachedPointMap[sender.uuid]; + if (!p1) { + cachedPointMap[sender.uuid] = [x, y]; + output("1st point set at (".concat(x, ",").concat(y, ")")); + } + else { + var p2 = [x, y]; + output("2nd point set at (".concat(x, ", ").concat(y, ")")); + var width = Math.abs(p1[0] - p2[0]); + var height = Math.abs(p1[1] - p2[1]); + if (width > 50 || height > 50) + (0, commands_1.fail)("Selection too large: width/height cannot be more than 50."); + handleArea(p1, p2); + cachedPointMap[sender.uuid] = undefined; + if (!args.persist) + handleTaps("off"); + } + }, + }; + }), afk: { + args: [], + description: 'Toggles your afk status.', + perm: commands_1.Perm.none, + handler: function (_a) { + var sender = _a.sender, outputSuccess = _a.outputSuccess; + sender.manualAfk = !sender.manualAfk; + sender.updateName(); + if (sender.manualAfk) + outputSuccess("You are now marked as AFK."); + else + outputSuccess("You are no longer marked as AFK."); + }, + }, vanish: { + args: ['target:player?'], + description: "Toggles visibility of your rank and flags.", + perm: commands_1.Perm.vanish, + handler: function (_a) { + var sender = _a.sender, _b = _a.args.target, target = _b === void 0 ? sender : _b, outputSuccess = _a.outputSuccess; + if (sender.stelled()) + (0, commands_1.fail)("Marked players may not hide flags."); + if (sender.muted) + (0, commands_1.fail)("Muted players may not hide flags."); + if (sender != target && target.hasPerm("blockTrolling")) + (0, commands_1.fail)("Target is insufficentlly trollable."); + if (sender != target && !sender.ranksAtLeast("mod")) + (0, commands_1.fail)("You do not have permission to vanish other players."); + target.showRankPrefix = !target.showRankPrefix; + outputSuccess("".concat(target == sender ? "Your" : "".concat(target.name, "'s"), " rank prefix is now ").concat(target.showRankPrefix ? "visible" : "hidden", ".")); + }, + }, tileid: { + args: [], + description: 'Checks id of a tile.', + perm: commands_1.Perm.none, + handler: function (_a) { + var output = _a.output, handleTaps = _a.handleTaps; + handleTaps("once"); + output("Click a tile to see its id..."); + }, + tapped: function (_a) { + var output = _a.output, f = _a.f, tile = _a.tile; + output(f(templateObject_1 || (templateObject_1 = __makeTemplateObject(["ID is ", ""], ["ID is ", ""])), tile.block().id)); + } + } }, Object.fromEntries(config_1.FishServer.all.map(function (server) { return [ + server.name, + { + args: [], + description: "Switches to the ".concat(server.name, " server."), + perm: server.requiredPerm ? commands_1.Perm.getByName(server.requiredPerm) : commands_1.Perm.none, + isHidden: true, + handler: function (_a) { + var sender = _a.sender, lastUsedSuccessfullySender = _a.lastUsedSuccessfullySender; + if (Date.now() - lastUsedSuccessfullySender > funcs_1.Duration.minutes(1)) + players_1.FishPlayer.messageAllWithPerm(server.requiredPerm, "".concat(sender.name, "[magenta] has gone to the ").concat(server.name, " server. Use [cyan]/").concat(server.name, " [magenta]to join them!")); + Call.connect(sender.con, server.ip, server.port); + }, + }, +]; }))), { switch: { + args: ["server:string", "target:player?"], + description: "Switches to another server.", + perm: commands_1.Perm.play, + handler: function (_a) { + var _b, _c; + var args = _a.args, sender = _a.sender, f = _a.f, lastUsedSuccessfullySender = _a.lastUsedSuccessfullySender; + if (args.target != null && args.target != sender && !sender.canModerate(args.target, true, "admin", true)) + (0, commands_1.fail)(f(templateObject_2 || (templateObject_2 = __makeTemplateObject(["You do not have permission to switch player ", "."], ["You do not have permission to switch player ", "."])), args.target)); + var target = (_b = args.target) !== null && _b !== void 0 ? _b : sender; + if (globals_1.ipPortPattern.test(args.server) && sender.hasPerm("admin")) { + //direct connect + Call.connect.apply(Call, __spreadArray([target.con], __read(args.server.split(":")), false)); + } + else { + var unknownServerMessage = "Unknown server ".concat(args.server, ". Valid options: ").concat(config_1.FishServer.all.filter(function (s) { return !s.requiredPerm || sender.hasPerm(s.requiredPerm); }).map(function (s) { return s.name; }).join(", ")); + var server = (_c = config_1.FishServer.byName(args.server)) !== null && _c !== void 0 ? _c : (0, commands_1.fail)(unknownServerMessage); + //Pretend the server doesn't exist + if (server.requiredPerm && !sender.hasPerm(server.requiredPerm)) + (0, commands_1.fail)(unknownServerMessage); + if (target == sender && Date.now() - lastUsedSuccessfullySender > funcs_1.Duration.minutes(1)) + players_1.FishPlayer.messageAllWithPerm(server.requiredPerm, "".concat(sender.name, "[magenta] has gone to the ").concat(server.name, " server. Use [cyan]/").concat(server.name, " [magenta]to join them!")); + Call.connect(target.con, server.ip, server.port); + } + } + }, s: { + args: ['message:string'], + description: "Sends a message to staff only.", + perm: commands_1.Perm.chat, + handler: function (_a) { + var sender = _a.sender, args = _a.args, outputSuccess = _a.outputSuccess, outputFail = _a.outputFail, lastUsedSender = _a.lastUsedSender; + if (!sender.hasPerm("mod")) { + if (Date.now() - lastUsedSender < 4000) + (0, commands_1.fail)("This command was used recently and is on cooldown. [orange]Misuse of this command may result in a mute."); + } + api.sendStaffMessage(args.message, sender.name, sender.hasPerm("mod"), function (sent) { + if (!sender.hasPerm("mod")) { + if (sent) { + outputSuccess("Message sent to [orange]all online staff."); + } + else { + var wasReceived = players_1.FishPlayer.messageStaff(sender.prefixedName, args.message); + if (wasReceived) + outputSuccess("Message sent to staff."); + else + outputFail("No staff were online to receive your message."); + } + } + }); + }, + }, + /** + * This command is mostly for mobile (or players without foos). + * + * Since the player's unit follows the camera and we are moving the + * camera, we need to keep setting the players real position to the + * spot the command was made. This is pretty buggy but otherwise the + * player will be up the target player's butt + */ + watch: (0, commands_1.command)({ + args: ['player:player?'], + description: "Watch/unwatch a player.", + perm: commands_1.Perm.none, + data: new Set, + handler: function (_a) { + var _b; + var args = _a.args, data = _a.data, sender = _a.sender, outputSuccess = _a.outputSuccess, outputFail = _a.outputFail; + if (data.has(sender.uuid)) { + outputSuccess("No longer watching a player."); + data.delete(sender.uuid); + } + else if (args.player) { + data.add(sender.uuid); + var senderUnit_1 = (_b = sender.unit()) !== null && _b !== void 0 ? _b : (0, commands_1.fail)("You do not have a unit."); + var stayX_1 = senderUnit_1.x; + var stayY_1 = senderUnit_1.y; + var target_1 = args.player.player; + (function watch() { + var _a, _b; + var unit = target_1.unit(); + if (data.has(sender.uuid) && unit) { + // Self.X+(172.5-Self.X)/10 + Call.setCameraPosition(sender.con, unit.x, unit.y); + if (senderUnit_1) + (_b = (_a = sender.unit()) === null || _a === void 0 ? void 0 : _a.set) === null || _b === void 0 ? void 0 : _b.call(_a, stayX_1, stayY_1); + Timer.schedule(function () { return watch(); }, 0.1, 0.1, 0); + } + else { + Call.setCameraPosition(sender.con, stayX_1, stayY_1); + } + })(); + } + else { + outputFail("No player to unwatch."); + } + }, + }), spectate: (0, commands_1.command)(function () { + //TODO revise code + /** Mapping between player and original team */ + var spectators = new Map(); + function spectate(target) { + spectators.set(target, target.team()); + target.forceRespawn(); + target.setTeam(Team.derelict); + target.forceRespawn(); + } + function resume(target) { + if (spectators.get(target) == null) + return; // this state is possible for a person who left not in spectate + target.setTeam(spectators.get(target)); + spectators.delete(target); + target.forceRespawn(); + } + Events.on(EventType.GameOverEvent, function () { return spectators.clear(); }); + Events.on(EventType.PlayerLeave, function (_a) { + var player = _a.player; + return resume(players_1.FishPlayer.get(player)); + }); + return { + args: ["target:player?"], + description: "Toggles spectator mode in PVP games.", + perm: commands_1.Perm.play, + requirements: [commands_1.Req.gameRunning], + handler: function (_a) { + var sender = _a.sender, _b = _a.args.target, target = _b === void 0 ? sender : _b, outputSuccess = _a.outputSuccess, f = _a.f; + if (!config_1.Gamemode.pvp() && !sender.hasPerm("mod")) + (0, commands_1.fail)("You do not have permission to spectate on a non-pvp server."); + if (target !== sender && target.hasPerm("blockTrolling")) + (0, commands_1.fail)("Target player is insufficiently trollable."); + if (target !== sender && !sender.ranksAtLeast("admin")) + (0, commands_1.fail)("You do not have permission to force other players to spectate."); + if (spectators.has(target)) { + resume(target); + outputSuccess(target == sender + ? f(templateObject_3 || (templateObject_3 = __makeTemplateObject(["Rejoining game as team ", "."], ["Rejoining game as team ", "."])), target.team()) : f(templateObject_4 || (templateObject_4 = __makeTemplateObject(["Forced ", " out of spectator mode."], ["Forced ", " out of spectator mode."])), target)); + } + else { + spectate(target); + outputSuccess(target == sender + ? f(templateObject_5 || (templateObject_5 = __makeTemplateObject(["Now spectating. Run /spectate again to resume gameplay."], ["Now spectating. Run /spectate again to resume gameplay."]))) : f(templateObject_6 || (templateObject_6 = __makeTemplateObject(["Forced ", " into spectator mode."], ["Forced ", " into spectator mode."])), target)); + } + } + }; + }), help: { + args: ['name:string?'], + description: 'Displays a list of all commands.', + perm: commands_1.Perm.none, + handler: function (_a) { + var _b; + var args = _a.args, output = _a.output, sender = _a.sender, allCommands = _a.allCommands; + var formatCommand = function (name, color) { + return new funcs_1.StringBuilder() + .add("".concat(color, "/").concat(name)) + .chunk("[white]".concat(allCommands[name].args.map(commands_1.formatArg).join(' '))) + .chunk("[lightgray]- ".concat(allCommands[name].description)).str; + }; + var formatList = function (commandList, color) { return commandList.map(function (c) { return formatCommand(c, color); }).join('\n'); }; + if (args.name && isNaN(parseInt(args.name)) && !['mod', 'admin', 'member'].includes(args.name)) { + //name is not a number or a category, therefore it is probably a command name + if (args.name in allCommands && (!allCommands[args.name].isHidden || allCommands[args.name].perm.check(sender))) { + if (args.name == "help") + achievements_1.Achievements.help_help.grantTo(sender, false); + output("Help for command ".concat(args.name, ":\n\t").concat(allCommands[args.name].description, "\n\tUsage: [sky]/").concat(args.name, " [white]").concat(allCommands[args.name].args.map(commands_1.formatArg).join(' '), "\n\tPermission required: ").concat(allCommands[args.name].perm.name)); + } + else + (0, commands_1.fail)("Command \"".concat(args.name, "\" does not exist.")); + } + else { + var commands_2 = { + player: [], + mod: [], + admin: [], + member: [], + }; + //TODO change this to category, not perm + Object.entries(allCommands).forEach(function (_a) { + var _b = __read(_a, 2), name = _b[0], data = _b[1]; + return (data.perm === commands_1.Perm.admin ? commands_2.admin : data.perm === commands_1.Perm.mod ? commands_2.mod : data.perm === commands_1.Perm.member ? commands_2.member : commands_2.player).push(name); + }); + var chunkedPlayerCommands = (0, funcs_1.to2DArray)(commands_2.player, 15); + switch (args.name) { + case 'admin': + output("".concat(commands_1.Perm.admin.color, "-- Admin commands --\n") + formatList(commands_2.admin, commands_1.Perm.admin.color)); + break; + case 'mod': + output("".concat(commands_1.Perm.mod.color, "-- Mod commands --\n") + formatList(commands_2.mod, commands_1.Perm.mod.color)); + break; + case 'member': + output("".concat(commands_1.Perm.member.color, "-- Member commands --\n") + formatList(commands_2.member, commands_1.Perm.member.color)); + break; + default: { + var pageNumber = args.name != undefined ? parseInt(args.name) : 1; + var page = (_b = chunkedPlayerCommands[pageNumber - 1]) !== null && _b !== void 0 ? _b : (0, commands_1.fail)("\"".concat(args.name, "\" is an invalid page number.")); + output("[sky]-- Commands page [lightgrey]".concat(pageNumber, "/").concat(chunkedPlayerCommands.length, "[sky] --\n") + formatList(page, '[sky]')); + } + } + } + }, + }, msg: { + args: ['player:player', 'message:string'], + description: 'Send a message to only one player.', + perm: commands_1.Perm.chat, + handler: function (_a) { + var args = _a.args, sender = _a.sender, output = _a.output, f = _a.f; + globals_1.recentWhispers[args.player.uuid] = sender.uuid; + args.player.sendMessage("".concat(sender.prefixedName, "[lightgray] whispered:[#BBBBBB] ").concat(args.message)); + output(f(templateObject_7 || (templateObject_7 = __makeTemplateObject(["[lightgray]Whispered to ", "[lightgray]:[#BBBBBB] ", ""], ["[lightgray]Whispered to ", "[lightgray]:[#BBBBBB] ", ""])), args.player, args.message)); + }, + }, r: { + args: ['message:string'], + description: 'Reply to the most recent message.', + perm: commands_1.Perm.chat, + handler: function (_a) { + var _b; + var args = _a.args, sender = _a.sender, output = _a.output, f = _a.f; + var recipient = players_1.FishPlayer.getById((_b = globals_1.recentWhispers[sender.uuid]) !== null && _b !== void 0 ? _b : (0, commands_1.fail)("It doesn't look like someone has messaged you recently. Try whispering to them with [white]\"/msg \"")); + if (!(recipient === null || recipient === void 0 ? void 0 : recipient.connected())) + (0, commands_1.fail)("The person who last messaged you doesn't seem to exist anymore. Try whispering to someone with [white]\"/msg \""); + globals_1.recentWhispers[globals_1.recentWhispers[sender.uuid]] = sender.uuid; + recipient.sendMessage("".concat(sender.name, "[lightgray] whispered:[#BBBBBB] ").concat(args.message)); + output(f(templateObject_8 || (templateObject_8 = __makeTemplateObject(["[lightgray]Whispered to ", "[lightgray]:[#BBBBBB] ", ""], ["[lightgray]Whispered to ", "[lightgray]:[#BBBBBB] ", ""])), recipient, args.message)); + }, + }, trail: { + args: ['type:string?', 'color:string?'], + description: 'Use command to see options and toggle trail on/off.', + perm: commands_1.Perm.none, + handler: function (_a) { + var args = _a.args, sender = _a.sender, output = _a.output, outputFail = _a.outputFail, outputSuccess = _a.outputSuccess; + //overload 1: type not specified + if (!args.type) { + if (sender.trail != null) { + sender.trail = null; + outputSuccess("Trail turned off."); + } + else { + output("Available types:[yellow]\n1 - fluxVapor (flowing smoke, long lasting)\n2 - overclocked (diamonds)\n3 - overdriven (squares)\n4 - shieldBreak (smol)\n5 - upgradeCoreBloom (square, long lasting, only orange)\n6 - electrified (tiny spiratic diamonds, but only green)\n7 - unitDust (same as above but round, and can change colors)\n[white]Usage: [orange]/trail [lightgrey] [color/#hex/r,g,b]"); + } + return; + } + //overload 2: type specified + var trailTypes = { + "1": 'fluxVapor', + "2": 'overclocked', + "3": 'overdriven', + "4": 'shieldBreak', + "5": 'upgradeCoreBloom', + "6": 'electrified', + "7": 'unitDust', + }; + var selectedType = trailTypes[args.type]; + if (!selectedType) { + if (Object.values(trailTypes).includes(args.type)) + (0, commands_1.fail)("Please use the numeric id to refer to a trail type."); + else + (0, commands_1.fail)("\"".concat(args.type, "\" is not an available type.")); + } + var color = args.color ? (0, utils_1.getColor)(args.color) : Color.white; + if (color instanceof Color) { + sender.trail = { + type: selectedType, + color: color, + }; + } + else { + outputFail("[scarlet]Sorry, \"".concat(args.color, "\" is not a valid color.\n[yellow]Color can be in the following formats:\n[pink]pink [white]| [gray]#696969 [white]| 255,0,0.")); + } + }, + }, ohno: (0, commands_1.command)({ + args: [], + description: 'Spawns an ohno.', + perm: commands_1.Perm.play, + init: function () { + var Ohnos = { + enabled: true, + ohnos: new Array(), + makeOhno: function (team, x, y) { + var ohno = UnitTypes.atrax.create(team); + ohno.set(x, y); + ohno.type = UnitTypes.alpha; + ohno.apply(StatusEffects.disarmed, Number.MAX_SAFE_INTEGER); + ohno.resetController(); //does this work? + ohno.add(); + this.ohnos.push(ohno); + return ohno; + }, + updateLength: function () { + this.ohnos = this.ohnos.filter(function (o) { return o && o.isAdded() && !o.dead; }); + }, + checkAchievement: function () { + var e_1, _a; + try { + for (var _b = __values(this.ohnos), _c = _b.next(); !_c.done; _c = _b.next()) { + var ohno = _c.value; + var player = ohno.getPlayer(); + if (player) + achievements_1.Achievements.ohno.grantTo(players_1.FishPlayer.get(player), false); + } + } + catch (e_1_1) { e_1 = { error: e_1_1 }; } + finally { + try { + if (_c && !_c.done && (_a = _b.return)) _a.call(_b); + } + finally { if (e_1) throw e_1.error; } + } + }, + killAll: function () { + this.ohnos.forEach(function (ohno) { var _a; return (_a = ohno === null || ohno === void 0 ? void 0 : ohno.kill) === null || _a === void 0 ? void 0 : _a.call(ohno); }); + this.ohnos = []; + }, + amount: function () { + return this.ohnos.length; + }, + }; + Events.on(EventType.GameOverEvent, function (e) { + Ohnos.killAll(); + }); + Timer.schedule(function () { return Ohnos.checkAchievement(); }, 1, 2); + return Ohnos; + }, + requirements: [ + commands_1.Req.gameRunning, commands_1.Req.modeNot("pvp"), + commands_1.Req.unitExists("You cannot spawn ohnos while dead.") + ], + handler: function (_a) { + var sender = _a.sender, Ohnos = _a.data; + if (!Ohnos.enabled) + (0, commands_1.fail)("Ohnos have been temporarily disabled."); + Ohnos.updateLength(); + if (Ohnos.ohnos.length >= (Groups.player.size() + 1) || + sender.team().data().countType(UnitTypes.alpha) >= Units.getCap(sender.team())) + (0, commands_1.fail)("Sorry, the max number of ohno units has been reached."); + if ((0, utils_1.nearbyEnemyTile)((sender.unit()), 6) != null) + (0, commands_1.fail)("Too close to an enemy building!"); + if (!UnitTypes.alpha.supportsEnv(Vars.state.rules.env)) + (0, commands_1.fail)("Ohnos cannot survive in this map."); + Ohnos.makeOhno(sender.team(), sender.player.x, sender.player.y); + }, + }), ranks: { + args: [], + description: 'Displays information about all ranks.', + perm: commands_1.Perm.none, + handler: function (_a) { + var output = _a.output; + output("List of ranks:\n" + + Object.values(ranks_1.Rank.ranks) + .map(function (rank) { return "".concat(rank.prefix, " ").concat(rank.color).concat((0, funcs_1.capitalizeText)(rank.name), "[]: ").concat(rank.color).concat(rank.description, "[]\n"); }) + .join("") + + "List of flags:\n" + + Object.values(ranks_1.RoleFlag.flags) + .map(function (flag) { return "".concat(flag.prefix, " ").concat(flag.color).concat((0, funcs_1.capitalizeText)(flag.name), "[]: ").concat(flag.color).concat(flag.description, "[]\n"); }) + .join("")); + }, + }, rules: { + args: ['player:player?'], + description: 'Displays the server rules.', + perm: commands_1.Perm.none, + handler: function (_a) { + var _b; + var args = _a.args, sender = _a.sender, output = _a.output, outputSuccess = _a.outputSuccess, f = _a.f; + var target = (_b = args.player) !== null && _b !== void 0 ? _b : sender; + if (target !== sender) { + if (!sender.hasPerm("warn")) + (0, commands_1.fail)("You do not have permission to show rules to other players."); + if (!sender.canModerate(target)) + commands_1.Req.cooldown(funcs_1.Duration.minutes(10)); + if (target.hasPerm("blockTrolling")) + (0, commands_1.fail)(f(templateObject_9 || (templateObject_9 = __makeTemplateObject(["Player ", " is insufficiently trollable."], ["Player ", " is insufficiently trollable."])), args.player)); + } + void menus_1.Menu.menu("Rules for [#0000ff]>|||> FISH [white]servers", config_1.rules.join("\n\n"), ["[green]I agree to abide by these rules[]", "No"], target, { onCancel: "null" }).then(function (option) { + if (option == "No") { + target.kick("You must agree to the rules to play on this server. Rejoin to agree to the rules.", 1); + outputSuccess('Player rejected the rules and was kicked.'); + } + else if (option == null) { + output('Player closed the menu.'); + } + else { + outputSuccess('Player acknowledged the rules.'); + } + }); + if (target !== sender) + outputSuccess(f(templateObject_10 || (templateObject_10 = __makeTemplateObject(["Reminded ", " of the rules."], ["Reminded ", " of the rules."])), target)); + }, + }, void: { + args: ["player:player?"], + description: 'Warns other players about power voids.', + perm: commands_1.Perm.play, + requirements: function (_a) { + var args = _a.args; + return [ + commands_1.Req.mode("attack"), + args.player ? commands_1.Req.cooldown(20000) : commands_1.Req.cooldownGlobal(10000) + ]; + }, + handler: function (_a) { + var args = _a.args, sender = _a.sender, outputSuccess = _a.outputSuccess, f = _a.f; + if (args.player) { + if (!sender.hasPerm("trusted")) + (0, commands_1.fail)("You do not have permission to show popups to other players, please run /void with no arguments to send a chat message to everyone."); + if (args.player !== sender && args.player.hasPerm("blockTrolling")) + (0, commands_1.fail)("Target player is insufficiently trollable."); + void menus_1.Menu.menu("\uf83f [scarlet]WARNING[] \uf83f", "[white]Don't break the Power Void (\uF83F), it's a trap!\nPower voids disable anything they are connected to.\nIf you break it, [scarlet]you will get attacked[] by enemy units.\nPlease stop attacking and [lime]build defenses[] first!", ["I understand"], args.player, { onCancel: 'null' }).then(function () { return outputSuccess(f(templateObject_11 || (templateObject_11 = __makeTemplateObject(["Player ", " acknowledged the warning."], ["Player ", " acknowledged the warning."])), args.player)); }); + (0, utils_1.logAction)("showed void warning", sender, args.player); + outputSuccess(f(templateObject_12 || (templateObject_12 = __makeTemplateObject(["Warned ", " about power voids with a popup message."], ["Warned ", " about power voids with a popup message."])), args.player)); + } + else { + Call.sendMessage("[white]Don't break the Power Void (\uF83F), it's a trap!\nPower voids disable anything they are connected to. If you break it, [scarlet]you will get attacked[] by enemy units.\nPlease stop attacking and [lime]build defenses[] first!"); + } + }, + }, team: { + args: ['team:team', 'reason:string?'], + description: 'Changes your team.', + perm: commands_1.Perm.changeTeam, + handler: function (_a) { + var _b; + var sender = _a.sender, _c = _a.args, team = _c.team, reason = _c.reason, outputSuccess = _a.outputSuccess, f = _a.f; + if (config_1.Gamemode.sandbox() && globals_1.fishState.peacefulMode && !sender.hasPerm("admin")) + (0, commands_1.fail)("You do not have permission to change teams because peaceful mode is on."); + if (config_1.Gamemode.sandbox() && team === Vars.state.rules.waveTeam && !sender.hasPerm("admin")) + (0, commands_1.fail)("You do not have permission to change to the wave team on sandbox."); + if (!(config_1.Gamemode.sandbox() || config_1.Gamemode.testsrv()) && !sender.hasPerm("mod") && !reason) + (0, commands_1.fail)("Please specify a reason for changing teams."); + if (!sender.hasPerm("changeTeamExternal")) { + if (team.data().cores.size <= 0) + (0, commands_1.fail)("You do not have permission to change to a team with no cores."); + if (!sender.player.dead() && !((_b = sender.unit()) === null || _b === void 0 ? void 0 : _b.spawnedByCore)) + sender.forceRespawn(); + } + if (!sender.hasPerm("mod")) + sender.changedTeam = true; + sender.setTeam(team); + outputSuccess(f(templateObject_13 || (templateObject_13 = __makeTemplateObject(["Changed your team to ", "."], ["Changed your team to ", "."])), team)); + if (reason && !config_1.Gamemode.sandbox()) + (0, utils_1.logAction)("changed team to ".concat(team.name, " on ").concat((0, funcs_1.escapeTextDiscord)(Vars.state.map.plainName()), " with reason ").concat((0, funcs_1.escapeTextDiscord)(reason)), sender); + }, + }, teamp: { + args: ['team:team', 'target:player'], + description: 'Changes the team of a player.', + perm: commands_1.Perm.changeTeam, + handler: function (_a) { + var _b; + var sender = _a.sender, _c = _a.args, team = _c.team, target = _c.target, outputSuccess = _a.outputSuccess, f = _a.f; + if (!sender.canModerate(target, true, "mod", true)) + (0, commands_1.fail)(f(templateObject_14 || (templateObject_14 = __makeTemplateObject(["You do not have permission to change the team of ", ""], ["You do not have permission to change the team of ", ""])), target)); + if (config_1.Gamemode.sandbox() && globals_1.fishState.peacefulMode && !sender.hasPerm("admin")) + (0, commands_1.fail)("You do not have permission to change teams because peaceful mode is on."); + if (!sender.hasPerm("changeTeamExternal")) { + if (team.data().cores.size <= 0) + (0, commands_1.fail)("You do not have permission to change to a team with no cores."); + if (!target.player.dead() && !((_b = target.unit()) === null || _b === void 0 ? void 0 : _b.spawnedByCore)) + target.forceRespawn(); + } + target.setTeam(team); + outputSuccess(f(templateObject_15 || (templateObject_15 = __makeTemplateObject(["Changed team of player ", " to ", "."], ["Changed team of player ", " to ", "."])), target, team)); + }, + }, rank: { + args: ['player:player'], + description: 'Displays the rank of a player.', + perm: commands_1.Perm.none, + handler: function (_a) { + var args = _a.args, output = _a.output, f = _a.f; + output(f(templateObject_16 || (templateObject_16 = __makeTemplateObject(["Player ", "'s rank is ", "."], ["Player ", "'s rank is ", "."])), args.player, args.player.rank)); + }, + }, forcevnw: { + args: ["force:boolean?"], + description: 'Force skip to the next wave.', + perm: commands_1.Perm.admin, + handler: function (_a) { + var allCommands = _a.allCommands, sender = _a.sender, _b = _a.args.force, force = _b === void 0 ? true : _b; + if (allCommands.vnw.data.manager.session == null) { + if (force == false) + (0, commands_1.fail)("Cannot clear votes for VNW because no vote is currently ongoing."); + (0, utils_1.skipWaves)(1, true); + } + else { + if (force) + Call.sendMessage("VNW: [green]Vote was forced by admin [yellow]".concat(sender.name, "[green], skipping wave.")); + else + Call.sendMessage("VNW: [red]Votes cleared by admin [yellow]".concat(sender.name, "[red].")); + allCommands.vnw.data.manager.forceVote(force); + } + }, + }, vnw: (0, commands_1.command)({ + args: ["waves:number?"], + description: "Vote to start the next wave.", + perm: commands_1.Perm.play, + init: function () { return ({ + manager: new votes_1.VoteManager(funcs_1.Duration.minutes(1.5)) + .on("success", function (t) { return (0, utils_1.skipWaves)(t.session.data, true); }) + .on("vote passed", function () { return Call.sendMessage('VNW: [green]Vote passed, skipping to next wave.'); }) + .on("vote failed", function () { return Call.sendMessage('VNW: [red]Vote failed.'); }) + .on("player vote change", function (t, player) { return Call.sendMessage("VNW: ".concat(player.name, " [white] has voted on skipping [accent]").concat(t.session.data, "[white] wave(s). [green]").concat(t.currentVotes(), "[white] votes, [green]").concat(t.requiredVotes(), "[white] required.")); }) + .on("player vote removed", function (t, player) { return Call.sendMessage("VNW: ".concat(player.name, " [white] has left. [green]").concat(t.currentVotes(), "[white] votes, [green]").concat(t.requiredVotes(), "[white] required.")); }) + }); }, + requirements: [commands_1.Req.cooldown(3000), commands_1.Req.integerRange("waves", 1, 15), commands_1.Req.mode("survival", "testsrv"), commands_1.Req.gameRunning], + handler: function (_a) { + return __awaiter(this, arguments, void 0, function (_b) { + var _c; + var sender = _b.sender, waves = _b.args.waves, manager = _b.data.manager; + return __generator(this, function (_d) { + switch (_d.label) { + case 0: + if (!!manager.session) return [3 /*break*/, 4]; + if (!(waves !== null && waves !== void 0)) return [3 /*break*/, 1]; + _c = waves; + return [3 /*break*/, 3]; + case 1: return [4 /*yield*/, menus_1.Menu.menu("Start a Next Wave Vote", "Select the amount of waves you would like to skip.", [1, 5, 10], sender, { + includeCancel: true, + optionStringifier: function (n) { return "".concat(n, " waves"); } + })]; + case 2: + _c = (waves = _d.sent()); + _d.label = 3; + case 3: + _c; + if (manager.session) { + //Someone else started a vote + if (manager.session.data != waves) + (0, commands_1.fail)("Someone else started a vote with a different number of waves to skip."); + else + manager.vote(sender, sender.voteWeight(), waves); + } + else { + manager.start(sender, sender.voteWeight(), waves); + } + return [3 /*break*/, 5]; + case 4: + manager.vote(sender, sender.voteWeight(), null); + _d.label = 5; + case 5: return [2 /*return*/]; + } + }); + }); + } + }), forcertv: { + args: ["force:boolean?"], + description: 'Force skip to the next map.', + perm: commands_1.Perm.admin, + handler: function (_a) { + var _b = _a.args.force, force = _b === void 0 ? true : _b, sender = _a.sender, allCommands = _a.allCommands; + if (allCommands.rtv.data.manager.session == null) { + if (force == false) + (0, commands_1.fail)("Cannot clear votes for RTV because no vote is currently ongoing."); + allCommands.rtv.data.manager.forceVote(true); + } + else { + if (force) + Call.sendMessage("RTV: [green]Vote was forced by admin [yellow]".concat(sender.name, "[green].")); + else + Call.sendMessage("RTV: [red]Votes cleared by admin [yellow]".concat(sender.name, "[red].")); + allCommands.rtv.data.manager.forceVote(force); + } + } + }, rtv: (0, commands_1.command)({ + args: [], + description: 'Rock the vote to change map.', + perm: commands_1.Perm.play, + init: function () { return ({ + manager: new votes_1.VoteManager(funcs_1.Duration.minutes(1.5), config_1.Gamemode.hexed() ? ["fractionOfVoters", 1] : undefined) //Require unanimity in Hexed, as it is often 1 v everyone + .on("success", function () { return (0, utils_1.neutralGameover)(); }) + .on("vote passed", function () { return Call.sendMessage("RTV: [green]Vote has passed, changing map."); }) + .on("vote failed", function () { return Call.sendMessage("RTV: [red]Vote failed."); }) + .on("player vote change", function (t, player, oldVote, newVote) { return Call.sendMessage("RTV: ".concat(player.name, "[white] ").concat(oldVote == newVote ? "still " : "", "wants to change the map. [green]").concat(t.currentVotes(), "[white] votes, [green]").concat(t.requiredVotes(), "[white] required.")); }) + .on("player vote removed", function (t, player) { return Call.sendMessage("RTV: ".concat(player.name, "[white] has left the game. [green]").concat(t.currentVotes(), "[white] votes, [green]").concat(t.requiredVotes(), "[white] required.")); }) + }); }, + requirements: [commands_1.Req.cooldown(3000), commands_1.Req.gameRunning], + handler: function (_a) { + var sender = _a.sender, manager = _a.data.manager; + manager.vote(sender, 1, 0); //No weighting for RTV except for removing AFK players + } + }), + // votekick: command({ + // args: ["target:player"], + // description: "Starts a vote to kick a player.", + // perm: Perm.play, + // data: new VoteManager( + // Duration.seconds(20), + // ["absolute", 3], + // (fishP, target) => fishP.team() == target.team() || fishP.hasPerm("voteOtherTeams") + // ), + // handler({args, sender, data: votekickmanager}){ + // if(votekickmanager.session) fail(`There is already a votekick in progress.`); + // votekickmanager.start(sender, 1, args.target); + // } + // }), + // vote: { + // args: ["vote:boolean"], + // description: "Use /votekick instead.", + // perm: Perm.play, + // handler({sender, args, allCommands}){ + // const votekickmanager = allCommands.votekick.data; + // votekickmanager.handleVote(sender, args ? 1 : -1); + // } + // }, + forcenextmap: { + args: ["map:mapOrRandom"], + description: 'Override the next map in queue.', + perm: commands_1.Perm.admin.exceptModes({ + testsrv: commands_1.Perm.play + }), + handler: function (_a) { + var allCommands = _a.allCommands, args = _a.args, sender = _a.sender, outputSuccess = _a.outputSuccess, f = _a.f; + Vars.maps.setNextMapOverride(args.map == "random" ? null : args.map); + if (allCommands.nextmap.data.voteEndTime() > -1) { + //Cancel /nextmap vote if it's ongoing + allCommands.nextmap.data.resetVotes(); + Call.sendMessage("[red]Admin ".concat(sender.name, "[red] has cancelled the vote. The next map will be ").concat(args.map == "random" ? "random" : "[yellow]".concat(args.map.name()), ".")); + } + else { + outputSuccess(f(templateObject_17 || (templateObject_17 = __makeTemplateObject(["Forced the next map to be ", "."], ["Forced the next map to be ", "."])), args.map == "random" ? "random" : "\"".concat(args.map.name(), "\" by ").concat(args.map.author()))); + } + }, + }, maps: { + args: [], + description: 'Lists the available maps.', + perm: commands_1.Perm.none, + handler: function (_a) { + var output = _a.output; + output("[yellow]Use [white]/nextmap [lightgray][[map name] [yellow]to vote on a map.\n\n[blue]Available maps:\n_________________________\n".concat(Vars.maps.customMaps().toArray().map(function (map) { + return "[yellow]".concat(map.name()); + }).join("\n"))); + } + }, nextmap: (0, commands_1.command)(function () { + var random = { + name: function () { return "[lightgray]Random"; }, + plainName: function () { return "random"; } + }; + var votes = new Map(); + var lastVoteCount = 0; + var lastVoteTime = 0; + var voteEndTime = -1; + var voteDuration = funcs_1.Duration.minutes(1.5); + var task = null; + var isThrowbackFile = function (file) { return config_1.throwbackMapFileNames.includes(file.name()); }; + var throwbackMaps = function () { return config_1.throwbackMapFileNames + .map(function (f) { return Vars.maps.customMaps().find(function (m) { return m.file.name() == f; }); }) + .filter(function (m) { return m != null; }); }; + var normalMaps = function () { return Vars.maps.customMaps().toArray().filter(function (m) { return !isThrowbackFile(m.file); }); }; + function showMapDetails(target, map) { + return __awaiter(this, void 0, void 0, function () { + var res; + return __generator(this, function (_a) { + switch (_a.label) { + case 0: return [4 /*yield*/, menus_1.Menu.buttons(target, map.name(), "[accent]Description: [white]".concat(map.description(), "\n[accent]Author: [white]").concat(map.author(), "\n[accent]Fastest Time: [white]").concat((0, utils_1.formatTime)(maps_1.FMap.getCreate(map).stats().shortestTime), "\n[accent]Runs: [white]").concat(maps_1.FMap.getCreate(map).stats().allRunCount, "\n[accent]Winrate: [white]").concat((maps_1.FMap.getCreate(map).stats().winRate * 100).toFixed(2), "%"), [[ + { data: "vote", text: "[green]Vote for this Map" } + ], [ + { data: "back", text: "[red]Back" } + ]], { onCancel: "null" })]; + case 1: + res = _a.sent(); + return [2 /*return*/, res !== null && res !== void 0 ? res : "back"]; + } + }); + }); + } + function pickMapFromList(target, title, maps) { + return __awaiter(this, void 0, void 0, function () { + var map, action; + return __generator(this, function (_a) { + switch (_a.label) { + case 0: return [4 /*yield*/, menus_1.Menu.pagedList(target, title, "Select a map to view more information.", maps, { optionStringifier: function (map) { return map.name(); }, rowsPerPage: 10, columns: 1 })]; + case 1: + map = _a.sent(); + return [4 /*yield*/, showMapDetails(target, map)]; + case 2: + action = _a.sent(); + if (action === "vote") + return [2 /*return*/, map]; + return [2 /*return*/, null]; + } + }); + }); + } + function currentMenu(target) { + return __awaiter(this, void 0, void 0, function () { + var result, _a, picked, throwback, picked, campaign, campaignMaps, picked; + var _b, _c, _d, _e; + return __generator(this, function (_f) { + switch (_f.label) { + case 0: return [4 /*yield*/, menus_1.Menu.menu("Select a map", "[accent]---Current Map---\nMap Name: [white]".concat(Vars.state.map.name(), "\n[accent]Map Author: [white]").concat(Vars.state.map.author(), "\nFastest Time: [white]").concat((0, utils_1.formatTime)(maps_1.FMap.getCreate(Vars.state.map).stats().shortestTime), "\nCurrent Time: [white]").concat((0, utils_1.formatTime)((_e = (_c = (_b = maps_1.PartialMapRun.current) === null || _b === void 0 ? void 0 : _b.duration()) !== null && _c !== void 0 ? _c : (_d = maps_1.FMap.getCreate(Vars.state.map).runs.at(-1)) === null || _d === void 0 ? void 0 : _d.duration()) !== null && _e !== void 0 ? _e : 0)), ["[green]Current Maps", "[yellow]Throwback Maps", "[orange]Campaigns"], target, { columns: 1, includeCancel: "Close" })]; + case 1: + result = _f.sent(); + _a = result; + switch (_a) { + case '[green]Current Maps': return [3 /*break*/, 2]; + case '[yellow]Throwback Maps': return [3 /*break*/, 4]; + case '[orange]Campaigns': return [3 /*break*/, 6]; + } + return [3 /*break*/, 9]; + case 2: return [4 /*yield*/, pickMapFromList(target, "Current Maps", normalMaps())]; + case 3: + picked = _f.sent(); + if (picked) + return [2 /*return*/, picked]; + return [2 /*return*/, currentMenu(target)]; + case 4: + throwback = throwbackMaps(); + if (throwback.length == 0) + (0, commands_1.fail)("No throwback maps are currently available."); + return [4 /*yield*/, pickMapFromList(target, "Throwback Maps", throwback)]; + case 5: + picked = _f.sent(); + if (picked) + return [2 /*return*/, picked]; + return [2 /*return*/, currentMenu(target)]; + case 6: + if (config_1.campaigns.length == 0) + (0, commands_1.fail)("No campaigns are currently available."); + return [4 /*yield*/, menus_1.Menu.pagedList(target, "Campaigns", "Select a campaign to view its maps.", config_1.campaigns, { optionStringifier: function (c) { return c.name; }, rowsPerPage: 10, columns: 1 })]; + case 7: + campaign = _f.sent(); + campaignMaps = campaign.mapFileNames + .map(function (f) { return Vars.maps.customMaps().find(function (m) { return m.file.name() == f; }); }) + .filter(function (m) { return m != null; }); + if (campaignMaps.length == 0) + (0, commands_1.fail)("Campaign \"".concat(campaign.name, "\" has no available maps.")); + return [4 /*yield*/, pickMapFromList(target, "Campaign: ".concat(campaign.name), campaignMaps)]; + case 8: + picked = _f.sent(); + if (picked) + return [2 /*return*/, picked]; + return [2 /*return*/, currentMenu(target)]; + case 9: + ; + return [2 /*return*/]; + } + }); + }); + } + function sendVote(sender, map) { + votes.set(sender, map); + if (voteEndTime == -1) { + if ((Date.now() - lastVoteTime) < funcs_1.Duration.minutes(1)) + (0, commands_1.fail)("Please wait 1 minute before starting a new map vote."); + startVote(); + Call.sendMessage("[cyan]Next Map Vote: ".concat(sender.name, "[cyan] started a map vote, and voted for [yellow]").concat(map.name(), "[cyan]. Use [white]/nextmap ").concat(map.plainName(), "[] to add your vote, or run [white]/maps[] to see other available maps.")); + } + else { + Call.sendMessage("[cyan]Next Map Vote: ".concat(sender.name, "[cyan] voted for [yellow]").concat(map.name(), "[cyan]. Time left: [scarlet]").concat((0, utils_1.formatTimeRelative)(voteEndTime, true))); + showVotes(); + } + } + function resetVotes() { + votes.clear(); + voteEndTime = -1; + task === null || task === void 0 ? void 0 : task.cancel(); + } + function getMapData() { + return __spreadArray([], __read(votes.values()), false).reduce(function (acc, map) { return (acc.increment(map), acc); }, new ObjectIntMap()).entries().toArray(); + } + function showVotes() { + Call.sendMessage("[green]Current votes:\n-----------------------------\n".concat(getMapData().map(function (_a) { + var map = _a.key, votes = _a.value; + return "[cyan]".concat(map.name(), "[yellow]: ").concat(votes); + }).toString("\n"))); + } + function startVote() { + voteEndTime = Date.now() + voteDuration; + task = Timer.schedule(endVote, voteDuration / 1000); + } + function endVote() { + if (voteEndTime == -1) + return; //aborted somehow + if (votes.size == 0) + return; //no votes? + if (votes.size + 2 <= lastVoteCount && (Date.now() - lastVoteTime) < funcs_1.Duration.minutes(10)) { + //If the number of votes is 2 less than the previous number of votes for a vote in the past 10 minutes, abor + Call.sendMessage("[cyan]Next Map Vote: [scarlet]Vote aborted because a previous vote had significantly higher turnout"); + resetVotes(); + return; + } + else { + lastVoteTime = Date.now(); + lastVoteCount = votes.size; + } + var mapData = getMapData(); + var highestVoteCount = mapData.max(floatf(function (e) { return e.value; })).value; + var highestVotedMaps = mapData.select(function (e) { return e.value == highestVoteCount; }); + var winner; + if (highestVotedMaps.size > 1) { + winner = highestVotedMaps.random().key; + Call.sendMessage("[green]There was a tie between the following maps:\n".concat(highestVotedMaps.map(function (_a) { + var map = _a.key, votes = _a.value; + return "[cyan]".concat(map.name(), "[yellow]: ").concat(votes); + }).toString("\n"), "\n[green]Picking random winner: [yellow]").concat(winner.name())); + } + else { + winner = highestVotedMaps.get(0).key; + Call.sendMessage("[green]Map voting complete! The next map will be [yellow]".concat(winner.name(), " [green]with [yellow]").concat(highestVoteCount, "[green] votes.")); + } + if (winner != random) + Vars.maps.setNextMapOverride(winner); + resetVotes(); + } + Events.on(EventType.GameOverEvent, resetVotes); + Events.on(EventType.ServerLoadEvent, resetVotes); + return { + args: ['map:mapOrRandom?'], + description: 'Allows you to vote for the next map.', + perm: commands_1.Perm.play, + data: { votes: votes, voteEndTime: function () { return voteEndTime; }, resetVotes: resetVotes, endVote: endVote }, + requirements: [commands_1.Req.cooldown(10000)], + handler: function (_a) { + return __awaiter(this, arguments, void 0, function (_b) { + var _c, _d, map; + var _e; + var args = _b.args, sender = _b.sender; + return __generator(this, function (_f) { + switch (_f.label) { + case 0: + if (config_1.Gamemode.testsrv()) + (0, commands_1.fail)("Please use /forcenextmap instead."); + if (votes.get(sender)) + (0, commands_1.fail)("You have already voted."); + if (!((_e = args.map) !== null && _e !== void 0)) return [3 /*break*/, 1]; + _c = _e; + return [3 /*break*/, 3]; + case 1: + _d = args; + return [4 /*yield*/, currentMenu(sender)]; + case 2: + _c = (_d.map = _f.sent()); + _f.label = 3; + case 3: + _c; + map = args.map === "random" ? random : args.map; + sendVote(sender, map); + return [2 /*return*/]; + } + }); + }); + } + }; + }), surrender: (0, commands_1.command)(function () { + var prefix = "[orange]Surrender[white]: "; + var managers = Team.all.map(function (team) { + return new votes_1.VoteManager(funcs_1.Duration.minutes(1.5), ["fractionOfVoters", config_1.Gamemode.hexed() ? 1 : 3 / 4], function (p) { return p.team() == team; }) + .on("success", function () { return team.cores().copy().each(function (c) { return c.kill(); }); }) + .on("vote passed", function () { return Call.sendMessage(prefix + "Team ".concat(team.coloredName(), " has voted to forfeit this match.")); }) + .on("vote failed", function (t) { return t.messageEligibleVoters(prefix + "Team ".concat(team.coloredName(), " has chosen not to forfeit this match.")); }) + .on("player vote change", function (t, player, oldVote, newVote) { return t.messageEligibleVoters(prefix + "".concat(player.name, "[white] ").concat(oldVote == newVote ? "still " : "", "wants to forfeit this match. [orange]").concat(t.currentVotes(), "[white] votes, [orange]").concat(t.requiredVotes(), "[white] required.")); }) + .on("player vote removed", function (t, player) { return t.messageEligibleVoters(prefix + "Player ".concat(player.name, "[white] has left the game. [orange]").concat(t.currentVotes(), "[white] votes, [orange]").concat(t.requiredVotes(), "[white] required.")); }); + }); + globals_1.FishEvents.on("playerTeamChange", function (_, fishP, previous) { + managers[previous.id].unvote(fishP); + }); + return { + args: ["force:boolean?", "team:team?"], + description: "Vote to surrender to the enemy team.", + perm: commands_1.Perm.play, + requirements: [commands_1.Req.mode("pvp"), commands_1.Req.teamAlive], + data: { managers: managers }, + handler: function (_a) { + return __awaiter(this, arguments, void 0, function (_b) { + var t, manager; + var sender = _b.sender, _c = _b.args, force = _c.force, team = _c.team; + return __generator(this, function (_d) { + switch (_d.label) { + case 0: + t = sender.hasPerm("admin") && team ? team : sender.team(); + manager = managers[t.id]; + if (!(sender.hasPerm("admin") && force != undefined)) return [3 /*break*/, 4]; + if (!force) return [3 /*break*/, 2]; + return [4 /*yield*/, menus_1.Menu.confirmDangerous(sender, "Are you sure you want to force team ".concat(t.coloredName(), "[] to lose?"))]; + case 1: + _d.sent(); + manager.messageEligibleVoters(prefix + "Vote forced by admin ".concat(sender.name, "[white].")); + Call.sendMessage(prefix + "Team ".concat(t.coloredName(), " has voted to forfeit this match.")); + return [3 /*break*/, 3]; + case 2: + manager.messageEligibleVoters(prefix + "Votes cleared by admin ".concat(sender.name, "[white].")); + _d.label = 3; + case 3: + manager.forceVote(force); + return [2 /*return*/]; + case 4: + if (sender.ranksAtLeast("mod")) + commands_1.Req.cooldown(5000); + else + commands_1.Req.cooldown(20000); + manager.vote(sender, 1, 0); + return [2 /*return*/]; + } + }); + }); + }, + }; + }), stats: { + args: ["target:player", "global:boolean?"], + perm: commands_1.Perm.none, + description: "Views a player's stats.", + handler: function (_a) { + var _b = _a.args, target = _b.target, _c = _b.global, global = _c === void 0 ? false : _c, output = _a.output, f = _a.f; + var stats = global ? target.globalStats : target.stats; + output(f(templateObject_18 || (templateObject_18 = __makeTemplateObject(["[accent]Statistics for player ", " ", ":\n(note: we started recording statistics on 22 Jan 2024)\n[white]--------------[]\nBlocks broken: ", "\nBlocks placed: ", "\nChat messages sent: ", "\nGames finished: ", "\nTime in-game: ", "\nWin rate: ", ""], ["[accent]\\\nStatistics for player ", " ", ":\n(note: we started recording statistics on 22 Jan 2024)\n[white]--------------[]\nBlocks broken: ", "\nBlocks placed: ", "\nChat messages sent: ", "\nGames finished: ", "\nTime in-game: ", "\nWin rate: ", ""])), target, global ? "across all servers" : "on this server", stats.blocksBroken, stats.blocksPlaced, stats.chatMessagesSent, stats.gamesFinished, (0, utils_1.formatTime)(stats.timeInGame), stats.gamesWon / stats.gamesFinished)); + } + }, showworld: { + args: ["x:number?", "y:number?", "size:number?"], + perm: commands_1.Perm.none, + description: "Views the world as a 2D scrollable menu.", + requirements: [commands_1.Req.cooldown(4000), commands_1.Req.integerRange("size", 1, 20)], + handler: function (_a) { + var sender = _a.sender, _b = _a.args, _c = _b.size, size = _c === void 0 ? 7 : _c, x = _b.x, y = _b.y; + if (Vars.state.rules.fog) + (0, commands_1.fail)("This command is disabled when fog is enabled."); + var options = (0, funcs_1.to2DArray)(Reflect.get(Vars.world.tiles, "array").map(function (tile) { return ({ + text: tile.block().emoji(), + data: null, + }); }), Vars.world.width()).reverse(); + var height = Vars.world.height(); + void menus_1.Menu.scroll2D(sender, "The World", "Use the arrow keys to navigate around the world. Click a blank square to exit.", options, { + columns: size, + rows: size, + x: x ? x - Math.trunc(size / 2) : 0, + y: height - (y ? y + 1 + Math.trunc(size / 2) : size), + getCenterText: function (x, y) { return "".concat(x, ",").concat(height - y - size); } + }); + } + }, mapinfo: { + args: ["map:map?"], + perm: commands_1.Perm.none, + description: "Displays information about a map.", + handler: function (_a) { + var _b; + var output = _a.output, map = _a.args.map, f = _a.f, sender = _a.sender; + if (map) { + var fmap = (_b = maps_1.FMap.getCreate(map)) !== null && _b !== void 0 ? _b : (0, commands_1.fail)("Map data is still being loaded, try again later."); + output(fmap.displayStats(f)); + } + else { + void menus_1.Menu.textPages(sender, Vars.maps.customMaps().map(function (m) { + return ["Map information", function () { var _a, _b; return (_b = (_a = maps_1.FMap.getCreate(m)) === null || _a === void 0 ? void 0 : _a.displayStats(f)) !== null && _b !== void 0 ? _b : (0, commands_1.fail)("Map data is still being loaded, try again later."); }]; + }).toArray(), [], { + startPage: Vars.maps.customMaps().toArray().indexOf(Vars.state.map), + }); + } + } + }, gamemode: { + args: ["mode:string"], + perm: new commands_1.Perm("changeGamemode", "manager").exceptModes({ + testsrv: commands_1.Perm.play, + }), + description: "Sets the gamemode.", + requirements: [commands_1.Req.cooldownGlobal(10000)], + handler: function (_a) { + var args = _a.args, sender = _a.sender, outputSuccess = _a.outputSuccess, lastUsedSuccessfully = _a.lastUsedSuccessfully; + if (!sender.hasPerm('trusted')) + commands_1.Req.cooldownGlobal(30000)({ lastUsedSuccessfully: lastUsedSuccessfully }); + //Unpause + Vars.state.set(GameState.State.playing); + switch (args.mode) { + case "attack": + Vars.state.rules.attackMode = true; + Vars.state.rules.pvp = false; + Vars.state.rules.infiniteResources = false; + break; + case "survival": + Vars.state.rules.attackMode = false; + Vars.state.rules.waves = true; + Vars.state.rules.pvp = false; + Vars.state.rules.infiniteResources = false; + break; + case "pvp": + Vars.state.rules.attackMode = true; + Vars.state.rules.pvp = true; + Vars.state.rules.waves = false; + Vars.state.rules.infiniteResources = false; + break; + case "sandbox": + Vars.state.rules.attackMode = true; + Vars.state.rules.pvp = false; + Vars.state.rules.waves = false; + Vars.state.rules.infiniteResources = true; + break; + default: (0, commands_1.fail)("Invalid mode, valid modes are: attack, survival, pvp"); + } + var reloader = new WorldReloader(); + Reflect.set(reloader, "wasServer", true); + Reflect.set(reloader, "players", Groups.player.copy()); + Call.worldDataBegin(); + reloader.end(); + Call.sendMessage("[orange]Player ".concat(sender.cleanedName, " changed the gamemode to ").concat(args.mode)); + outputSuccess("Changed mode to ".concat(args.mode)); + } + }, mixunit: { + args: ["type:unittype", "base:unittype"], + description: "Spawns a unit that is made of two unit types mixed together.", + perm: commands_1.Perm.admin.exceptModes({ + sandbox: commands_1.Perm.play + }), + requirements: function (_a) { + var sender = _a.sender; + return [!sender.hasPerm("admin") && commands_1.Req.cooldown(1500), commands_1.Req.unitExists()].filter(Boolean); + }, + handler: function (_a) { + var args = _a.args, sender = _a.sender, f = _a.f, outputSuccess = _a.outputSuccess; + var _b = sender.unit(), team = _b.team, x = _b.x, y = _b.y; + var unit = args.base.create(team); + unit.type = args.type; + unit.maxHealth = args.type.health; //because half-dead units aren't fun + unit.set(x, y); + unit.add(); + outputSuccess(f(templateObject_19 || (templateObject_19 = __makeTemplateObject(["Spawned a ", " that is partly a ", "."], ["Spawned a ", " that is partly a ", "."])), args.type, args.base)); + } + }, achievement: { + args: ["name:string?", "verbose:boolean?"], + description: "Displays information on a specific achievement.", + perm: commands_1.Perm.none, + handler: function (_a) { + return __awaiter(this, arguments, void 0, function (_b) { + var matching, achievement, _c; + var _d = _b.args, _e = _d.name, name = _e === void 0 ? "" : _e, _f = _d.verbose, verbose = _f === void 0 ? false : _f, sender = _b.sender, f = _b.f, output = _b.output; + return __generator(this, function (_g) { + switch (_g.label) { + case 0: + name = Strings.stripColors(name.toLowerCase()); + matching = achievements_1.Achievement.all.filter(function (a) { return Strings.stripColors(a.name).toLowerCase().includes(name); }); + if (matching.length == 0) + (0, commands_1.fail)(f(templateObject_20 || (templateObject_20 = __makeTemplateObject(["No achievements found with name ", ". To view all achievements, run [accent]/achievements[]."], ["No achievements found with name ", ". To view all achievements, run [accent]/achievements[]."])), name)); + if (!(matching.length > 2)) return [3 /*break*/, 2]; + return [4 /*yield*/, menus_1.Menu.pagedList(sender, "Achievement", "Select an achievement to view", matching, { + onCancel: "reject", + columns: 2, + optionStringifier: function (a) { return "".concat(a.icon, "[] ").concat(a.name); } + })]; + case 1: + _c = _g.sent(); + return [3 /*break*/, 3]; + case 2: + _c = matching[0]; + _g.label = 3; + case 3: + achievement = _c; + output(config_1.FColor.achievement(templateObject_21 || (templateObject_21 = __makeTemplateObject(["Achievement ", " ", "\n[white]--------------[]\n", "\nAllowed modes: ", "\nUnlocked: ", "\n", "", "", ""], ["\\\nAchievement ", " ", "\n[white]--------------[]\n", "\nAllowed modes: ", "\nUnlocked: ", "\n", "\\\n", "\\\n", "\\\n"])), achievement.icon, achievement.name, achievement.description + (achievement.extendedDescription ? ("\n" + "[gray]".concat(achievement.extendedDescription)) : ""), achievement.modesText, f.boolGood(achievement.has(sender)), verbose ? "[gray]ID: (".concat(achievement.nid, ")").concat(achievement.sid, "\n") : "", verbose ? "[gray]Notifies: ".concat(achievement.notify, "\n") : "", achievement.hidden ? "This achievement is secret." : "")); + return [2 /*return*/]; + } + }); + }); + } + }, achievementlist: { + args: ["target:player?"], + description: "Shows all achievements in a paged menu.", + perm: commands_1.Perm.none, + handler: function (_a) { + return __awaiter(this, arguments, void 0, function (_b) { + var sender = _b.sender, _c = _b.args.target, target = _c === void 0 ? sender : _c, f = _b.f; + return __generator(this, function (_d) { + switch (_d.label) { + case 0: return [4 /*yield*/, menus_1.Menu.textPages(sender, achievements_1.Achievement.all.filter(function (a) { return !a.hidden || a.has(target); }) + .map(function (a) { return [ + "".concat(a.icon, "[] ").concat(a.name), + function () { return config_1.FColor.achievement(templateObject_22 || (templateObject_22 = __makeTemplateObject(["", "\nAllowed modes: ", "\nUnlocked: ", "\n", ""], ["\\\n", "\nAllowed modes: ", "\nUnlocked: ", "\n", "\\\n"])), a.description + (a.extendedDescription ? ("\n" + "[gray]".concat(a.extendedDescription)) : ""), a.modesText, f.boolGood(a.has(target)), a.hidden ? "This achievement is secret." : ""); } + ]; }))]; + case 1: + _d.sent(); + return [2 /*return*/]; + } + }); + }); + } + }, achievementgrid: { + args: ["target:player?"], + description: "Shows all achievements in a 2D scrolling menu.", + perm: commands_1.Perm.none, + handler: function (_a) { + return __awaiter(this, arguments, void 0, function (_b) { + var visibleAchievements, options, numberAchievements, totalAchievements, x, y, a; + var _c; + var sender = _b.sender, _d = _b.args.target, target = _d === void 0 ? sender : _d, f = _b.f; + return __generator(this, function (_e) { + switch (_e.label) { + case 0: + visibleAchievements = achievements_1.Achievement.all.filter(function (a) { return !a.hidden || a.has(target); }); + options = (0, funcs_1.to2DArray)(visibleAchievements, 7).map(function (row) { return row.map(function (a) { return ({ + data: a, + text: a.has(target) ? a.icon : "[gray]".concat(Strings.stripColors(a.icon)), + }); }); }); + numberAchievements = achievements_1.Achievement.all.filter(function (a) { return a.has(target); }).length; + totalAchievements = visibleAchievements.length; + x = 0, y = 0; + a = null; + _e.label = 1; + case 1: + if (!true) return [3 /*break*/, 3]; + return [4 /*yield*/, menus_1.Menu.scroll2D(sender, "Achievements", a ? config_1.FColor.achievement(templateObject_23 || (templateObject_23 = __makeTemplateObject(["", " ", "\n\n", "\n\nAllowed modes: ", "\nUnlocked: ", "\n", ""], ["\\\n", " ", "\n\n", "\n\nAllowed modes: ", "\nUnlocked: ", "\n", "\\\n"])), a.icon, a.name, a.description + (a.extendedDescription ? ("\n" + "[gray]".concat(a.extendedDescription)) : ""), a.modesText, f.boolGood(a.has(target)), a.hidden ? "This achievement is secret." : "") : + (target == sender ? "You have ".concat(numberAchievements, "/").concat(totalAchievements, " achievements.") + : config_1.FColor.achievement(templateObject_24 || (templateObject_24 = __makeTemplateObject(["Player ", " has ", "/", " achievements."], ["Player ", " has ", "/", " achievements."])), target.prefixedName, numberAchievements, totalAchievements)) + + "\nClick an achievement icon to show more information.", options, { onCancel: "reject", columns: 5, rows: 4, getCenterText: function () { return String.fromCharCode(Iconc.settings); }, x: x, y: y })]; + case 2: + //the loop will be aborted if the menu is cancelled (promise will reject) + _c = __read.apply(void 0, [_e.sent(), 3]), a = _c[0], x = _c[1], y = _c[2]; + if (a == achievements_1.Achievements.click_me && target == sender) + a.grantTo(sender); + return [3 /*break*/, 1]; + case 3: return [2 /*return*/]; + } + }); + }); + } + } })); +var templateObject_1, templateObject_2, templateObject_3, templateObject_4, templateObject_5, templateObject_6, templateObject_7, templateObject_8, templateObject_9, templateObject_10, templateObject_11, templateObject_12, templateObject_13, templateObject_14, templateObject_15, templateObject_16, templateObject_17, templateObject_18, templateObject_19, templateObject_20, templateObject_21, templateObject_22, templateObject_23, templateObject_24; diff --git a/build/scripts/commands/member.js b/build/scripts/commands/member.js index b432bb30..90115b4d 100644 --- a/build/scripts/commands/member.js +++ b/build/scripts/commands/member.js @@ -1,137 +1,137 @@ -"use strict"; -/* -Copyright © BalaM314, 2026. All Rights Reserved. -This file contains member commands, which are fun cosmetics for donators. -*/ -Object.defineProperty(exports, "__esModule", { value: true }); -exports.commands = void 0; -var commands_1 = require("/frameworks/commands"); -exports.commands = (0, commands_1.commandList)({ - pet: (0, commands_1.command)({ - args: ["name:string?"], - description: 'Spawns a cool pet with a displayed name that follows you around.', - perm: commands_1.Perm.member, - data: {}, - handler: function (_a) { - var _b, _c; - var args = _a.args, sender = _a.sender, data = _a.data, outputSuccess = _a.outputSuccess; - if (!args.name) { - var pet_1 = data[sender.uuid]; - if (pet_1) { - pet_1.kill(); - delete data[sender.uuid]; - outputSuccess("Your pet has been removed."); - return; - } - } - if (sender.muted || !args.name) - args.name = "".concat(sender.name, "[white]'s pet"); - if (args.name.length > 500) - (0, commands_1.fail)("Name cannot be more than 500 characters."); - if (Strings.stripColors(args.name).length > 70) - (0, commands_1.fail)("Name cannot be more than 70 characters, not including color tags."); - (_b = data[sender.uuid]) === null || _b === void 0 ? void 0 : _b.kill(); - var unit = (_c = sender.unit()) !== null && _c !== void 0 ? _c : (0, commands_1.fail)("You do not have a unit for the pet to follow."); - var pet = UnitTypes.merui.spawn(sender.team(), unit.x, unit.y); - pet.apply(StatusEffects.disarmed, Number.MAX_SAFE_INTEGER); - data[sender.uuid] = pet; - Call.infoPopup('[#7FD7FD7f]\uE81B', 5, Align.topRight, 180, 0, 0, 10); - outputSuccess("Spawned a pet."); - var petName = args.name; - var vec = new Vec2(0, 0); - (function controlUnit() { - try { - var unit_1 = sender.unit(); - var currentPet = data[sender.uuid]; - if (pet != currentPet) - return; - if (currentPet.dead) { - delete data[sender.uuid]; - return; - } - if (!sender.connected()) { - currentPet === null || currentPet === void 0 ? void 0 : currentPet.kill(); - return; - } - if (unit_1 && currentPet) { - var distX = unit_1.x - currentPet.x; - var distY = unit_1.y - currentPet.y; - vec.set(distX, distY); - if (vec.len() > 50) { - currentPet.approach(vec); - } - if (vec.len() > 20 * 8) { - currentPet.apply(StatusEffects.fast, 60); - } - Call.label(petName, 0.07, currentPet.x, currentPet.y + 5); - //Pets share the sender's trail - if (sender.trail) { - Call.effect(Fx[sender.trail.type], currentPet.x, currentPet.y, 0, sender.trail.color); - } - } - return Timer.schedule(controlUnit, 0.05); - } - catch (err) { - Log.err(err); - } - })(); - } - }), - highlight: { - args: ['color:string?'], - description: 'Makes your chat text colored by default.', - perm: commands_1.Perm.member, - handler: function (_a) { - var args = _a.args, sender = _a.sender, outputFail = _a.outputFail, outputSuccess = _a.outputSuccess; - if (args.color == null || args.color.length == 0) { - if (sender.highlight != null) { - sender.highlight = null; - outputSuccess("Cleared your highlight."); - } - else { - outputFail("No highlight to clear."); - } - } - else if (Strings.stripColors(args.color) == "") { - sender.highlight = args.color; - outputSuccess("Set highlight to ".concat(args.color.replace("[", "").replace("]", ""), ".")); - } - else if (Strings.stripColors("[".concat(args.color, "]")) == "") { - sender.highlight = "[".concat(args.color, "]"); - outputSuccess("Set highlight to ".concat(args.color, ".")); - } - else { - outputFail("[yellow]\"".concat(args.color, "[yellow]\" was not a valid color!")); - } - } - }, - rainbow: { - args: ["speed:number?"], - description: 'Make your name change colors.', - perm: commands_1.Perm.member, - requirements: [commands_1.Req.integerRange("speed", 0, 10)], - handler: function (_a) { - var _b; - var args = _a.args, sender = _a.sender, outputSuccess = _a.outputSuccess; - var colors = ['[red]', '[orange]', '[yellow]', '[acid]', '[blue]', '[purple]']; - function rainbowLoop(index, fishP) { - Timer.schedule(function () { - if (!(fishP.rainbow && fishP.player && fishP.connected())) - return; - fishP.player.name = colors[index % colors.length] + Strings.stripColors(fishP.player.name); - rainbowLoop(index + 1, fishP); - }, args.speed / 5); - } - if (!args.speed) { - sender.rainbow = null; - sender.updateName(); - outputSuccess("Turned off rainbow."); - } - else { - (_b = sender.rainbow) !== null && _b !== void 0 ? _b : (sender.rainbow = { speed: args.speed }); - rainbowLoop(0, sender); - outputSuccess("Activated rainbow name mode with speed ".concat(args.speed)); - } - } - } -}); +"use strict"; +/* +Copyright © BalaM314, 2026. All Rights Reserved. +This file contains member commands, which are fun cosmetics for donators. +*/ +Object.defineProperty(exports, "__esModule", { value: true }); +exports.commands = void 0; +var commands_1 = require("/frameworks/commands"); +exports.commands = (0, commands_1.commandList)({ + pet: (0, commands_1.command)({ + args: ["name:string?"], + description: 'Spawns a cool pet with a displayed name that follows you around.', + perm: commands_1.Perm.member, + data: {}, + handler: function (_a) { + var _b, _c; + var args = _a.args, sender = _a.sender, data = _a.data, outputSuccess = _a.outputSuccess; + if (!args.name) { + var pet_1 = data[sender.uuid]; + if (pet_1) { + pet_1.kill(); + delete data[sender.uuid]; + outputSuccess("Your pet has been removed."); + return; + } + } + if (sender.muted || !args.name) + args.name = "".concat(sender.name, "[white]'s pet"); + if (args.name.length > 500) + (0, commands_1.fail)("Name cannot be more than 500 characters."); + if (Strings.stripColors(args.name).length > 70) + (0, commands_1.fail)("Name cannot be more than 70 characters, not including color tags."); + (_b = data[sender.uuid]) === null || _b === void 0 ? void 0 : _b.kill(); + var unit = (_c = sender.unit()) !== null && _c !== void 0 ? _c : (0, commands_1.fail)("You do not have a unit for the pet to follow."); + var pet = UnitTypes.merui.spawn(sender.team(), unit.x, unit.y); + pet.apply(StatusEffects.disarmed, Number.MAX_SAFE_INTEGER); + data[sender.uuid] = pet; + Call.infoPopup('[#7FD7FD7f]\uE81B', 5, Align.topRight, 180, 0, 0, 10); + outputSuccess("Spawned a pet."); + var petName = args.name; + var vec = new Vec2(0, 0); + (function controlUnit() { + try { + var unit_1 = sender.unit(); + var currentPet = data[sender.uuid]; + if (pet != currentPet) + return; + if (currentPet.dead) { + delete data[sender.uuid]; + return; + } + if (!sender.connected()) { + currentPet === null || currentPet === void 0 ? void 0 : currentPet.kill(); + return; + } + if (unit_1 && currentPet) { + var distX = unit_1.x - currentPet.x; + var distY = unit_1.y - currentPet.y; + vec.set(distX, distY); + if (vec.len() > 50) { + currentPet.approach(vec); + } + if (vec.len() > 20 * 8) { + currentPet.apply(StatusEffects.fast, 60); + } + Call.label(petName, 0.07, currentPet.x, currentPet.y + 5); + //Pets share the sender's trail + if (sender.trail) { + Call.effect(Fx[sender.trail.type], currentPet.x, currentPet.y, 0, sender.trail.color); + } + } + return Timer.schedule(controlUnit, 0.05); + } + catch (err) { + Log.err(err); + } + })(); + } + }), + highlight: { + args: ['color:string?'], + description: 'Makes your chat text colored by default.', + perm: commands_1.Perm.member, + handler: function (_a) { + var args = _a.args, sender = _a.sender, outputFail = _a.outputFail, outputSuccess = _a.outputSuccess; + if (args.color == null || args.color.length == 0) { + if (sender.highlight != null) { + sender.highlight = null; + outputSuccess("Cleared your highlight."); + } + else { + outputFail("No highlight to clear."); + } + } + else if (Strings.stripColors(args.color) == "") { + sender.highlight = args.color; + outputSuccess("Set highlight to ".concat(args.color.replace("[", "").replace("]", ""), ".")); + } + else if (Strings.stripColors("[".concat(args.color, "]")) == "") { + sender.highlight = "[".concat(args.color, "]"); + outputSuccess("Set highlight to ".concat(args.color, ".")); + } + else { + outputFail("[yellow]\"".concat(args.color, "[yellow]\" was not a valid color!")); + } + } + }, + rainbow: { + args: ["speed:number?"], + description: 'Make your name change colors.', + perm: commands_1.Perm.member, + requirements: [commands_1.Req.integerRange("speed", 0, 10)], + handler: function (_a) { + var _b; + var args = _a.args, sender = _a.sender, outputSuccess = _a.outputSuccess; + var colors = ['[red]', '[orange]', '[yellow]', '[acid]', '[blue]', '[purple]']; + function rainbowLoop(index, fishP) { + Timer.schedule(function () { + if (!(fishP.rainbow && fishP.player && fishP.connected())) + return; + fishP.player.name = colors[index % colors.length] + Strings.stripColors(fishP.player.name); + rainbowLoop(index + 1, fishP); + }, args.speed / 5); + } + if (!args.speed) { + sender.rainbow = null; + sender.updateName(); + outputSuccess("Turned off rainbow."); + } + else { + (_b = sender.rainbow) !== null && _b !== void 0 ? _b : (sender.rainbow = { speed: args.speed }); + rainbowLoop(0, sender); + outputSuccess("Activated rainbow name mode with speed ".concat(args.speed)); + } + } + } +}); diff --git a/build/scripts/commands/staff.js b/build/scripts/commands/staff.js index 7a5bb59d..4d65abef 100644 --- a/build/scripts/commands/staff.js +++ b/build/scripts/commands/staff.js @@ -1,1584 +1,1584 @@ -"use strict"; -/* -Copyright © BalaM314, 2026. All Rights Reserved. -This file contains the in-game chat commands that can be run by trusted staff. -*/ -var __makeTemplateObject = (this && this.__makeTemplateObject) || function (cooked, raw) { - if (Object.defineProperty) { Object.defineProperty(cooked, "raw", { value: raw }); } else { cooked.raw = raw; } - return cooked; -}; -var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { - if (k2 === undefined) k2 = k; - var desc = Object.getOwnPropertyDescriptor(m, k); - if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { - desc = { enumerable: true, get: function() { return m[k]; } }; - } - Object.defineProperty(o, k2, desc); -}) : (function(o, m, k, k2) { - if (k2 === undefined) k2 = k; - o[k2] = m[k]; -})); -var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { - Object.defineProperty(o, "default", { enumerable: true, value: v }); -}) : function(o, v) { - o["default"] = v; -}); -var __importStar = (this && this.__importStar) || (function () { - var ownKeys = function(o) { - ownKeys = Object.getOwnPropertyNames || function (o) { - var ar = []; - for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k; - return ar; - }; - return ownKeys(o); - }; - return function (mod) { - if (mod && mod.__esModule) return mod; - var result = {}; - if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]); - __setModuleDefault(result, mod); - return result; - }; -})(); -var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { - function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } - return new (P || (P = Promise))(function (resolve, reject) { - function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } - function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } - function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } - step((generator = generator.apply(thisArg, _arguments || [])).next()); - }); -}; -var __generator = (this && this.__generator) || function (thisArg, body) { - var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g = Object.create((typeof Iterator === "function" ? Iterator : Object).prototype); - return g.next = verb(0), g["throw"] = verb(1), g["return"] = verb(2), typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g; - function verb(n) { return function (v) { return step([n, v]); }; } - function step(op) { - if (f) throw new TypeError("Generator is already executing."); - while (g && (g = 0, op[0] && (_ = 0)), _) try { - if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t; - if (y = 0, t) op = [op[0] & 2, t.value]; - switch (op[0]) { - case 0: case 1: t = op; break; - case 4: _.label++; return { value: op[1], done: false }; - case 5: _.label++; y = op[1]; op = [0]; continue; - case 7: op = _.ops.pop(); _.trys.pop(); continue; - default: - if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; } - if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; } - if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; } - if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; } - if (t[2]) _.ops.pop(); - _.trys.pop(); continue; - } - op = body.call(thisArg, _); - } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; } - if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true }; - } -}; -var __read = (this && this.__read) || function (o, n) { - var m = typeof Symbol === "function" && o[Symbol.iterator]; - if (!m) return o; - var i = m.call(o), r, ar = [], e; - try { - while ((n === void 0 || n-- > 0) && !(r = i.next()).done) ar.push(r.value); - } - catch (error) { e = { error: error }; } - finally { - try { - if (r && !r.done && (m = i["return"])) m.call(i); - } - finally { if (e) throw e.error; } - } - return ar; -}; -var __spreadArray = (this && this.__spreadArray) || function (to, from, pack) { - if (pack || arguments.length === 2) for (var i = 0, l = from.length, ar; i < l; i++) { - if (ar || !(i in from)) { - if (!ar) ar = Array.prototype.slice.call(from, 0, i); - ar[i] = from[i]; - } - } - return to.concat(ar || Array.prototype.slice.call(from)); -}; -var __values = (this && this.__values) || function(o) { - var s = typeof Symbol === "function" && Symbol.iterator, m = s && o[s], i = 0; - if (m) return m.call(o); - if (o && typeof o.length === "number") return { - next: function () { - if (o && i >= o.length) o = void 0; - return { value: o && o[i++], done: !o }; - } - }; - throw new TypeError(s ? "Object is not iterable." : "Symbol.iterator is not defined."); -}; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.commands = void 0; -var api = __importStar(require("/api")); -var config_1 = require("/config"); -var files_1 = require("/files"); -var fjsContext = __importStar(require("/fjsContext")); -var commands_1 = require("/frameworks/commands"); -var menus_1 = require("/frameworks/menus"); -var funcs_1 = require("/funcs"); -var globals_1 = require("/globals"); -var maps_1 = require("/maps"); -var players_1 = require("/players"); -var ranks_1 = require("/ranks"); -var utils_1 = require("/utils"); -exports.commands = (0, commands_1.commandList)({ - warn: { - args: ['player:player', 'message:string?'], - description: 'Sends the player a warning (menu popup).', - perm: commands_1.Perm.warn, - requirements: [commands_1.Req.cooldown(3000)], - handler: function (_a) { - var _b; - var args = _a.args, sender = _a.sender, outputSuccess = _a.outputSuccess, f = _a.f; - if (args.player.hasPerm("blockTrolling")) - (0, commands_1.fail)(f(templateObject_1 || (templateObject_1 = __makeTemplateObject(["Player ", " is insufficiently trollable."], ["Player ", " is insufficiently trollable."])), args.player)); - var message = (_b = args.message) !== null && _b !== void 0 ? _b : "You have been warned. I suggest you stop what you're doing"; - void menus_1.Menu.menu('Warning', message, ["[green]Accept"], args.player, { onCancel: 'null' }) - .then(function () { return outputSuccess('Player acknowledged the warning.'); }); - (0, utils_1.logAction)('warned', sender, args.player, message); - outputSuccess(f(templateObject_2 || (templateObject_2 = __makeTemplateObject(["Warned player ", " for \"", "\""], ["Warned player ", " for \"", "\""])), args.player, message)); - } - }, - mute: { - args: ['player:player'], - description: 'Stops a player from chatting.', - perm: commands_1.Perm.mod, - requirements: [commands_1.Req.moderate("player")], - handler: function (_a) { - return __awaiter(this, arguments, void 0, function (_b) { - var args = _b.args, sender = _b.sender, outputSuccess = _b.outputSuccess, f = _b.f; - return __generator(this, function (_c) { - switch (_c.label) { - case 0: - if (args.player.muted) - (0, commands_1.fail)(f(templateObject_3 || (templateObject_3 = __makeTemplateObject(["Player ", " is already muted."], ["Player ", " is already muted."])), args.player)); - return [4 /*yield*/, args.player.mute(sender)]; - case 1: - _c.sent(); - (0, utils_1.logAction)('muted', sender, args.player); - outputSuccess(f(templateObject_4 || (templateObject_4 = __makeTemplateObject(["Muted player ", "."], ["Muted player ", "."])), args.player)); - return [2 /*return*/]; - } - }); - }); - } - }, - unmute: { - args: ['player:player'], - description: 'Unmutes a player', - perm: commands_1.Perm.mod, - handler: function (_a) { - return __awaiter(this, arguments, void 0, function (_b) { - var args = _b.args, sender = _b.sender, outputSuccess = _b.outputSuccess, f = _b.f; - return __generator(this, function (_c) { - switch (_c.label) { - case 0: - if (!args.player.muted && args.player.autoflagged) - (0, commands_1.fail)(f(templateObject_5 || (templateObject_5 = __makeTemplateObject(["Player ", " is not muted, but they are autoflagged. You probably want to free them with /free."], ["Player ", " is not muted, but they are autoflagged. You probably want to free them with /free."])), args.player)); - if (!args.player.muted) - (0, commands_1.fail)(f(templateObject_6 || (templateObject_6 = __makeTemplateObject(["Player ", " is not muted."], ["Player ", " is not muted."])), args.player)); - return [4 /*yield*/, args.player.unmute(sender)]; - case 1: - _c.sent(); - (0, utils_1.logAction)('unmuted', sender, args.player); - outputSuccess(f(templateObject_7 || (templateObject_7 = __makeTemplateObject(["Unmuted player ", "."], ["Unmuted player ", "."])), args.player)); - return [2 /*return*/]; - } - }); - }); - } - }, - kick: { - args: ["player:player", "duration:time?", "reason:string?"], - description: 'Kick a player with optional reason.', - perm: commands_1.Perm.mod, - requirements: [commands_1.Req.moderate("player")], - handler: function (_a) { - var _b, _c, _d; - var args = _a.args, outputSuccess = _a.outputSuccess, f = _a.f, sender = _a.sender; - if (!sender.hasPerm("admin") && args.duration && args.duration > funcs_1.Duration.hours(6)) - (0, commands_1.fail)("Maximum kick duration is 6 hours."); - var reason = (_b = args.reason) !== null && _b !== void 0 ? _b : "A staff member did not like your actions."; - var duration = (_c = args.duration) !== null && _c !== void 0 ? _c : 60000; - args.player.kick(reason, duration); - (0, utils_1.logAction)("kicked", sender, args.player, (_d = args.reason) !== null && _d !== void 0 ? _d : undefined, duration); - if (duration > 60000) - args.player.setPunishedIP(config_1.stopAntiEvadeTime); - outputSuccess(f(templateObject_8 || (templateObject_8 = __makeTemplateObject(["Kicked player ", " for ", " with reason \"", "\""], ["Kicked player ", " for ", " with reason \"", "\""])), args.player, (0, utils_1.formatTime)(duration), reason)); - } - }, - pardon: { - args: ["player:offlinePlayer"], - description: 'Pardons a votekicked player.', - perm: commands_1.Perm.mod, - requirements: [commands_1.Req.moderate("player")], - handler: function (_a) { - var player = _a.args.player, admins = _a.admins, outputSuccess = _a.outputSuccess, f = _a.f; - var info = admins.getInfo(player.uuid); - if (Time.millis() > info.lastKicked && !admins.kickedIPs.containsKey(info.lastIP)) - (0, commands_1.fail)("That player is not kicked."); - info.lastKicked = 0; - admins.kickedIPs.remove(info.lastIP); - outputSuccess(f(templateObject_9 || (templateObject_9 = __makeTemplateObject(["Pardoned player ", "."], ["Pardoned player ", "."])), player)); - } - }, - stop: { - args: ['player:player', "time:time?", "message:string?"], - description: 'Stops a player.', - perm: commands_1.Perm.mod, - requirements: [commands_1.Req.moderate("player", true)], - handler: function (_a) { - return __awaiter(this, arguments, void 0, function (_b) { - var previousTime, time; - var _c, _d, _e, _f; - var args = _b.args, sender = _b.sender, outputSuccess = _b.outputSuccess, f = _b.f; - return __generator(this, function (_g) { - switch (_g.label) { - case 0: - if (!args.player.marked()) return [3 /*break*/, 2]; - //overload: overwrite stoptime - if (!args.time) - (0, commands_1.fail)(f(templateObject_10 || (templateObject_10 = __makeTemplateObject(["Player ", " is already marked."], ["Player ", " is already marked."])), args.player)); - previousTime = (0, utils_1.formatTimeRelative)(args.player.unmarkTime, true); - return [4 /*yield*/, args.player.updateStopTime(args.time)]; - case 1: - _g.sent(); - outputSuccess(f(templateObject_11 || (templateObject_11 = __makeTemplateObject(["Player ", "'s stop time has been updated to ", " (was ", ")."], ["Player ", "'s stop time has been updated to ", " (was ", ")."])), args.player, (0, utils_1.formatTime)(args.time), previousTime)); - (0, utils_1.logAction)("updated stop time of", sender, args.player, (_c = args.message) !== null && _c !== void 0 ? _c : undefined, args.time); - return [3 /*break*/, 4]; - case 2: - time = (_d = args.time) !== null && _d !== void 0 ? _d : (0, utils_1.untilForever)(); - if (time + Date.now() > globals_1.maxTime) - (0, commands_1.fail)("Error: time too high."); - return [4 /*yield*/, args.player.stop(sender, time, (_e = args.message) !== null && _e !== void 0 ? _e : undefined)]; - case 3: - _g.sent(); - (0, utils_1.logAction)('stopped', sender, args.player, (_f = args.message) !== null && _f !== void 0 ? _f : undefined, time); - //TODO outputGlobal() - Call.sendMessage("[orange]Player \"".concat(args.player.prefixedName, "[orange]\" has been marked for ").concat((0, utils_1.formatTime)(time)).concat(args.message ? " with reason: [white]".concat(args.message, "[]") : "", ".")); - _g.label = 4; - case 4: return [2 /*return*/]; - } - }); - }); - } - }, - free: { - args: ['player:player'], - description: 'Frees a player.', - perm: commands_1.Perm.mod, - handler: function (_a) { - return __awaiter(this, arguments, void 0, function (_b) { - var args = _b.args, sender = _b.sender, outputSuccess = _b.outputSuccess, outputFail = _b.outputFail, f = _b.f; - return __generator(this, function (_c) { - switch (_c.label) { - case 0: - if (!args.player.marked()) return [3 /*break*/, 2]; - return [4 /*yield*/, args.player.free(sender)]; - case 1: - _c.sent(); - (0, utils_1.logAction)('freed', sender, args.player); - outputSuccess(f(templateObject_12 || (templateObject_12 = __makeTemplateObject(["Player ", " has been unmarked."], ["Player ", " has been unmarked."])), args.player)); - return [3 /*break*/, 3]; - case 2: - if (args.player.autoflagged) { - args.player.autoflagged = false; - args.player.sendMessage("[yellow]You have been freed! Enjoy!"); - args.player.updateName(); - args.player.forceRespawn(); - outputSuccess(f(templateObject_13 || (templateObject_13 = __makeTemplateObject(["Player ", " has been unflagged."], ["Player ", " has been unflagged."])), args.player)); - } - else { - outputFail(f(templateObject_14 || (templateObject_14 = __makeTemplateObject(["Player ", " is not marked or autoflagged."], ["Player ", " is not marked or autoflagged."])), args.player)); - } - _c.label = 3; - case 3: return [2 /*return*/]; - } - }); - }); - } - }, - setrank: { - args: ["player:player", "rank:rank"], - description: "Set a player's rank.", - perm: commands_1.Perm.mod, - requirements: [commands_1.Req.moderate("player")], - handler: function (_a) { - return __awaiter(this, arguments, void 0, function (_b) { - var _c = _b.args, rank = _c.rank, player = _c.player, outputSuccess = _b.outputSuccess, f = _b.f, sender = _b.sender; - return __generator(this, function (_d) { - switch (_d.label) { - case 0: - if (rank.level >= sender.rank.level) - (0, commands_1.fail)(f(templateObject_15 || (templateObject_15 = __makeTemplateObject(["You do not have permission to promote players to rank ", ", because your current rank is ", ""], ["You do not have permission to promote players to rank ", ", because your current rank is ", ""])), rank, sender.rank)); - if (rank == ranks_1.Rank.pi && !config_1.Mode.localDebug) - (0, commands_1.fail)(f(templateObject_16 || (templateObject_16 = __makeTemplateObject(["Rank ", " is immutable."], ["Rank ", " is immutable."])), rank)); - if (player.immutable() && !config_1.Mode.localDebug) - (0, commands_1.fail)(f(templateObject_17 || (templateObject_17 = __makeTemplateObject(["Player ", " is immutable."], ["Player ", " is immutable."])), player)); - if (!(player == sender && rank.level < sender.rank.level)) return [3 /*break*/, 2]; - return [4 /*yield*/, menus_1.Menu.confirmDangerous(sender, "[red] ARE YOU SURE YOU WANT TO SELF DEMOTE. THIS ACTION CANNOT BE UNDONE!")]; - case 1: - _d.sent(); - _d.label = 2; - case 2: return [4 /*yield*/, player.setRank(rank)]; - case 3: - _d.sent(); - (0, utils_1.logAction)("set rank to ".concat(rank.name, " for"), sender, player); - outputSuccess(f(templateObject_18 || (templateObject_18 = __makeTemplateObject(["Set rank of player ", " to ", ""], ["Set rank of player ", " to ", ""])), player, rank)); - return [2 /*return*/]; - } - }); - }); - } - }, - setflag: { - args: ["player:player", "flag:roleflag", "value:boolean"], - description: "Set a player's role flags.", - perm: commands_1.Perm.mod, - requirements: [commands_1.Req.moderate("player")], - handler: function (_a) { - return __awaiter(this, arguments, void 0, function (_b) { - var _c = _b.args, flag = _c.flag, player = _c.player, value = _c.value, sender = _b.sender, outputSuccess = _b.outputSuccess, f = _b.f; - return __generator(this, function (_d) { - switch (_d.label) { - case 0: - if (!sender.hasPerm("admin") && !flag.assignableByModerators) - (0, commands_1.fail)(f(templateObject_19 || (templateObject_19 = __makeTemplateObject(["You do not have permission to change the value of role flag ", ""], ["You do not have permission to change the value of role flag ", ""])), flag)); - return [4 /*yield*/, player.setFlag(flag, value)]; - case 1: - _d.sent(); - (0, utils_1.logAction)("set roleflag ".concat(flag.name, " to ").concat(value, " for"), sender, player); - outputSuccess(f(templateObject_20 || (templateObject_20 = __makeTemplateObject(["Set role flag ", " of player ", " to ", ""], ["Set role flag ", " of player ", " to ", ""])), flag, player, value)); - return [2 /*return*/]; - } - }); - }); - } - }, - murder: { - args: [], - description: 'Kills all ohno units', - perm: commands_1.Perm.mod, - customUnauthorizedMessage: "[yellow]You're a [scarlet]monster[].", - handler: function (_a) { - var output = _a.output, f = _a.f, allCommands = _a.allCommands; - var Ohnos = allCommands["ohno"].data; //this is not ideal... TODO commit omega shenanigans - var numOhnos = Ohnos.amount(); - Ohnos.killAll(); - output(f(templateObject_21 || (templateObject_21 = __makeTemplateObject(["[orange]You massacred ", " helpless ohno crawlers."], ["[orange]You massacred ", " helpless ohno crawlers."])), numOhnos)); - } - }, - stop_offline: { - args: ["time:time?", "name:string?"], - description: "Stops an offline player.", - perm: commands_1.Perm.mod, - handler: function (_a) { - return __awaiter(this, arguments, void 0, function (_b) { - function stop(option, time) { - return __awaiter(this, void 0, void 0, function () { - var fishP; - return __generator(this, function (_a) { - switch (_a.label) { - case 0: - fishP = players_1.FishPlayer.getFromInfo(option); - if (!sender.canModerate(fishP, true)) return [3 /*break*/, 2]; - (0, utils_1.logAction)(fishP.marked() ? time == 1000 ? "freed" : "updated stop time of" : "stopped", sender, option, undefined, time); - return [4 /*yield*/, fishP.stop(sender, time)]; - case 1: - _a.sent(); - outputSuccess(f(templateObject_22 || (templateObject_22 = __makeTemplateObject(["Player ", " was marked for ", "."], ["Player ", " was marked for ", "."])), option, (0, utils_1.formatTime)(time))); - return [3 /*break*/, 3]; - case 2: - outputFail("You do not have permission to stop this player."); - _a.label = 3; - case 3: return [2 /*return*/]; - } - }); - }); - } - var maxPlayers, info, possiblePlayers, exactPlayers, score_1, optionPlayer, _c, _d, _e; - var _f, _g; - var args = _b.args, sender = _b.sender, outputFail = _b.outputFail, outputSuccess = _b.outputSuccess, f = _b.f, admins = _b.admins; - return __generator(this, function (_h) { - switch (_h.label) { - case 0: - maxPlayers = 60; - if (!(args.name && globals_1.uuidPattern.test(args.name))) return [3 /*break*/, 4]; - info = admins.getInfoOptional(args.name); - if (!(info != null)) return [3 /*break*/, 2]; - return [4 /*yield*/, stop(info, (_f = args.time) !== null && _f !== void 0 ? _f : (0, utils_1.untilForever)())]; - case 1: - _h.sent(); - return [3 /*break*/, 3]; - case 2: - outputFail(f(templateObject_23 || (templateObject_23 = __makeTemplateObject(["Unknown UUID ", ""], ["Unknown UUID ", ""])), args.name)); - _h.label = 3; - case 3: return [2 /*return*/]; - case 4: - if (args.name) { - possiblePlayers = (0, funcs_1.setToArray)(admins.searchNames(args.name)); - if (possiblePlayers.length > maxPlayers) { - exactPlayers = (0, funcs_1.setToArray)(admins.findByName(args.name)); - if (exactPlayers.length > 0) { - possiblePlayers = exactPlayers; - } - else { - (0, commands_1.fail)("Too many players with that name."); - } - } - else if (possiblePlayers.length == 0) { - (0, commands_1.fail)("No players with that name were found."); - } - score_1 = function (data) { - var fishP = players_1.FishPlayer.getById(data.id); - if (fishP) - return fishP.lastJoined; - return -data.timesJoined; - }; - possiblePlayers.sort(function (a, b) { return score_1(b) - score_1(a); }); - } - else { - possiblePlayers = players_1.FishPlayer.recentLeaves.map(function (p) { return p.info(); }); - } - return [4 /*yield*/, menus_1.Menu.menu("Stop", "Choose a player to mark", possiblePlayers, sender, { - includeCancel: true, - optionStringifier: function (p) { return p.lastName; } - })]; - case 5: - optionPlayer = _h.sent(); - if (!((_g = args.time) !== null && _g !== void 0)) return [3 /*break*/, 6]; - _c = _g; - return [3 /*break*/, 8]; - case 6: - _d = args; - _e = utils_1.match; - return [4 /*yield*/, menus_1.Menu.menu("Stop", "Select stop time", ["2 days", "7 days", "30 days", "forever"], sender)]; - case 7: - _c = (_d.time = _e.apply(void 0, [_h.sent(), { - "2 days": funcs_1.Duration.days(2), - "7 days": funcs_1.Duration.days(7), - "30 days": funcs_1.Duration.days(30), - "forever": globals_1.maxTime - Date.now() - 10000, - }])); - _h.label = 8; - case 8: - _c; - return [4 /*yield*/, stop(optionPlayer, args.time)]; - case 9: - _h.sent(); - return [2 /*return*/]; - } - }); - }); - } - }, - mute_offline: { - args: ["name:string?"], - description: "Mutes an offline player.", - perm: commands_1.Perm.mod, - handler: function (_a) { - return __awaiter(this, arguments, void 0, function (_b) { - function mute(option) { - return __awaiter(this, void 0, void 0, function () { - var fishP; - return __generator(this, function (_a) { - switch (_a.label) { - case 0: - fishP = players_1.FishPlayer.getFromInfo(option); - if (!sender.canModerate(fishP, true)) - (0, commands_1.fail)("You do not have permission to mute this player."); - return [4 /*yield*/, menus_1.Menu.confirm(sender, "Are you sure you want to ".concat(fishP.muted ? "unmute" : "mute", " player ").concat(option.lastName, "?"), { - title: "Mute Offine Confirmation", - confirmText: "[green]Yes, ".concat(fishP.muted ? "unmute" : "mute", " them"), - })]; - case 1: - _a.sent(); - (0, utils_1.logAction)(fishP.muted ? "unmuted" : "muted", sender, fishP); - if (!fishP.muted) return [3 /*break*/, 3]; - return [4 /*yield*/, fishP.unmute(sender)]; - case 2: - _a.sent(); - return [3 /*break*/, 5]; - case 3: return [4 /*yield*/, fishP.mute(sender)]; - case 4: - _a.sent(); - _a.label = 5; - case 5: - outputSuccess("".concat(fishP.muted ? "Muted" : "Unmuted", " ").concat(option.lastName, ".")); - return [2 /*return*/]; - } - }); - }); - } - var maxPlayers, info, possiblePlayers, exactPlayers, score_2, option; - var _c; - var args = _b.args, sender = _b.sender, outputSuccess = _b.outputSuccess, f = _b.f, admins = _b.admins; - return __generator(this, function (_d) { - switch (_d.label) { - case 0: - maxPlayers = 300; - if (!(args.name && globals_1.uuidPattern.test(args.name))) return [3 /*break*/, 2]; - info = (_c = admins.getInfoOptional(args.name)) !== null && _c !== void 0 ? _c : (0, commands_1.fail)(f(templateObject_24 || (templateObject_24 = __makeTemplateObject(["Unknown UUID ", ""], ["Unknown UUID ", ""])), args.name)); - return [4 /*yield*/, mute(info)]; - case 1: - _d.sent(); - return [2 /*return*/]; - case 2: - if (args.name) { - possiblePlayers = (0, funcs_1.setToArray)(admins.searchNames(args.name)); - if (possiblePlayers.length > maxPlayers) { - exactPlayers = (0, funcs_1.setToArray)(admins.findByName(args.name)); - if (exactPlayers.length > 0) { - possiblePlayers = exactPlayers; - } - else { - (0, commands_1.fail)("Too many players with that name."); - } - } - else if (possiblePlayers.length == 0) { - (0, commands_1.fail)("No players with that name were found."); - } - score_2 = function (data) { - var fishP = players_1.FishPlayer.getById(data.id); - if (fishP) - return fishP.lastJoined; - return -data.timesJoined; - }; - possiblePlayers.sort(function (a, b) { return score_2(b) - score_2(a); }); - } - else { - possiblePlayers = players_1.FishPlayer.recentLeaves.map(function (p) { return p.info(); }); - } - return [4 /*yield*/, menus_1.Menu.pagedList(sender, "Mute", "Choose a player to mute", possiblePlayers, { - optionStringifier: function (p) { return p.lastName; } - })]; - case 3: - option = _d.sent(); - return [4 /*yield*/, mute(option)]; - case 4: - _d.sent(); - return [2 /*return*/]; - } - }); - }); - } - }, - restart: { - args: ["time:number?"], - perm: commands_1.Perm.admin, - description: "Restarts the server.", - handler: function (_a) { - var _b; - var time = _a.args.time; - (_b = globals_1.fishState.restartLoopTask) === null || _b === void 0 ? void 0 : _b.cancel(); - if (Groups.player.isEmpty()) { - if (time == undefined) { - Log.info("Restarting immediately as no players are online."); - time !== null && time !== void 0 ? time : (time = 0); - } - } - else if (config_1.Gamemode.pvp()) { - time !== null && time !== void 0 ? time : (time = -1); - Log.info("PVP: restart will occur at the end of the current match. Specify a time to override, but &rthat would interrupt the current pvp match, and players would lose their teams.&fr"); - } - else { - time !== null && time !== void 0 ? time : (time = 60); - } - if (time == -1) { - Call.sendMessage("[accent]---[[[coral]+++[]]---\n[accent]Server restart queued. The server will restart after the current match is over.[]\n[accent]---[[[coral]+++[]]---"); - globals_1.fishState.restartQueued = true; - } - else { - if (time < 0 || time > 100) - (0, commands_1.fail)("Invalid time: out of valid range."); - (0, utils_1.serverRestartLoop)(time); - if (config_1.Gamemode.pvp()) { - Call.sendMessage("[accent]---[[[coral]+++[]]---\n[accent]Server restart imminent. [green]We'll be back after 15 seconds.[]\n[accent]---[[[coral]+++[]]---"); - } - else { - Call.sendMessage("[accent]---[[[coral]+++[]]---\n[accent]Server restart imminent. [green]We'll be back with 20 seconds of downtime, and all progress will be saved.[]\n[accent]---[[[coral]+++[]]---"); - } - } - } - }, - history: { - args: ["player:player"], - description: "Shows moderation history for a player.", - perm: commands_1.Perm.mod, - handler: function (_a) { - var args = _a.args, output = _a.output, f = _a.f; - if (args.player.history && args.player.history.length > 0) { - output("[yellow]_______________Player history_______________\n\n" + - (args.player).history.sort(function (a, b) { return a.time - b.time; }).map(function (e) { - return "".concat(e.by, " [yellow]").concat(e.action, " ").concat(args.player.prefixedName, " [white]").concat((0, utils_1.formatTimeRelative)(e.time)); - }).join("\n")); - } - else { - output(f(templateObject_25 || (templateObject_25 = __makeTemplateObject(["[yellow]No history was found for player ", "."], ["[yellow]No history was found for player ", "."])), args.player)); - } - } - }, - save: { - args: [], - description: "Saves the game state.", - perm: commands_1.Perm.mod, - handler: function (_a) { - var outputSuccess = _a.outputSuccess; - players_1.FishPlayer.saveAll(); - players_1.FishPlayer.uploadAll(); - globals_1.FishEvents.fire("saveData", []); - var file = Vars.saveDirectory.child("1.".concat(Vars.saveExtension)); - SaveIO.save(file); - outputSuccess("Game saved."); - } - }, - wave: { - args: ["wave:number"], - description: "Sets the wave number.", - perm: commands_1.Perm.admin, - requirements: [commands_1.Req.positiveInteger("wave")], - handler: function (_a) { - var args = _a.args, outputSuccess = _a.outputSuccess, f = _a.f; - Vars.state.wave = args.wave; - outputSuccess(f(templateObject_26 || (templateObject_26 = __makeTemplateObject(["Set wave to ", ""], ["Set wave to ", ""])), Vars.state.wave)); - } - }, - label: { - args: ["time:time", "message:string"], - description: "Places a label at your position for a specified amount of time.", - perm: commands_1.Perm.mod, - handler: function (_a) { - var _b; - var args = _a.args, sender = _a.sender, outputSuccess = _a.outputSuccess, f = _a.f; - if (args.time > funcs_1.Duration.hours(10)) - (0, commands_1.fail)("Time must be less than 10 hours."); - var unit = (_b = sender.unit()) !== null && _b !== void 0 ? _b : (0, commands_1.fail)("You must be in a unit to use this command."); - var timeRemaining = args.time / 1000; - var labelx = unit.x; - var labely = unit.y; - var task = Timer.schedule(function () { - if (timeRemaining > 0) { - var timeseconds = timeRemaining % 60; - var timeminutes = (timeRemaining - timeseconds) / 60; - Call.label("".concat(sender.name, "\n\n[white]").concat(args.message, "\n\n[acid]").concat(timeminutes.toString().padStart(2, "0"), ":").concat(timeseconds.toString().padStart(2, "0")), 1, labelx, labely); - timeRemaining--; - } - }, 0, 1, args.time); - globals_1.fishState.labels.push({ x: labelx, y: labely, task: task }); - outputSuccess(f(templateObject_27 || (templateObject_27 = __makeTemplateObject(["Placed label \"", "\" for ", " seconds."], ["Placed label \"", "\" for ", " seconds."])), args.message, timeRemaining)); - } - }, - labelsticky: { - args: ["time:time", "message:string"], - description: "Places a label at the bottom left corner of everyone's screen.", - perm: commands_1.Perm.admin, - handler: function (_a) { - var args = _a.args, outputSuccess = _a.outputSuccess, f = _a.f; - if (args.time > funcs_1.Duration.hours(10)) - (0, commands_1.fail)("Time must be less than 10 hours."); - var timeRemaining = args.time / 1000; - var task = Timer.schedule(function () { - if (timeRemaining > 0) { - Call.label(args.message, 5, NaN, NaN); - timeRemaining -= 5; - } - }, 0, 5, Math.ceil(args.time / 5)); - globals_1.fishState.labels.push({ task: task, x: null, y: null }); - outputSuccess(f(templateObject_28 || (templateObject_28 = __makeTemplateObject(["Placed label \"", "\" for ", " seconds."], ["Placed label \"", "\" for ", " seconds."])), args.message, timeRemaining)); - } - }, - clearlabels: { - args: [], - description: "Removes all labels.", - perm: commands_1.Perm.mod, - handler: function (_a) { - var outputSuccess = _a.outputSuccess; - if (globals_1.fishState.labels.length == 0) - (0, commands_1.fail)("No labels found."); - globals_1.fishState.labels.forEach(function (l) { return l.task.cancel(); }); - outputSuccess("Removed all labels."); - } - }, - clearlabel: { - args: ["sticky:boolean?"], - description: "Removes the closest label, or sticky label if specified", - perm: commands_1.Perm.mod, - handler: function (_a) { - var _b; - var _c = _a.args.sticky, sticky = _c === void 0 ? false : _c, sender = _a.sender, outputSuccess = _a.outputSuccess; - if (globals_1.fishState.labels.length == 0) - (0, commands_1.fail)("No labels found."); - var label; - if (sticky) { - var index = globals_1.fishState.labels.findIndex(function (l) { return l.x == null; }); - if (index == -1) - (0, commands_1.fail)("No sticky label found."); - label = globals_1.fishState.labels.splice(index, 1)[0]; - } - else { - var unit_1 = (_b = sender.unit()) !== null && _b !== void 0 ? _b : (0, commands_1.fail)("Cannot remove the closest label because you are dead."); - var dist_1 = function (label) { - if (label.x == null || label.y == null) - return Infinity; - return Mathf.dst(label.x, label.y, unit_1.x, unit_1.y); - }; - var index = __spreadArray([], __read(globals_1.fishState.labels.entries()), false).reduce(function (a, b) { return dist_1(a[1]) < dist_1(b[1]) ? a : b; })[0]; - label = globals_1.fishState.labels.splice(index, 1)[0]; - } - label.task.cancel(); - outputSuccess("Removed one label."); - } - }, - member: { - args: ["value:boolean", "player:player"], - description: "Sets a player's member status.", - perm: commands_1.Perm.admin, - handler: function (_a) { - return __awaiter(this, arguments, void 0, function (_b) { - var args = _b.args, outputSuccess = _b.outputSuccess, f = _b.f; - return __generator(this, function (_c) { - switch (_c.label) { - case 0: return [4 /*yield*/, args.player.setFlag("member", args.value)]; - case 1: - _c.sent(); - outputSuccess(f(templateObject_29 || (templateObject_29 = __makeTemplateObject(["Set membership status of player ", " to ", "."], ["Set membership status of player ", " to ", "."])), args.player, args.value)); - return [2 /*return*/]; - } - }); - }); - } - }, - remind: { - args: ["rule:number", "target:player?"], - description: "Remind players in chat of a specific rule.", - perm: commands_1.Perm.mod, - handler: function (_a) { - var _b; - var args = _a.args, outputSuccess = _a.outputSuccess, f = _a.f; - var rule = (_b = config_1.rules[args.rule - 1]) !== null && _b !== void 0 ? _b : (0, commands_1.fail)("The rule you requested does not exist."); - if (args.target) { - args.target.sendMessage("A staff member wants to remind you of the following rule:\n" + rule); - outputSuccess(f(templateObject_30 || (templateObject_30 = __makeTemplateObject(["Reminded ", " of rule ", ""], ["Reminded ", " of rule ", ""])), args.target, args.rule)); - } - else { - Call.sendMessage("A staff member wants to remind everyone of the following rule:\n" + rule); - } - }, - }, - ban: { - args: ["uuid_or_ip:string?"], - description: "Bans a player by UUID and IP.", - perm: commands_1.Perm.admin, - handler: function (_a) { - return __awaiter(this, arguments, void 0, function (_b) { - var uuid, data, name, ip, ip, info, alreadyBanned, option; - var args = _b.args, sender = _b.sender, outputSuccess = _b.outputSuccess, f = _b.f, admins = _b.admins; - return __generator(this, function (_c) { - switch (_c.label) { - case 0: - if (!(args.uuid_or_ip && globals_1.uuidPattern.test(args.uuid_or_ip))) return [3 /*break*/, 2]; - uuid = args.uuid_or_ip; - data = void 0; - if ((data = admins.getInfoOptional(uuid)) != null && data.admin) - (0, commands_1.fail)("Cannot ban an admin."); - name = data ? "".concat((0, funcs_1.escapeStringColorsClient)(data.lastName), " (").concat(uuid, "/").concat(data.lastIP, ")") : uuid; - return [4 /*yield*/, menus_1.Menu.confirmDangerous(sender, "Are you sure you want to ban ".concat(name, "?"))]; - case 1: - _c.sent(); - admins.banPlayerID(uuid); - if (data) { - ip = data.lastIP; - admins.banPlayerIP(ip); - api.ban({ ip: ip, uuid: uuid }); - Log.info("".concat(uuid, "/").concat(ip, " was banned.")); - (0, utils_1.logAction)("banned", sender, data); - outputSuccess(f(templateObject_31 || (templateObject_31 = __makeTemplateObject(["Banned player ", " (", "/", ")"], ["Banned player ", " (", "/", ")"])), (0, funcs_1.escapeStringColorsClient)(data.lastName), uuid, ip)); - //TODO add way to specify whether to activate or escape color tags - } - else { - api.ban({ uuid: uuid }); - Log.info("".concat(uuid, " was banned.")); - (0, utils_1.logAction)("banned", sender, uuid); - outputSuccess(f(templateObject_32 || (templateObject_32 = __makeTemplateObject(["Banned player ", ". [yellow]Unable to determine IP.[]"], ["Banned player ", ". [yellow]Unable to determine IP.[]"])), uuid)); - } - (0, utils_1.updateBans)(function (player) { return "[scarlet]Player [yellow]".concat(player.name, "[scarlet] has been whacked by ").concat(sender.prefixedName, "."); }); - return [2 /*return*/]; - case 2: - if (!(args.uuid_or_ip && globals_1.ipPattern.test(args.uuid_or_ip))) return [3 /*break*/, 4]; - ip = args.uuid_or_ip; - return [4 /*yield*/, menus_1.Menu.confirmDangerous(sender, "Are you sure you want to ban IP ".concat(ip, "?"))]; - case 3: - _c.sent(); - api.ban({ ip: ip }); - info = admins.findByIP(ip); - if (info) - (0, utils_1.logAction)("banned", sender, info); - else - (0, utils_1.logAction)("banned ".concat(ip), sender); - alreadyBanned = admins.banPlayerIP(ip); - if (alreadyBanned) { - outputSuccess(f(templateObject_33 || (templateObject_33 = __makeTemplateObject(["IP ", " is already banned. Ban was synced to other servers."], ["IP ", " is already banned. Ban was synced to other servers."])), ip)); - } - else { - outputSuccess(f(templateObject_34 || (templateObject_34 = __makeTemplateObject(["IP ", " has been banned. Ban was synced to other servers."], ["IP ", " has been banned. Ban was synced to other servers."])), ip)); - } - (0, utils_1.updateBans)(function (player) { return "[scarlet]Player [yellow]".concat(player.name, "[scarlet] has been whacked by ").concat(sender.prefixedName, "."); }); - return [2 /*return*/]; - case 4: return [4 /*yield*/, menus_1.Menu.menu("[scarlet]BAN[]", "Choose a player to ban.", (0, funcs_1.setToArray)(Groups.player), sender, { - includeCancel: true, - optionStringifier: function (opt) { return opt.name; } - })]; - case 5: - option = _c.sent(); - if (option.admin) - (0, commands_1.fail)("Cannot ban an admin."); - return [4 /*yield*/, menus_1.Menu.confirmDangerous(sender, "Are you sure you want to ban ".concat(option.name, "?"))]; - case 6: - _c.sent(); - admins.banPlayerIP(option.ip()); //this also bans the UUID - api.ban({ ip: option.ip(), uuid: option.uuid() }); - Log.info("".concat(option.ip(), "/").concat(option.uuid(), " was banned.")); - (0, utils_1.logAction)("banned", sender, option.getInfo()); - outputSuccess(f(templateObject_35 || (templateObject_35 = __makeTemplateObject(["Banned player ", "."], ["Banned player ", "."])), option)); - (0, utils_1.updateBans)(function (player) { return "[scarlet]Player [yellow]".concat(player.name, "[scarlet] has been whacked by ").concat(sender.prefixedName, "."); }); - return [2 /*return*/]; - } - }); - }); - } - }, - kill: { - args: ["player:player"], - description: "Kills a player's unit.", - perm: commands_1.Perm.admin, - requirements: [commands_1.Req.moderate("player", true)], - handler: function (_a) { - var args = _a.args, outputFail = _a.outputFail, outputSuccess = _a.outputSuccess, f = _a.f; - var unit = args.player.unit(); - if (unit) { - unit.kill(); - outputSuccess(f(templateObject_36 || (templateObject_36 = __makeTemplateObject(["Killed the unit of player ", "."], ["Killed the unit of player ", "."])), args.player)); - } - else { - outputFail(f(templateObject_37 || (templateObject_37 = __makeTemplateObject(["Player ", " does not have a unit."], ["Player ", " does not have a unit."])), args.player)); - } - } - }, - killunits: { - args: ["team:team?", "unit:unittype?"], - description: "Kills all units, optionally specifying a team and unit type.", - perm: commands_1.Perm.massKill, - handler: function (_a) { - return __awaiter(this, arguments, void 0, function (_b) { - var i_1, before, i_2, before; - var _c = _b.args, team = _c.team, unit = _c.unit, sender = _b.sender, outputSuccess = _b.outputSuccess, f = _b.f; - return __generator(this, function (_d) { - switch (_d.label) { - case 0: - if (!team) return [3 /*break*/, 2]; - return [4 /*yield*/, menus_1.Menu.confirmDangerous(sender, "This will kill [scarlet]every ".concat(unit ? unit.localizedName : "unit", "[] on the team ").concat(team.coloredName(), "."), { confirmText: "[orange]Kill units[]" })]; - case 1: - _d.sent(); - if (unit) { - i_1 = 0; - team.data().units.each(function (u) { return u.type == unit; }, function (u) { - u.kill(); - i_1++; - }); - outputSuccess(f(templateObject_38 || (templateObject_38 = __makeTemplateObject(["Killed ", " units on ", "."], ["Killed ", " units on ", "."])), i_1, team)); - } - else { - before = team.data().units.size; - team.data().units.each(function (u) { return u.kill(); }); - outputSuccess(f(templateObject_39 || (templateObject_39 = __makeTemplateObject(["Killed ", " units on ", "."], ["Killed ", " units on ", "."])), before, team)); - } - return [3 /*break*/, 4]; - case 2: return [4 /*yield*/, menus_1.Menu.confirmDangerous(sender, "This will kill [scarlet]every single ".concat(unit ? unit.localizedName : "unit", "[]."), { confirmText: "[orange]Kill all units[]" })]; - case 3: - _d.sent(); - if (unit) { - i_2 = 0; - Groups.unit.each(function (u) { return u.type == unit; }, function (u) { - u.kill(); - i_2++; - }); - outputSuccess(f(templateObject_40 || (templateObject_40 = __makeTemplateObject(["Killed ", " units."], ["Killed ", " units."])), i_2)); - } - else { - before = Groups.unit.size(); - Groups.unit.each(function (u) { return u.kill(); }); - outputSuccess(f(templateObject_41 || (templateObject_41 = __makeTemplateObject(["Killed ", " units."], ["Killed ", " units."])), before)); - } - _d.label = 4; - case 4: return [2 /*return*/]; - } - }); - }); - } - }, - killbuildings: { - args: ["team:team?"], - description: "Kills all buildings (except cores), optionally specifying a team.", - perm: commands_1.Perm.massKill, - handler: function (_a) { - return __awaiter(this, arguments, void 0, function (_b) { - var count, count; - var team = _b.args.team, sender = _b.sender, outputSuccess = _b.outputSuccess, f = _b.f; - return __generator(this, function (_c) { - switch (_c.label) { - case 0: - if (!team) return [3 /*break*/, 2]; - return [4 /*yield*/, menus_1.Menu.confirmDangerous(sender, "This will kill [scarlet]every building[] on the team ".concat(team.coloredName(), ", except cores."), { confirmText: "[orange]Kill buildings[]" })]; - case 1: - _c.sent(); - count = team.data().buildings.size; - team.data().buildings.each(function (b) { return !(b.block instanceof CoreBlock); }, function (b) { return b.tile.remove(); }); - outputSuccess(f(templateObject_42 || (templateObject_42 = __makeTemplateObject(["Killed ", " buildings on ", "."], ["Killed ", " buildings on ", "."])), count, team)); - return [3 /*break*/, 4]; - case 2: return [4 /*yield*/, menus_1.Menu.confirmDangerous(sender, "This will kill [scarlet]every building[] except cores.", { confirmText: "[orange]Kill buildings[]" })]; - case 3: - _c.sent(); - count = Groups.build.size(); - Groups.build.each(function (b) { return !(b.block instanceof CoreBlock); }, function (b) { return b.tile.remove(); }); - outputSuccess(f(templateObject_43 || (templateObject_43 = __makeTemplateObject(["Killed ", " buildings."], ["Killed ", " buildings."])), count)); - _c.label = 4; - case 4: return [2 /*return*/]; - } - }); - }); - } - }, - respawn: { - args: ["player:player"], - description: "Forces a player to respawn.", - perm: commands_1.Perm.mod, - requirements: [commands_1.Req.moderate("player", true, "mod", true)], - handler: function (_a) { - var args = _a.args, outputSuccess = _a.outputSuccess, f = _a.f; - args.player.forceRespawn(); - outputSuccess(f(templateObject_44 || (templateObject_44 = __makeTemplateObject(["Respawned player ", "."], ["Respawned player ", "."])), args.player)); - } - }, - clearunit: { - args: ["target:player", "duration:time?"], - description: "Forces a player out of the unit they are controlling, and blocks them from possessing units for a specified duration.", - perm: commands_1.Perm.mod, - requirements: [commands_1.Req.moderate("target", false, "mod", false)], - handler: function (_a) { - var _b = _a.args, target = _b.target, duration = _b.duration, sender = _a.sender, outputSuccess = _a.outputSuccess, f = _a.f; - if (Date.now() > 1000 + target.blockedFromPossessingUnitsUntil) - duration !== null && duration !== void 0 ? duration : (duration = funcs_1.Duration.minutes(1)); - else - duration !== null && duration !== void 0 ? duration : (duration = 0); - if (duration == 0) { - target.blockedFromPossessingUnitsUntil = 0; - target.sendMessage("You are allowed to control units again."); - outputSuccess(f(templateObject_45 || (templateObject_45 = __makeTemplateObject(["Restored ", "'s ability to control units."], ["Restored ", "'s ability to control units."])), target)); - (0, utils_1.logAction)("restored unit possession for", sender, target); - } - else { - target.forceRespawn(); - target.blockedFromPossessingUnitsUntil = Date.now() + duration; - target.sendMessage("You have been blocked from controlling units for ".concat((0, utils_1.formatTime)(duration), ".")); - outputSuccess(f(templateObject_46 || (templateObject_46 = __makeTemplateObject(["Blocked ", " from controlling units for ", "."], ["Blocked ", " from controlling units for ", "."])), target, (0, utils_1.formatTime)(duration))); - (0, utils_1.logAction)("revoked unit possession for", sender, target, undefined, duration); - } - } - }, - clearcommand: { - args: ["target:player", "duration:time?"], - description: "Blocks a player from commanding units for a specified duration.", - perm: commands_1.Perm.mod, - requirements: [commands_1.Req.moderate("target", false, "mod", false)], - handler: function (_a) { - var _b = _a.args, target = _b.target, duration = _b.duration, sender = _a.sender, outputSuccess = _a.outputSuccess, f = _a.f; - if (Date.now() > 1000 + target.blockedFromCommandingUnitsUntil) - duration !== null && duration !== void 0 ? duration : (duration = funcs_1.Duration.minutes(1)); - else - duration !== null && duration !== void 0 ? duration : (duration = 0); - if (duration == 0) { - target.blockedFromCommandingUnitsUntil = 0; - target.sendMessage("You are allowed to command units again."); - outputSuccess(f(templateObject_47 || (templateObject_47 = __makeTemplateObject(["Restored ", "'s ability to command units."], ["Restored ", "'s ability to command units."])), target)); - (0, utils_1.logAction)("restored command mode for", sender, target, undefined, duration); - } - else { - target.blockedFromCommandingUnitsUntil = Date.now() + duration; - target.sendMessage("You have been blocked from commanding units for ".concat((0, utils_1.formatTime)(duration), ".")); - outputSuccess(f(templateObject_48 || (templateObject_48 = __makeTemplateObject(["Blocked ", " from commanding units for ", "."], ["Blocked ", " from commanding units for ", "."])), target, (0, utils_1.formatTime)(duration))); - (0, utils_1.logAction)("revoked command mode for", sender, target, undefined, duration); - } - } - }, - stealunit: { - args: ["target:player", "newcontroller:player?"], - description: "Steals the unit of a player, putting you in their unit and forcing them to respawn.", - perm: commands_1.Perm.mod, - requirements: [commands_1.Req.moderate("target", true, "mod", true), commands_1.Req.moderate("newcontroller", true, "mod", true)], - handler: function (_a) { - var _b; - var sender = _a.sender, _c = _a.args, target = _c.target, _d = _c.newcontroller, newcontroller = _d === void 0 ? sender : _d, outputSuccess = _a.outputSuccess, f = _a.f; - var unit = (_b = target.unit()) !== null && _b !== void 0 ? _b : (0, commands_1.fail)(f(templateObject_49 || (templateObject_49 = __makeTemplateObject(["Targeted player ", " is not in a unit."], ["Targeted player ", " is not in a unit."])), target)); - if (target.team() !== newcontroller.team()) { - if (!sender.hasPerm("changeTeamExternal")) { - if (!sender.hasPerm("changeTeam")) - (0, commands_1.fail)("You do not have permission to change teams."); - newcontroller.setTeam(unit.team); - } - } - target.forceRespawn(); - newcontroller.unit(unit); - if (newcontroller == sender) { - outputSuccess(f(templateObject_50 || (templateObject_50 = __makeTemplateObject(["Commandeered the unit of player ", "."], ["Commandeered the unit of player ", "."])), target)); - } - else { - outputSuccess(f(templateObject_51 || (templateObject_51 = __makeTemplateObject(["Transferred player ", " into the unit of ", "."], ["Transferred player ", " into the unit of ", "."])), newcontroller, target)); - newcontroller.sendMessage(f(templateObject_52 || (templateObject_52 = __makeTemplateObject(["[green]You were transferred to the unit of player ", " by ", "."], ["[green]You were transferred to the unit of player ", " by ", "."])), target, sender)('[green]')); - } - } - }, - m: { - args: ["message:string"], - description: "Sends a message to muted players only.", - perm: commands_1.Perm.mod, - handler: function (_a) { - var sender = _a.sender, args = _a.args; - players_1.FishPlayer.messageMuted(sender.prefixedName, args.message); - } - }, - info: { - args: ["target:player", "showColors:boolean?"], - description: "Displays information about an online player.", - perm: commands_1.Perm.none, - handler: function (_a) { - var sender = _a.sender, args = _a.args, output = _a.output, f = _a.f; - var info = args.target.info(); - var names = args.showColors - ? info.names.map(funcs_1.escapeStringColorsClient).toString(", ") - : __spreadArray([], __read(new Set(info.names.map(function (n) { return Strings.stripColors(n); }).toArray())), false).join(", "); - output(f(templateObject_53 || (templateObject_53 = __makeTemplateObject(["[accent]Info for player ", " [gray](", ") (#", ")\n\t[accent]Rank: ", "\n\t[accent]Role flags: ", "\n\t[accent]Stopped: ", "\n\t[accent]marked: ", "\n\t[accent]muted: ", "\n\t[accent]autoflagged: ", "\n\t[accent]VPN detected: ", "\n\t[accent]times joined / kicked: ", "/", "\n\t[accent]First joined: ", "\n\t[accent]Names used: [[", "]"], ["\\\n[accent]Info for player ", " [gray](", ") (#", ")\n\t[accent]Rank: ", "\n\t[accent]Role flags: ", "\n\t[accent]Stopped: ", "\n\t[accent]marked: ", "\n\t[accent]muted: ", "\n\t[accent]autoflagged: ", "\n\t[accent]VPN detected: ", "\n\t[accent]times joined / kicked: ", "/", "\n\t[accent]First joined: ", "\n\t[accent]Names used: [[", "]"])), args.target, (0, funcs_1.escapeStringColorsClient)(args.target.name), args.target.player.id.toString(), args.target.rank, Array.from(args.target.flags).map(function (f) { return f.coloredName(); }).join(" "), f.boolBad(!args.target.hasPerm("play")), args.target.marked() ? "until ".concat((0, utils_1.formatTimeRelative)(args.target.unmarkTime)) : "[green]false", f.boolBad(args.target.muted), f.boolBad(args.target.autoflagged), f.boolBad(args.target.ipDetectedVpn), info.timesJoined, info.timesKicked, args.target.firstJoined < 1 ? "unknown" : (0, utils_1.formatTimeRelative)(args.target.firstJoined), names)); - if (sender.hasPerm("viewUUIDs")) - output(f(templateObject_54 || (templateObject_54 = __makeTemplateObject(["\t[#FFAAAA]UUID: ", ""], ["\\t[#FFAAAA]UUID: ", ""])), args.target.uuid)); - if (sender.hasPerm("viewIPs")) - output(f(templateObject_55 || (templateObject_55 = __makeTemplateObject(["\t[#FFAAAA]IP: ", ""], ["\\t[#FFAAAA]IP: ", ""])), args.target.ip())); - } - }, - spawn: { - args: ["type:unittype", "x:number?", "y:number?", "count:number?", "team:team?", "effects:string?", "stack:boolean?"], - description: "Spawns a unit of specified type at your position. [scarlet]Usage will be logged.[]", - perm: commands_1.Perm.admin.exceptModes({ - testsrv: commands_1.Perm.trusted, - }), - data: [], - requirements: [commands_1.Req.positiveInteger("count")], - handler: function (_a) { - var _b, _c; - var sender = _a.sender, args = _a.args, data = _a.data, outputSuccess = _a.outputSuccess, f = _a.f; - var x = args.x ? (args.x * 8) : sender.player.x; - var y = args.y ? (args.y * 8) : sender.player.y; - var team = (_b = args.team) !== null && _b !== void 0 ? _b : sender.team(); - var count = Math.min((_c = args.count) !== null && _c !== void 0 ? _c : 1, 1000); - for (var i = 0; i < count; i++) { - var unit = args.type.create(team); - var xOffset = args.stack ? 0 : 0.01 * i; - var yOffset = args.stack ? 0 : 0.5 * (i % 10); - unit.set(x + xOffset, y + yOffset); - if (args.effects) - (0, utils_1.applyEffectMode)(args.effects, unit, 1e12); - unit.add(); - data.push(unit); - } - if (!(config_1.Gamemode.sandbox() || config_1.Gamemode.testsrv()) && args.effects !== 'paper') - (0, utils_1.logAction)("spawned unit ".concat(args.type.name).concat(count == 1 ? '' : " x".concat(count), " at ").concat(Math.round(x / 8), ", ").concat(Math.round(y / 8)) + (args.effects ? "with ".concat(args.effects, " effects") : ''), sender); - outputSuccess(f(templateObject_56 || (templateObject_56 = __makeTemplateObject(["Spawned unit ", " at (", ", ", ")"], ["Spawned unit ", " at (", ", ", ")"])), args.type, Math.round(x / 8), Math.round(y / 8))); - } - }, - setblock: { - args: ["x:number", "y:number", "block:block", "team:team?", "rotation:number?"], - description: "Sets the block at a location.", - perm: commands_1.Perm.admin.exceptModes({ - testsrv: commands_1.Perm.trusted, - }), - requirements: [commands_1.Req.integerRange("rotation", 0, 3)], - handler: function (_a) { - var _b, _c; - var args = _a.args, sender = _a.sender, outputSuccess = _a.outputSuccess, f = _a.f; - var team = (_b = args.team) !== null && _b !== void 0 ? _b : sender.team(); - var tile = Vars.world.tile(args.x, args.y); - if (tile == null) - (0, commands_1.fail)(f(templateObject_57 || (templateObject_57 = __makeTemplateObject(["Position (", ", ", ") is out of bounds."], ["Position (", ", ", ") is out of bounds."])), args.x, args.y)); - tile.setNet(args.block, team, (_c = args.rotation) !== null && _c !== void 0 ? _c : 0); - (0, utils_1.addToTileHistory)({ - pos: "".concat(args.x, ",").concat(args.y), - uuid: sender.uuid, - action: "setblocked", - type: args.block.localizedName - }); - if (!config_1.Gamemode.sandbox()) - (0, utils_1.logAction)("set block to ".concat(args.block.localizedName, " at ").concat(args.x, ",").concat(args.y), sender); - outputSuccess(f(templateObject_58 || (templateObject_58 = __makeTemplateObject(["Set block at ", ", ", " to ", ""], ["Set block at ", ", ", " to ", ""])), args.x, args.y, args.block)); - } - }, - setblockr: { - args: ["block:block?", "team:team?", "rotation:number?"], - description: "Sets the block at tapped locations, repeatedly.", - perm: commands_1.Perm.admin, - requirements: [commands_1.Req.integerRange("rotation", 0, 3)], - tapped: function (_a) { - var _b, _c; - var args = _a.args, sender = _a.sender, f = _a.f, x = _a.x, y = _a.y, outputSuccess = _a.outputSuccess; - if (!args.block) - (0, funcs_1.crash)("uh oh"); - var team = (_b = args.team) !== null && _b !== void 0 ? _b : sender.team(); - var tile = Vars.world.tile(x, y); - if (tile == null) - (0, commands_1.fail)(f(templateObject_59 || (templateObject_59 = __makeTemplateObject(["Position (", ", ", ") is out of bounds."], ["Position (", ", ", ") is out of bounds."])), x, y)); - tile.setNet(args.block, team, (_c = args.rotation) !== null && _c !== void 0 ? _c : 0); - (0, utils_1.addToTileHistory)({ - pos: "".concat(x, ",").concat(y), - uuid: sender.uuid, - action: "setblocked", - type: args.block.localizedName - }); - if (!config_1.Gamemode.sandbox()) - (0, utils_1.logAction)("set block to ".concat(args.block.localizedName, " at ").concat(x, ",").concat(y), sender); - outputSuccess(f(templateObject_60 || (templateObject_60 = __makeTemplateObject(["Set block at ", ", ", " to ", ""], ["Set block at ", ", ", " to ", ""])), x, y, args.block)); - }, - handler: function (_a) { - var args = _a.args, outputSuccess = _a.outputSuccess, handleTaps = _a.handleTaps, currentTapMode = _a.currentTapMode, f = _a.f; - if (args.block) { - handleTaps("on"); - if (currentTapMode == "off") { - outputSuccess("setblockr enabled.\n[scarlet]Be careful, you have the midas touch now![] Turn it off by running /setblockr again."); - } - else { - outputSuccess(f(templateObject_61 || (templateObject_61 = __makeTemplateObject(["Changed setblockr's block to ", ""], ["Changed setblockr's block to ", ""])), args.block)); - } - } - else { - if (currentTapMode == "off") { - (0, commands_1.fail)("Please specify the block to place."); - } - else { - handleTaps("off"); - outputSuccess("setblockr disabled."); - } - } - } - }, - exterminate: { - args: [], - description: "Removes all spawned units.", - perm: commands_1.Perm.admin.exceptModes({ - testsrv: commands_1.Perm.trusted, - }), - handler: function (_a) { - var sender = _a.sender, outputSuccess = _a.outputSuccess, f = _a.f, allCommands = _a.allCommands; - var numKilled = 0; - allCommands.spawn.data.forEach(function (u) { - if (u.isAdded() && !u.dead) { - u.kill(); - numKilled++; - } - }); - if (!config_1.Gamemode.sandbox()) - (0, utils_1.logAction)("exterminated ".concat(numKilled, " units"), sender); - outputSuccess(f(templateObject_62 || (templateObject_62 = __makeTemplateObject(["Exterminated ", " units."], ["Exterminated ", " units."])), numKilled)); - } - }, - js: { - args: ["javascript:string"], - description: "Run arbitrary javascript.", - perm: commands_1.Perm.runJS, - customUnauthorizedMessage: "[scarlet]You are not in the jsers file. This incident will be reported.[]", - handler: function (_a) { - var javascript = _a.args.javascript, output = _a.output, outputFail = _a.outputFail, sender = _a.sender; - //Additional validation couldn't hurt... - var playerInfo_AdminUsid = sender.info().adminUsid; - if (!playerInfo_AdminUsid || playerInfo_AdminUsid != sender.player.usid() || sender.usid != sender.player.usid()) { - api.sendModerationMessage("# !!!!! /js authentication failed !!!!!\nServer: ".concat(config_1.Gamemode.name(), " Player: ").concat((0, funcs_1.escapeTextDiscord)(sender.cleanedName), "/`").concat(sender.uuid, "`\n<@!709904412033810533>")); - (0, commands_1.fail)("Authentication failure"); - } - if (javascript == "Timer.instance().clear()") - (0, commands_1.fail)("Are you really sure you want to do that? If so, prepend \"void\" to your command."); - try { - var scripts = Vars.mods.getScripts(); - var out = scripts.context.evaluateString(scripts.scope, javascript, "fish-js-console.js", 1); - if (out instanceof Array) { - output("[cyan]Array: [[[]" + out.join(", ") + "[cyan]]"); - } - else if (out === undefined) { - output("[blue]undefined[]"); - } - else if (out === null) { - output("[blue]null[]"); - } - else if (out instanceof Error) { - outputFail((0, funcs_1.parseError)(out)); - } - else if (typeof out == "number") { - output("[blue]".concat(out, "[]")); - } - else { - output(out); - } - } - catch (err) { - outputFail((0, funcs_1.parseError)(err)); - } - } - }, - fjs: { - args: ["javascript:string"], - description: "Run arbitrary javascript in the fish-commands context.", - perm: commands_1.Perm.runJS, - customUnauthorizedMessage: "[scarlet]You are not in the jsers file. This incident will be reported.[]", - handler: function (_a) { - var javascript = _a.args.javascript, output = _a.output, outputFail = _a.outputFail, sender = _a.sender; - //Additional validation couldn't hurt... - var playerInfo_AdminUsid = sender.info().adminUsid; - if (!playerInfo_AdminUsid || playerInfo_AdminUsid != sender.player.usid() || sender.usid != sender.player.usid()) { - api.sendModerationMessage("# !!!!! /js authentication failed !!!!!\nServer: ".concat(config_1.Gamemode.name(), " Player: ").concat((0, funcs_1.escapeTextDiscord)(sender.cleanedName), "/`").concat(sender.uuid, "`\n<@!709904412033810533>")); - (0, commands_1.fail)("Authentication failure"); - } - fjsContext.runJS(javascript, output, outputFail, sender); - } - }, - antibot: { - args: ["timeout:time?"], - description: "Checks anti bot stats, or force enables anti bot mode.", - perm: commands_1.Perm.mod, - handler: function (_a) { - var args = _a.args, sender = _a.sender, outputSuccess = _a.outputSuccess, output = _a.output, f = _a.f; - if (args.timeout != undefined) { - args.timeout = Math.min(args.timeout, sender.hasPerm("admin") ? funcs_1.Duration.hours(1) : funcs_1.Duration.minutes(10)); - players_1.FishPlayer.triggerAntibot(args.timeout, "Manually triggered by player ".concat(sender.name), "manual"); - outputSuccess("Set antibot mode override for ".concat((0, utils_1.formatTime)(args.timeout), ".")); - } - else { - output("[acid]Antibot status:\n[acid]Enabled: ".concat(f.boolBad(players_1.FishPlayer.antiBotMode()), "\n").concat((0, utils_1.getAntiBotInfo)("client"))); - } - } - }, - chatstrictness: { - args: ["player:player", "value:string"], - description: "Sets chat strictness for a player.", - perm: commands_1.Perm.mod, - handler: function (_a) { - var _b = _a.args, player = _b.player, value = _b.value, sender = _a.sender, outputSuccess = _a.outputSuccess, f = _a.f; - if (!sender.canModerate(player, true)) - (0, commands_1.fail)("You do not have permission to set the chat strictness level of this player."); - if (!(value == "chat" || value == "strict")) - (0, commands_1.fail)("Invalid chat strictness level: valid levels are \"chat\", \"strict\""); - player.chatStrictness = value; - (0, utils_1.logAction)("set chat strictness to ".concat(value, " for"), sender, player); - outputSuccess(f(templateObject_63 || (templateObject_63 = __makeTemplateObject(["Set chat strictness for player ", " to \"", "\"."], ["Set chat strictness for player ", " to \"", "\"."])), player, value)); - } - }, - emanate: (0, commands_1.command)(function () { - var unitMapping = {}; - Timer.schedule(function () { - var e_1, _a; - try { - for (var _b = __values(Object.entries(unitMapping)), _c = _b.next(); !_c.done; _c = _b.next()) { - var _d = __read(_c.value, 2), uuid = _d[0], unit = _d[1]; - var fishP = players_1.FishPlayer.getById(uuid); - if (!fishP || !fishP.connected() || (unit.getPlayer() != fishP.player)) { - delete unitMapping[uuid]; - unit === null || unit === void 0 ? void 0 : unit.kill(); - } - } - } - catch (e_1_1) { e_1 = { error: e_1_1 }; } - finally { - try { - if (_c && !_c.done && (_a = _b.return)) _a.call(_b); - } - finally { if (e_1) throw e_1.error; } - } - }, 1, 0.5); - return { - args: [], - description: "Puts you in an emanate.", - perm: commands_1.Perm.admin, - data: { unitMapping: unitMapping }, - requirements: [], - handler: function (_a) { - var sender = _a.sender, outputSuccess = _a.outputSuccess; - var emanate = UnitTypes.emanate.spawn(sender.team(), sender.player.x, sender.player.y); - sender.player.unit(emanate); - unitMapping[sender.uuid] = emanate; - if (!config_1.Gamemode.sandbox()) - (0, utils_1.logAction)("spawned an emanate", sender); - outputSuccess("Spawned an emanate."); - } - }; - }), - updatemaps: { - args: [], - description: 'Attempt to fetch and update all map files', - perm: commands_1.Perm.trusted, - requirements: function (_a) { - var sender = _a.sender; - return [commands_1.Req.cooldownGlobal(config_1.Gamemode.testsrv() || sender.hasPerm("mod") ? 15000 : funcs_1.Duration.minutes(5))]; - }, - handler: function (_a) { - var output = _a.output, outputSuccess = _a.outputSuccess, outputFail = _a.outputFail; - output("Updating maps... (this may take a while)"); - (0, files_1.updateMaps)() - .then(function (changed) { - Log.info("Maps updated."); - if (changed) { - outputSuccess("Map update completed."); - Call.sendMessage("[orange]Maps have been updated. Run [white]/maps[] to view available maps."); - } - else { - outputSuccess("Map update completed; already up to date."); - } - }) - .catch(function (message) { - outputFail("Map update failed: ".concat(String(message))); - Log.err("Map updates failed: ".concat(String(message))); - }); - } - }, - clearfire: { - args: [], - description: "Clears all the fires.", - perm: commands_1.Perm.admin, - handler: function (_a) { - var output = _a.output, outputSuccess = _a.outputSuccess; - output("Removing fires..."); - var totalRemoved = 0; - Call.sendMessage("[scarlet][[Fire Department]:[yellow] Fires were reported. Trucks are en-route. Removing all fires shortly."); - Timer.schedule(function () { - totalRemoved += Groups.fire.size(); - Groups.fire.each(function (f) { return f.remove(); }); - Groups.fire.clear(); - }, 2, 0.1, 40); - Timer.schedule(function () { - outputSuccess("Removed ".concat(totalRemoved, " fires.")); - Call.sendMessage("[scarlet][[Fire Department]:[yellow] We've extinguished ".concat(totalRemoved, " fires.")); - }, 6.1); - } - }, - search: { - args: ["input:string"], - description: "Searches playerinfo by name, IP, or UUID.", - perm: commands_1.Perm.admin, - handler: function (_a) { - return __awaiter(this, arguments, void 0, function (_b) { - var fishP, info, matches, matches_1, displayMatches; - var input = _b.args.input, admins = _b.admins, output = _b.output, f = _b.f, sender = _b.sender; - return __generator(this, function (_c) { - switch (_c.label) { - case 0: - if (!globals_1.uuidPattern.test(input)) return [3 /*break*/, 1]; - fishP = players_1.FishPlayer.getById(input); - info = admins.getInfoOptional(input); - if (fishP == null && info == null) - (0, commands_1.fail)(f(templateObject_64 || (templateObject_64 = __makeTemplateObject(["No stored data matched uuid ", "."], ["No stored data matched uuid ", "."])), input)); - else if (fishP == null && info) - output(f(templateObject_65 || (templateObject_65 = __makeTemplateObject(["[accent]Found player info (but no fish player data) for uuid ", "\nLast name used: \"", "\" [gray](", ")[] [[", "]\nIPs used: ", ""], ["[accent]\\\nFound player info (but no fish player data) for uuid ", "\nLast name used: \"", "\" [gray](", ")[] [[", "]\nIPs used: ", ""])), input, info.plainLastName(), (0, funcs_1.escapeStringColorsClient)(info.lastName), info.names.map(funcs_1.escapeStringColorsClient).items.join(", "), info.ips.map(function (i) { return "[blue]".concat(i, "[]"); }).toString(", "))); - else if (fishP && info) - output(f(templateObject_66 || (templateObject_66 = __makeTemplateObject(["[accent]Found fish player data for uuid ", "\nLast name used: \"", "\" [gray](", ")[] [[", "]\nIPs used: ", ""], ["[accent]\\\nFound fish player data for uuid ", "\nLast name used: \"", "\" [gray](", ")[] [[", "]\nIPs used: ", ""])), input, fishP.name, (0, funcs_1.escapeStringColorsClient)(info.lastName), info.names.map(funcs_1.escapeStringColorsClient).items.join(", "), info.ips.map(function (i) { return "[blue]".concat(i, "[]"); }).toString(", "))); - else - (0, commands_1.fail)(f(templateObject_67 || (templateObject_67 = __makeTemplateObject(["Super weird edge case: found fish player data but no player info for uuid ", "."], ["Super weird edge case: found fish player data but no player info for uuid ", "."])), input)); - return [3 /*break*/, 5]; - case 1: - if (!globals_1.ipPattern.test(input)) return [3 /*break*/, 2]; - matches = admins.findByIPs(input); - if (matches.isEmpty()) - (0, commands_1.fail)(f(templateObject_68 || (templateObject_68 = __makeTemplateObject(["No stored data matched IP ", ""], ["No stored data matched IP ", ""])), input)); - output(f(templateObject_69 || (templateObject_69 = __makeTemplateObject(["[accent]Found ", " match", " for search \"", "\"."], ["[accent]Found ", " match", " for search \"", "\"."])), matches.size, matches.size == 1 ? "" : "es", input)); - matches.each(function (info) { return output(f(templateObject_70 || (templateObject_70 = __makeTemplateObject(["[accent]Player with uuid ", "\nLast name used: \"", "\" [gray](", ")[] [[", "]\nIPs used: ", ""], ["[accent]\\\nPlayer with uuid ", "\nLast name used: \"", "\" [gray](", ")[] [[", "]\nIPs used: ", ""])), info.id, info.plainLastName(), (0, funcs_1.escapeStringColorsClient)(info.lastName), info.names.map(funcs_1.escapeStringColorsClient).items.join(", "), info.ips.map(function (i) { return "[blue]".concat(i, "[]"); }).toString(", "))); }); - return [3 /*break*/, 5]; - case 2: - matches_1 = Vars.netServer.admins.searchNames(input); - if (matches_1.isEmpty()) - (0, commands_1.fail)(f(templateObject_71 || (templateObject_71 = __makeTemplateObject(["No stored data matched name ", ""], ["No stored data matched name ", ""])), input)); - output(f(templateObject_72 || (templateObject_72 = __makeTemplateObject(["[accent]Found ", " match", " for search \"", "\"."], ["[accent]Found ", " match", " for search \"", "\"."])), matches_1.size, matches_1.size == 1 ? "" : "es", input)); - displayMatches = function () { - matches_1.each(function (info) { return output(f(templateObject_73 || (templateObject_73 = __makeTemplateObject(["[accent]Player with uuid ", "\nLast name used: \"", "\" [gray](", ")[] [[", "]\nIPs used: ", ""], ["[accent]\\\nPlayer with uuid ", "\nLast name used: \"", "\" [gray](", ")[] [[", "]\nIPs used: ", ""])), info.id, info.plainLastName(), (0, funcs_1.escapeStringColorsClient)(info.lastName), info.names.map(funcs_1.escapeStringColorsClient).items.join(", "), info.ips.map(function (i) { return "[blue]".concat(i, "[]"); }).toString(", "))); }); - }; - if (!(matches_1.size > 20)) return [3 /*break*/, 4]; - return [4 /*yield*/, menus_1.Menu.confirm(sender, "Are you sure you want to view all ".concat(matches_1.size, " matches?"))]; - case 3: - _c.sent(); - _c.label = 4; - case 4: - displayMatches(); - _c.label = 5; - case 5: return [2 /*return*/]; - } - }); - }); - } - }, - peace: { - args: ["peace:boolean"], - description: "Toggles peaceful mode for sandbox.", - perm: commands_1.Perm.mod, - requirements: [commands_1.Req.mode('sandbox')], - handler: function (_a) { - var args = _a.args; - if (args.peace) { - globals_1.fishState.peacefulMode = true; - Groups.player.each(function (p) { - if (p.team() != Vars.state.rules.defaultTeam) { - p.team(Vars.state.rules.defaultTeam); - } - }); - Call.sendMessage("[[Sandbox] [green]Enabled peaceful mode."); - } - else { - globals_1.fishState.peacefulMode = false; - Call.sendMessage("[[Sandbox] [red]Disabled peaceful mode."); - } - }, - }, - effects: { - args: ["mode:string", "player:player?", "duration:time?"], - description: "Applies effects to a player's unit.", - perm: commands_1.Perm.admin.exceptModes({ - testsrv: commands_1.Perm.trusted, - }), - handler: function (_a) { - var _b, _c, _d; - var args = _a.args, sender = _a.sender, f = _a.f, outputSuccess = _a.outputSuccess; - if ((_b = args.player) === null || _b === void 0 ? void 0 : _b.hasPerm("blockTrolling")) - (0, commands_1.fail)(f(templateObject_74 || (templateObject_74 = __makeTemplateObject(["Player ", " is insufficiently trollable."], ["Player ", " is insufficiently trollable."])), args.player)); - if (args.player && !sender.canModerate(args.player, false)) - (0, commands_1.fail)("You do not have permission to perform moderation actions on this player."); - var target = (_c = args.player) !== null && _c !== void 0 ? _c : sender; - var unit = target.unit(); - if (!unit || unit.dead) - (0, commands_1.fail)(f(templateObject_75 || (templateObject_75 = __makeTemplateObject(["", "'s unit is dead."], ["", "'s unit is dead."])), target)); - var ticks = ((_d = args.duration) !== null && _d !== void 0 ? _d : 1e12) / 1000 * 60; - (0, utils_1.applyEffectMode)(args.mode, unit, ticks); - outputSuccess("".concat(args.mode === "clear" ? "Cleared" : "Applied", " effects.")); - if (!config_1.Gamemode.sandbox()) - (0, utils_1.logAction)("applied **".concat(args.mode, "** effects to"), sender, target); - } - }, - items: { - args: ["team:team", "item:item", "amount:number"], - description: "Gives items to a team.", - perm: commands_1.Perm.admin, - requirements: [commands_1.Req.integer("amount")], - handler: function (_a) { - var _b; - var _c = _a.args, team = _c.team, item = _c.item, amount = _c.amount, sender = _a.sender, outputSuccess = _a.outputSuccess, f = _a.f; - var core = (_b = team.data().cores.firstOpt()) !== null && _b !== void 0 ? _b : (0, commands_1.fail)(f(templateObject_76 || (templateObject_76 = __makeTemplateObject(["Team ", " has no cores."], ["Team ", " has no cores."])), team)); - core.items.add(item, amount); - outputSuccess(f(templateObject_77 || (templateObject_77 = __makeTemplateObject(["Gave ", " ", " to ", "."], ["Gave ", " ", " to ", "."])), amount, item, team)); - if (!config_1.Gamemode.sandbox()) - (0, utils_1.logAction)("gave ".concat(amount, " ").concat(item.localizedName.toLowerCase(), " to ").concat(team.name), sender); - } - }, - explosion: { - args: ["radius:number", "x:number", "y:number", "team:team?", "damage:number?", "damageMode:string?"], - description: "Causes an explosion at specified coordinates.", - perm: commands_1.Perm.admin, - handler: function (_a) { - var _b; - var _c = _a.args, radius = _c.radius, x = _c.x, y = _c.y, _d = _c.team, team = _d === void 0 ? Team.derelict : _d, _e = _c.damage, damage = _e === void 0 ? 1e12 : _e, _f = _c.damageMode, damageMode = _f === void 0 ? "both" : _f, outputSuccess = _a.outputSuccess; - var _g = __read((_b = (0, utils_1.match)(damageMode, { - air: [true, false], - ground: [false, true], - both: [true, true], - none: [false, false], - })) !== null && _b !== void 0 ? _b : (0, commands_1.fail)("Valid values of damageMode: air, ground, both, none"), 2), air = _g[0], ground = _g[1]; - if (radius > 100) - (0, commands_1.fail)("Maximum radius is 100"); - if (damage < 0) - Call.effect(Fx.dynamicSpikes, x * 8, y * 8, radius * 8, Pal.heal); - else - Call.effect(Fx.dynamicExplosion, x * 8, y * 8, Math.max(radius, 8) / 7, Color.white); - Damage.damage(team, x * 8, y * 8, radius * 8, damage, true, air, ground); - outputSuccess("Created an explosion at (".concat(x, ", ").concat(y, ").")); - } - }, - memorycorruption: { - args: [], - description: "Triggers a fake memory corruption prank.", - perm: commands_1.Perm.mod, - requirements: [commands_1.Req.cooldownGlobal(funcs_1.Duration.minutes(30))], - handler: function () { - (0, utils_1.definitelyRealMemoryCorruption)(); - } - }, - editor: { - args: ["editor:boolean"], - description: "Toggles the in-game editor mode.", - perm: commands_1.Perm.trusted, - requirements: [commands_1.Req.mode("testsrv"), commands_1.Req.cooldownGlobal(20000)], - handler: function (_a) { - var editor = _a.args.editor; - Vars.state.rules.editor = editor; - Call.setRules(Vars.state.rules); - } - }, - mapruns: { - args: ["map:map", "lowestHighscores:boolean?"], - description: "Displays all map runs for a selected map, and allows deleting invalid/cheated runs.", - perm: commands_1.Perm.admin, - handler: function (_a) { - return __awaiter(this, arguments, void 0, function (_b) { - var fmap, _c, initialLength, runs, _d, index, _, deleted; - var _e; - var _f = _b.args, map = _f.map, lowestHighscores = _f.lowestHighscores, sender = _b.sender, outputSuccess = _b.outputSuccess; - return __generator(this, function (_g) { - switch (_g.label) { - case 0: - fmap = (_e = maps_1.FMap.getCreate(map)) !== null && _e !== void 0 ? _e : (0, commands_1.fail)("Map data is still loading, please try again."); - if (!(lowestHighscores !== null && lowestHighscores !== void 0)) return [3 /*break*/, 1]; - _c = lowestHighscores; - return [3 /*break*/, 3]; - case 1: return [4 /*yield*/, menus_1.Menu.buttons(sender, "[accent]Map runs", "Select a view", [ - [{ data: true, text: "Lowest highscores" }], - [{ data: false, text: "All runs" }], - ], { - includeCancel: true, - onCancel: "reject" - })]; - case 2: - _c = (lowestHighscores = _g.sent()); - _g.label = 3; - case 3: - _c; - initialLength = fmap.runs.length; - runs = fmap.runs.slice(); - if (lowestHighscores) - runs = runs.filter(function (r) { return r.success; }) - .sort(function (a, b) { return a.duration() - b.duration(); }); - return [4 /*yield*/, menus_1.Menu.textPages(sender, runs.map(function (r) { return [ - (0, utils_1.formatTimestamp)(r.startTime), - function () { - return "Duration: ".concat((0, utils_1.formatTime)(r.duration()), "\nMax player count: ").concat(r.maxPlayerCount, "\nOutcome: ").concat(r.outcome()[1], "\nWave: ").concat(r.wave); - } - ]; }), ["[scarlet]\uE86FDelete"], { - onCancel: "reject" - })]; - case 4: - _d = __read.apply(void 0, [_g.sent(), 2]), index = _d[0], _ = _d[1]; - return [4 /*yield*/, menus_1.Menu.confirmDangerous(sender, "Are you sure you want to delete this map run? This action is irreversible.")]; - case 5: - _g.sent(); - if (initialLength != fmap.runs.length) - (0, commands_1.fail)("Someone else deleted a run, please try again."); - deleted = fmap.runs.splice(index, 1)[0]; - outputSuccess("Deleted run (".concat((0, utils_1.formatTimestamp)(deleted.startTime), ") with duration ").concat((0, utils_1.formatTime)(deleted.duration()), ".")); - return [2 /*return*/]; - } - }); - }); - } - } -}); -var templateObject_1, templateObject_2, templateObject_3, templateObject_4, templateObject_5, templateObject_6, templateObject_7, templateObject_8, templateObject_9, templateObject_10, templateObject_11, templateObject_12, templateObject_13, templateObject_14, templateObject_15, templateObject_16, templateObject_17, templateObject_18, templateObject_19, templateObject_20, templateObject_21, templateObject_22, templateObject_23, templateObject_24, templateObject_25, templateObject_26, templateObject_27, templateObject_28, templateObject_29, templateObject_30, templateObject_31, templateObject_32, templateObject_33, templateObject_34, templateObject_35, templateObject_36, templateObject_37, templateObject_38, templateObject_39, templateObject_40, templateObject_41, templateObject_42, templateObject_43, templateObject_44, templateObject_45, templateObject_46, templateObject_47, templateObject_48, templateObject_49, templateObject_50, templateObject_51, templateObject_52, templateObject_53, templateObject_54, templateObject_55, templateObject_56, templateObject_57, templateObject_58, templateObject_59, templateObject_60, templateObject_61, templateObject_62, templateObject_63, templateObject_64, templateObject_65, templateObject_66, templateObject_67, templateObject_68, templateObject_69, templateObject_70, templateObject_71, templateObject_72, templateObject_73, templateObject_74, templateObject_75, templateObject_76, templateObject_77; +"use strict"; +/* +Copyright © BalaM314, 2026. All Rights Reserved. +This file contains the in-game chat commands that can be run by trusted staff. +*/ +var __makeTemplateObject = (this && this.__makeTemplateObject) || function (cooked, raw) { + if (Object.defineProperty) { Object.defineProperty(cooked, "raw", { value: raw }); } else { cooked.raw = raw; } + return cooked; +}; +var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + var desc = Object.getOwnPropertyDescriptor(m, k); + if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { + desc = { enumerable: true, get: function() { return m[k]; } }; + } + Object.defineProperty(o, k2, desc); +}) : (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + o[k2] = m[k]; +})); +var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { + Object.defineProperty(o, "default", { enumerable: true, value: v }); +}) : function(o, v) { + o["default"] = v; +}); +var __importStar = (this && this.__importStar) || (function () { + var ownKeys = function(o) { + ownKeys = Object.getOwnPropertyNames || function (o) { + var ar = []; + for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k; + return ar; + }; + return ownKeys(o); + }; + return function (mod) { + if (mod && mod.__esModule) return mod; + var result = {}; + if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]); + __setModuleDefault(result, mod); + return result; + }; +})(); +var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { + function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } + return new (P || (P = Promise))(function (resolve, reject) { + function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } + function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } + function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } + step((generator = generator.apply(thisArg, _arguments || [])).next()); + }); +}; +var __generator = (this && this.__generator) || function (thisArg, body) { + var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g = Object.create((typeof Iterator === "function" ? Iterator : Object).prototype); + return g.next = verb(0), g["throw"] = verb(1), g["return"] = verb(2), typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g; + function verb(n) { return function (v) { return step([n, v]); }; } + function step(op) { + if (f) throw new TypeError("Generator is already executing."); + while (g && (g = 0, op[0] && (_ = 0)), _) try { + if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t; + if (y = 0, t) op = [op[0] & 2, t.value]; + switch (op[0]) { + case 0: case 1: t = op; break; + case 4: _.label++; return { value: op[1], done: false }; + case 5: _.label++; y = op[1]; op = [0]; continue; + case 7: op = _.ops.pop(); _.trys.pop(); continue; + default: + if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; } + if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; } + if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; } + if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; } + if (t[2]) _.ops.pop(); + _.trys.pop(); continue; + } + op = body.call(thisArg, _); + } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; } + if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true }; + } +}; +var __read = (this && this.__read) || function (o, n) { + var m = typeof Symbol === "function" && o[Symbol.iterator]; + if (!m) return o; + var i = m.call(o), r, ar = [], e; + try { + while ((n === void 0 || n-- > 0) && !(r = i.next()).done) ar.push(r.value); + } + catch (error) { e = { error: error }; } + finally { + try { + if (r && !r.done && (m = i["return"])) m.call(i); + } + finally { if (e) throw e.error; } + } + return ar; +}; +var __spreadArray = (this && this.__spreadArray) || function (to, from, pack) { + if (pack || arguments.length === 2) for (var i = 0, l = from.length, ar; i < l; i++) { + if (ar || !(i in from)) { + if (!ar) ar = Array.prototype.slice.call(from, 0, i); + ar[i] = from[i]; + } + } + return to.concat(ar || Array.prototype.slice.call(from)); +}; +var __values = (this && this.__values) || function(o) { + var s = typeof Symbol === "function" && Symbol.iterator, m = s && o[s], i = 0; + if (m) return m.call(o); + if (o && typeof o.length === "number") return { + next: function () { + if (o && i >= o.length) o = void 0; + return { value: o && o[i++], done: !o }; + } + }; + throw new TypeError(s ? "Object is not iterable." : "Symbol.iterator is not defined."); +}; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.commands = void 0; +var api = __importStar(require("/api")); +var config_1 = require("/config"); +var files_1 = require("/files"); +var fjsContext = __importStar(require("/fjsContext")); +var commands_1 = require("/frameworks/commands"); +var menus_1 = require("/frameworks/menus"); +var funcs_1 = require("/funcs"); +var globals_1 = require("/globals"); +var maps_1 = require("/maps"); +var players_1 = require("/players"); +var ranks_1 = require("/ranks"); +var utils_1 = require("/utils"); +exports.commands = (0, commands_1.commandList)({ + warn: { + args: ['player:player', 'message:string?'], + description: 'Sends the player a warning (menu popup).', + perm: commands_1.Perm.warn, + requirements: [commands_1.Req.cooldown(3000)], + handler: function (_a) { + var _b; + var args = _a.args, sender = _a.sender, outputSuccess = _a.outputSuccess, f = _a.f; + if (args.player.hasPerm("blockTrolling")) + (0, commands_1.fail)(f(templateObject_1 || (templateObject_1 = __makeTemplateObject(["Player ", " is insufficiently trollable."], ["Player ", " is insufficiently trollable."])), args.player)); + var message = (_b = args.message) !== null && _b !== void 0 ? _b : "You have been warned. I suggest you stop what you're doing"; + void menus_1.Menu.menu('Warning', message, ["[green]Accept"], args.player, { onCancel: 'null' }) + .then(function () { return outputSuccess('Player acknowledged the warning.'); }); + (0, utils_1.logAction)('warned', sender, args.player, message); + outputSuccess(f(templateObject_2 || (templateObject_2 = __makeTemplateObject(["Warned player ", " for \"", "\""], ["Warned player ", " for \"", "\""])), args.player, message)); + } + }, + mute: { + args: ['player:player'], + description: 'Stops a player from chatting.', + perm: commands_1.Perm.mod, + requirements: [commands_1.Req.moderate("player")], + handler: function (_a) { + return __awaiter(this, arguments, void 0, function (_b) { + var args = _b.args, sender = _b.sender, outputSuccess = _b.outputSuccess, f = _b.f; + return __generator(this, function (_c) { + switch (_c.label) { + case 0: + if (args.player.muted) + (0, commands_1.fail)(f(templateObject_3 || (templateObject_3 = __makeTemplateObject(["Player ", " is already muted."], ["Player ", " is already muted."])), args.player)); + return [4 /*yield*/, args.player.mute(sender)]; + case 1: + _c.sent(); + (0, utils_1.logAction)('muted', sender, args.player); + outputSuccess(f(templateObject_4 || (templateObject_4 = __makeTemplateObject(["Muted player ", "."], ["Muted player ", "."])), args.player)); + return [2 /*return*/]; + } + }); + }); + } + }, + unmute: { + args: ['player:player'], + description: 'Unmutes a player', + perm: commands_1.Perm.mod, + handler: function (_a) { + return __awaiter(this, arguments, void 0, function (_b) { + var args = _b.args, sender = _b.sender, outputSuccess = _b.outputSuccess, f = _b.f; + return __generator(this, function (_c) { + switch (_c.label) { + case 0: + if (!args.player.muted && args.player.autoflagged) + (0, commands_1.fail)(f(templateObject_5 || (templateObject_5 = __makeTemplateObject(["Player ", " is not muted, but they are autoflagged. You probably want to free them with /free."], ["Player ", " is not muted, but they are autoflagged. You probably want to free them with /free."])), args.player)); + if (!args.player.muted) + (0, commands_1.fail)(f(templateObject_6 || (templateObject_6 = __makeTemplateObject(["Player ", " is not muted."], ["Player ", " is not muted."])), args.player)); + return [4 /*yield*/, args.player.unmute(sender)]; + case 1: + _c.sent(); + (0, utils_1.logAction)('unmuted', sender, args.player); + outputSuccess(f(templateObject_7 || (templateObject_7 = __makeTemplateObject(["Unmuted player ", "."], ["Unmuted player ", "."])), args.player)); + return [2 /*return*/]; + } + }); + }); + } + }, + kick: { + args: ["player:player", "duration:time?", "reason:string?"], + description: 'Kick a player with optional reason.', + perm: commands_1.Perm.mod, + requirements: [commands_1.Req.moderate("player")], + handler: function (_a) { + var _b, _c, _d; + var args = _a.args, outputSuccess = _a.outputSuccess, f = _a.f, sender = _a.sender; + if (!sender.hasPerm("admin") && args.duration && args.duration > funcs_1.Duration.hours(6)) + (0, commands_1.fail)("Maximum kick duration is 6 hours."); + var reason = (_b = args.reason) !== null && _b !== void 0 ? _b : "A staff member did not like your actions."; + var duration = (_c = args.duration) !== null && _c !== void 0 ? _c : 60000; + args.player.kick(reason, duration); + (0, utils_1.logAction)("kicked", sender, args.player, (_d = args.reason) !== null && _d !== void 0 ? _d : undefined, duration); + if (duration > 60000) + args.player.setPunishedIP(config_1.stopAntiEvadeTime); + outputSuccess(f(templateObject_8 || (templateObject_8 = __makeTemplateObject(["Kicked player ", " for ", " with reason \"", "\""], ["Kicked player ", " for ", " with reason \"", "\""])), args.player, (0, utils_1.formatTime)(duration), reason)); + } + }, + pardon: { + args: ["player:offlinePlayer"], + description: 'Pardons a votekicked player.', + perm: commands_1.Perm.mod, + requirements: [commands_1.Req.moderate("player")], + handler: function (_a) { + var player = _a.args.player, admins = _a.admins, outputSuccess = _a.outputSuccess, f = _a.f; + var info = admins.getInfo(player.uuid); + if (Time.millis() > info.lastKicked && !admins.kickedIPs.containsKey(info.lastIP)) + (0, commands_1.fail)("That player is not kicked."); + info.lastKicked = 0; + admins.kickedIPs.remove(info.lastIP); + outputSuccess(f(templateObject_9 || (templateObject_9 = __makeTemplateObject(["Pardoned player ", "."], ["Pardoned player ", "."])), player)); + } + }, + stop: { + args: ['player:player', "time:time?", "message:string?"], + description: 'Stops a player.', + perm: commands_1.Perm.mod, + requirements: [commands_1.Req.moderate("player", true)], + handler: function (_a) { + return __awaiter(this, arguments, void 0, function (_b) { + var previousTime, time; + var _c, _d, _e, _f; + var args = _b.args, sender = _b.sender, outputSuccess = _b.outputSuccess, f = _b.f; + return __generator(this, function (_g) { + switch (_g.label) { + case 0: + if (!args.player.marked()) return [3 /*break*/, 2]; + //overload: overwrite stoptime + if (!args.time) + (0, commands_1.fail)(f(templateObject_10 || (templateObject_10 = __makeTemplateObject(["Player ", " is already marked."], ["Player ", " is already marked."])), args.player)); + previousTime = (0, utils_1.formatTimeRelative)(args.player.unmarkTime, true); + return [4 /*yield*/, args.player.updateStopTime(args.time)]; + case 1: + _g.sent(); + outputSuccess(f(templateObject_11 || (templateObject_11 = __makeTemplateObject(["Player ", "'s stop time has been updated to ", " (was ", ")."], ["Player ", "'s stop time has been updated to ", " (was ", ")."])), args.player, (0, utils_1.formatTime)(args.time), previousTime)); + (0, utils_1.logAction)("updated stop time of", sender, args.player, (_c = args.message) !== null && _c !== void 0 ? _c : undefined, args.time); + return [3 /*break*/, 4]; + case 2: + time = (_d = args.time) !== null && _d !== void 0 ? _d : (0, utils_1.untilForever)(); + if (time + Date.now() > globals_1.maxTime) + (0, commands_1.fail)("Error: time too high."); + return [4 /*yield*/, args.player.stop(sender, time, (_e = args.message) !== null && _e !== void 0 ? _e : undefined)]; + case 3: + _g.sent(); + (0, utils_1.logAction)('stopped', sender, args.player, (_f = args.message) !== null && _f !== void 0 ? _f : undefined, time); + //TODO outputGlobal() + Call.sendMessage("[orange]Player \"".concat(args.player.prefixedName, "[orange]\" has been marked for ").concat((0, utils_1.formatTime)(time)).concat(args.message ? " with reason: [white]".concat(args.message, "[]") : "", ".")); + _g.label = 4; + case 4: return [2 /*return*/]; + } + }); + }); + } + }, + free: { + args: ['player:player'], + description: 'Frees a player.', + perm: commands_1.Perm.mod, + handler: function (_a) { + return __awaiter(this, arguments, void 0, function (_b) { + var args = _b.args, sender = _b.sender, outputSuccess = _b.outputSuccess, outputFail = _b.outputFail, f = _b.f; + return __generator(this, function (_c) { + switch (_c.label) { + case 0: + if (!args.player.marked()) return [3 /*break*/, 2]; + return [4 /*yield*/, args.player.free(sender)]; + case 1: + _c.sent(); + (0, utils_1.logAction)('freed', sender, args.player); + outputSuccess(f(templateObject_12 || (templateObject_12 = __makeTemplateObject(["Player ", " has been unmarked."], ["Player ", " has been unmarked."])), args.player)); + return [3 /*break*/, 3]; + case 2: + if (args.player.autoflagged) { + args.player.autoflagged = false; + args.player.sendMessage("[yellow]You have been freed! Enjoy!"); + args.player.updateName(); + args.player.forceRespawn(); + outputSuccess(f(templateObject_13 || (templateObject_13 = __makeTemplateObject(["Player ", " has been unflagged."], ["Player ", " has been unflagged."])), args.player)); + } + else { + outputFail(f(templateObject_14 || (templateObject_14 = __makeTemplateObject(["Player ", " is not marked or autoflagged."], ["Player ", " is not marked or autoflagged."])), args.player)); + } + _c.label = 3; + case 3: return [2 /*return*/]; + } + }); + }); + } + }, + setrank: { + args: ["player:player", "rank:rank"], + description: "Set a player's rank.", + perm: commands_1.Perm.mod, + requirements: [commands_1.Req.moderate("player")], + handler: function (_a) { + return __awaiter(this, arguments, void 0, function (_b) { + var _c = _b.args, rank = _c.rank, player = _c.player, outputSuccess = _b.outputSuccess, f = _b.f, sender = _b.sender; + return __generator(this, function (_d) { + switch (_d.label) { + case 0: + if (rank.level >= sender.rank.level) + (0, commands_1.fail)(f(templateObject_15 || (templateObject_15 = __makeTemplateObject(["You do not have permission to promote players to rank ", ", because your current rank is ", ""], ["You do not have permission to promote players to rank ", ", because your current rank is ", ""])), rank, sender.rank)); + if (rank == ranks_1.Rank.pi && !config_1.Mode.localDebug) + (0, commands_1.fail)(f(templateObject_16 || (templateObject_16 = __makeTemplateObject(["Rank ", " is immutable."], ["Rank ", " is immutable."])), rank)); + if (player.immutable() && !config_1.Mode.localDebug) + (0, commands_1.fail)(f(templateObject_17 || (templateObject_17 = __makeTemplateObject(["Player ", " is immutable."], ["Player ", " is immutable."])), player)); + if (!(player == sender && rank.level < sender.rank.level)) return [3 /*break*/, 2]; + return [4 /*yield*/, menus_1.Menu.confirmDangerous(sender, "[red] ARE YOU SURE YOU WANT TO SELF DEMOTE. THIS ACTION CANNOT BE UNDONE!")]; + case 1: + _d.sent(); + _d.label = 2; + case 2: return [4 /*yield*/, player.setRank(rank)]; + case 3: + _d.sent(); + (0, utils_1.logAction)("set rank to ".concat(rank.name, " for"), sender, player); + outputSuccess(f(templateObject_18 || (templateObject_18 = __makeTemplateObject(["Set rank of player ", " to ", ""], ["Set rank of player ", " to ", ""])), player, rank)); + return [2 /*return*/]; + } + }); + }); + } + }, + setflag: { + args: ["player:player", "flag:roleflag", "value:boolean"], + description: "Set a player's role flags.", + perm: commands_1.Perm.mod, + requirements: [commands_1.Req.moderate("player")], + handler: function (_a) { + return __awaiter(this, arguments, void 0, function (_b) { + var _c = _b.args, flag = _c.flag, player = _c.player, value = _c.value, sender = _b.sender, outputSuccess = _b.outputSuccess, f = _b.f; + return __generator(this, function (_d) { + switch (_d.label) { + case 0: + if (!sender.hasPerm("admin") && !flag.assignableByModerators) + (0, commands_1.fail)(f(templateObject_19 || (templateObject_19 = __makeTemplateObject(["You do not have permission to change the value of role flag ", ""], ["You do not have permission to change the value of role flag ", ""])), flag)); + return [4 /*yield*/, player.setFlag(flag, value)]; + case 1: + _d.sent(); + (0, utils_1.logAction)("set roleflag ".concat(flag.name, " to ").concat(value, " for"), sender, player); + outputSuccess(f(templateObject_20 || (templateObject_20 = __makeTemplateObject(["Set role flag ", " of player ", " to ", ""], ["Set role flag ", " of player ", " to ", ""])), flag, player, value)); + return [2 /*return*/]; + } + }); + }); + } + }, + murder: { + args: [], + description: 'Kills all ohno units', + perm: commands_1.Perm.mod, + customUnauthorizedMessage: "[yellow]You're a [scarlet]monster[].", + handler: function (_a) { + var output = _a.output, f = _a.f, allCommands = _a.allCommands; + var Ohnos = allCommands["ohno"].data; //this is not ideal... TODO commit omega shenanigans + var numOhnos = Ohnos.amount(); + Ohnos.killAll(); + output(f(templateObject_21 || (templateObject_21 = __makeTemplateObject(["[orange]You massacred ", " helpless ohno crawlers."], ["[orange]You massacred ", " helpless ohno crawlers."])), numOhnos)); + } + }, + stop_offline: { + args: ["time:time?", "name:string?"], + description: "Stops an offline player.", + perm: commands_1.Perm.mod, + handler: function (_a) { + return __awaiter(this, arguments, void 0, function (_b) { + function stop(option, time) { + return __awaiter(this, void 0, void 0, function () { + var fishP; + return __generator(this, function (_a) { + switch (_a.label) { + case 0: + fishP = players_1.FishPlayer.getFromInfo(option); + if (!sender.canModerate(fishP, true)) return [3 /*break*/, 2]; + (0, utils_1.logAction)(fishP.marked() ? time == 1000 ? "freed" : "updated stop time of" : "stopped", sender, option, undefined, time); + return [4 /*yield*/, fishP.stop(sender, time)]; + case 1: + _a.sent(); + outputSuccess(f(templateObject_22 || (templateObject_22 = __makeTemplateObject(["Player ", " was marked for ", "."], ["Player ", " was marked for ", "."])), option, (0, utils_1.formatTime)(time))); + return [3 /*break*/, 3]; + case 2: + outputFail("You do not have permission to stop this player."); + _a.label = 3; + case 3: return [2 /*return*/]; + } + }); + }); + } + var maxPlayers, info, possiblePlayers, exactPlayers, score_1, optionPlayer, _c, _d, _e; + var _f, _g; + var args = _b.args, sender = _b.sender, outputFail = _b.outputFail, outputSuccess = _b.outputSuccess, f = _b.f, admins = _b.admins; + return __generator(this, function (_h) { + switch (_h.label) { + case 0: + maxPlayers = 60; + if (!(args.name && globals_1.uuidPattern.test(args.name))) return [3 /*break*/, 4]; + info = admins.getInfoOptional(args.name); + if (!(info != null)) return [3 /*break*/, 2]; + return [4 /*yield*/, stop(info, (_f = args.time) !== null && _f !== void 0 ? _f : (0, utils_1.untilForever)())]; + case 1: + _h.sent(); + return [3 /*break*/, 3]; + case 2: + outputFail(f(templateObject_23 || (templateObject_23 = __makeTemplateObject(["Unknown UUID ", ""], ["Unknown UUID ", ""])), args.name)); + _h.label = 3; + case 3: return [2 /*return*/]; + case 4: + if (args.name) { + possiblePlayers = (0, funcs_1.setToArray)(admins.searchNames(args.name)); + if (possiblePlayers.length > maxPlayers) { + exactPlayers = (0, funcs_1.setToArray)(admins.findByName(args.name)); + if (exactPlayers.length > 0) { + possiblePlayers = exactPlayers; + } + else { + (0, commands_1.fail)("Too many players with that name."); + } + } + else if (possiblePlayers.length == 0) { + (0, commands_1.fail)("No players with that name were found."); + } + score_1 = function (data) { + var fishP = players_1.FishPlayer.getById(data.id); + if (fishP) + return fishP.lastJoined; + return -data.timesJoined; + }; + possiblePlayers.sort(function (a, b) { return score_1(b) - score_1(a); }); + } + else { + possiblePlayers = players_1.FishPlayer.recentLeaves.map(function (p) { return p.info(); }); + } + return [4 /*yield*/, menus_1.Menu.menu("Stop", "Choose a player to mark", possiblePlayers, sender, { + includeCancel: true, + optionStringifier: function (p) { return p.lastName; } + })]; + case 5: + optionPlayer = _h.sent(); + if (!((_g = args.time) !== null && _g !== void 0)) return [3 /*break*/, 6]; + _c = _g; + return [3 /*break*/, 8]; + case 6: + _d = args; + _e = utils_1.match; + return [4 /*yield*/, menus_1.Menu.menu("Stop", "Select stop time", ["2 days", "7 days", "30 days", "forever"], sender)]; + case 7: + _c = (_d.time = _e.apply(void 0, [_h.sent(), { + "2 days": funcs_1.Duration.days(2), + "7 days": funcs_1.Duration.days(7), + "30 days": funcs_1.Duration.days(30), + "forever": globals_1.maxTime - Date.now() - 10000, + }])); + _h.label = 8; + case 8: + _c; + return [4 /*yield*/, stop(optionPlayer, args.time)]; + case 9: + _h.sent(); + return [2 /*return*/]; + } + }); + }); + } + }, + mute_offline: { + args: ["name:string?"], + description: "Mutes an offline player.", + perm: commands_1.Perm.mod, + handler: function (_a) { + return __awaiter(this, arguments, void 0, function (_b) { + function mute(option) { + return __awaiter(this, void 0, void 0, function () { + var fishP; + return __generator(this, function (_a) { + switch (_a.label) { + case 0: + fishP = players_1.FishPlayer.getFromInfo(option); + if (!sender.canModerate(fishP, true)) + (0, commands_1.fail)("You do not have permission to mute this player."); + return [4 /*yield*/, menus_1.Menu.confirm(sender, "Are you sure you want to ".concat(fishP.muted ? "unmute" : "mute", " player ").concat(option.lastName, "?"), { + title: "Mute Offine Confirmation", + confirmText: "[green]Yes, ".concat(fishP.muted ? "unmute" : "mute", " them"), + })]; + case 1: + _a.sent(); + (0, utils_1.logAction)(fishP.muted ? "unmuted" : "muted", sender, fishP); + if (!fishP.muted) return [3 /*break*/, 3]; + return [4 /*yield*/, fishP.unmute(sender)]; + case 2: + _a.sent(); + return [3 /*break*/, 5]; + case 3: return [4 /*yield*/, fishP.mute(sender)]; + case 4: + _a.sent(); + _a.label = 5; + case 5: + outputSuccess("".concat(fishP.muted ? "Muted" : "Unmuted", " ").concat(option.lastName, ".")); + return [2 /*return*/]; + } + }); + }); + } + var maxPlayers, info, possiblePlayers, exactPlayers, score_2, option; + var _c; + var args = _b.args, sender = _b.sender, outputSuccess = _b.outputSuccess, f = _b.f, admins = _b.admins; + return __generator(this, function (_d) { + switch (_d.label) { + case 0: + maxPlayers = 300; + if (!(args.name && globals_1.uuidPattern.test(args.name))) return [3 /*break*/, 2]; + info = (_c = admins.getInfoOptional(args.name)) !== null && _c !== void 0 ? _c : (0, commands_1.fail)(f(templateObject_24 || (templateObject_24 = __makeTemplateObject(["Unknown UUID ", ""], ["Unknown UUID ", ""])), args.name)); + return [4 /*yield*/, mute(info)]; + case 1: + _d.sent(); + return [2 /*return*/]; + case 2: + if (args.name) { + possiblePlayers = (0, funcs_1.setToArray)(admins.searchNames(args.name)); + if (possiblePlayers.length > maxPlayers) { + exactPlayers = (0, funcs_1.setToArray)(admins.findByName(args.name)); + if (exactPlayers.length > 0) { + possiblePlayers = exactPlayers; + } + else { + (0, commands_1.fail)("Too many players with that name."); + } + } + else if (possiblePlayers.length == 0) { + (0, commands_1.fail)("No players with that name were found."); + } + score_2 = function (data) { + var fishP = players_1.FishPlayer.getById(data.id); + if (fishP) + return fishP.lastJoined; + return -data.timesJoined; + }; + possiblePlayers.sort(function (a, b) { return score_2(b) - score_2(a); }); + } + else { + possiblePlayers = players_1.FishPlayer.recentLeaves.map(function (p) { return p.info(); }); + } + return [4 /*yield*/, menus_1.Menu.pagedList(sender, "Mute", "Choose a player to mute", possiblePlayers, { + optionStringifier: function (p) { return p.lastName; } + })]; + case 3: + option = _d.sent(); + return [4 /*yield*/, mute(option)]; + case 4: + _d.sent(); + return [2 /*return*/]; + } + }); + }); + } + }, + restart: { + args: ["time:number?"], + perm: commands_1.Perm.admin, + description: "Restarts the server.", + handler: function (_a) { + var _b; + var time = _a.args.time; + (_b = globals_1.fishState.restartLoopTask) === null || _b === void 0 ? void 0 : _b.cancel(); + if (Groups.player.isEmpty()) { + if (time == undefined) { + Log.info("Restarting immediately as no players are online."); + time !== null && time !== void 0 ? time : (time = 0); + } + } + else if (config_1.Gamemode.pvp()) { + time !== null && time !== void 0 ? time : (time = -1); + Log.info("PVP: restart will occur at the end of the current match. Specify a time to override, but &rthat would interrupt the current pvp match, and players would lose their teams.&fr"); + } + else { + time !== null && time !== void 0 ? time : (time = 60); + } + if (time == -1) { + Call.sendMessage("[accent]---[[[coral]+++[]]---\n[accent]Server restart queued. The server will restart after the current match is over.[]\n[accent]---[[[coral]+++[]]---"); + globals_1.fishState.restartQueued = true; + } + else { + if (time < 0 || time > 100) + (0, commands_1.fail)("Invalid time: out of valid range."); + (0, utils_1.serverRestartLoop)(time); + if (config_1.Gamemode.pvp()) { + Call.sendMessage("[accent]---[[[coral]+++[]]---\n[accent]Server restart imminent. [green]We'll be back after 15 seconds.[]\n[accent]---[[[coral]+++[]]---"); + } + else { + Call.sendMessage("[accent]---[[[coral]+++[]]---\n[accent]Server restart imminent. [green]We'll be back with 20 seconds of downtime, and all progress will be saved.[]\n[accent]---[[[coral]+++[]]---"); + } + } + } + }, + history: { + args: ["player:player"], + description: "Shows moderation history for a player.", + perm: commands_1.Perm.mod, + handler: function (_a) { + var args = _a.args, output = _a.output, f = _a.f; + if (args.player.history && args.player.history.length > 0) { + output("[yellow]_______________Player history_______________\n\n" + + (args.player).history.sort(function (a, b) { return a.time - b.time; }).map(function (e) { + return "".concat(e.by, " [yellow]").concat(e.action, " ").concat(args.player.prefixedName, " [white]").concat((0, utils_1.formatTimeRelative)(e.time)); + }).join("\n")); + } + else { + output(f(templateObject_25 || (templateObject_25 = __makeTemplateObject(["[yellow]No history was found for player ", "."], ["[yellow]No history was found for player ", "."])), args.player)); + } + } + }, + save: { + args: [], + description: "Saves the game state.", + perm: commands_1.Perm.mod, + handler: function (_a) { + var outputSuccess = _a.outputSuccess; + players_1.FishPlayer.saveAll(); + players_1.FishPlayer.uploadAll(); + globals_1.FishEvents.fire("saveData", []); + var file = Vars.saveDirectory.child("1.".concat(Vars.saveExtension)); + SaveIO.save(file); + outputSuccess("Game saved."); + } + }, + wave: { + args: ["wave:number"], + description: "Sets the wave number.", + perm: commands_1.Perm.admin, + requirements: [commands_1.Req.positiveInteger("wave")], + handler: function (_a) { + var args = _a.args, outputSuccess = _a.outputSuccess, f = _a.f; + Vars.state.wave = args.wave; + outputSuccess(f(templateObject_26 || (templateObject_26 = __makeTemplateObject(["Set wave to ", ""], ["Set wave to ", ""])), Vars.state.wave)); + } + }, + label: { + args: ["time:time", "message:string"], + description: "Places a label at your position for a specified amount of time.", + perm: commands_1.Perm.mod, + handler: function (_a) { + var _b; + var args = _a.args, sender = _a.sender, outputSuccess = _a.outputSuccess, f = _a.f; + if (args.time > funcs_1.Duration.hours(10)) + (0, commands_1.fail)("Time must be less than 10 hours."); + var unit = (_b = sender.unit()) !== null && _b !== void 0 ? _b : (0, commands_1.fail)("You must be in a unit to use this command."); + var timeRemaining = args.time / 1000; + var labelx = unit.x; + var labely = unit.y; + var task = Timer.schedule(function () { + if (timeRemaining > 0) { + var timeseconds = timeRemaining % 60; + var timeminutes = (timeRemaining - timeseconds) / 60; + Call.label("".concat(sender.name, "\n\n[white]").concat(args.message, "\n\n[acid]").concat(timeminutes.toString().padStart(2, "0"), ":").concat(timeseconds.toString().padStart(2, "0")), 1, labelx, labely); + timeRemaining--; + } + }, 0, 1, args.time); + globals_1.fishState.labels.push({ x: labelx, y: labely, task: task }); + outputSuccess(f(templateObject_27 || (templateObject_27 = __makeTemplateObject(["Placed label \"", "\" for ", " seconds."], ["Placed label \"", "\" for ", " seconds."])), args.message, timeRemaining)); + } + }, + labelsticky: { + args: ["time:time", "message:string"], + description: "Places a label at the bottom left corner of everyone's screen.", + perm: commands_1.Perm.admin, + handler: function (_a) { + var args = _a.args, outputSuccess = _a.outputSuccess, f = _a.f; + if (args.time > funcs_1.Duration.hours(10)) + (0, commands_1.fail)("Time must be less than 10 hours."); + var timeRemaining = args.time / 1000; + var task = Timer.schedule(function () { + if (timeRemaining > 0) { + Call.label(args.message, 5, NaN, NaN); + timeRemaining -= 5; + } + }, 0, 5, Math.ceil(args.time / 5)); + globals_1.fishState.labels.push({ task: task, x: null, y: null }); + outputSuccess(f(templateObject_28 || (templateObject_28 = __makeTemplateObject(["Placed label \"", "\" for ", " seconds."], ["Placed label \"", "\" for ", " seconds."])), args.message, timeRemaining)); + } + }, + clearlabels: { + args: [], + description: "Removes all labels.", + perm: commands_1.Perm.mod, + handler: function (_a) { + var outputSuccess = _a.outputSuccess; + if (globals_1.fishState.labels.length == 0) + (0, commands_1.fail)("No labels found."); + globals_1.fishState.labels.forEach(function (l) { return l.task.cancel(); }); + outputSuccess("Removed all labels."); + } + }, + clearlabel: { + args: ["sticky:boolean?"], + description: "Removes the closest label, or sticky label if specified", + perm: commands_1.Perm.mod, + handler: function (_a) { + var _b; + var _c = _a.args.sticky, sticky = _c === void 0 ? false : _c, sender = _a.sender, outputSuccess = _a.outputSuccess; + if (globals_1.fishState.labels.length == 0) + (0, commands_1.fail)("No labels found."); + var label; + if (sticky) { + var index = globals_1.fishState.labels.findIndex(function (l) { return l.x == null; }); + if (index == -1) + (0, commands_1.fail)("No sticky label found."); + label = globals_1.fishState.labels.splice(index, 1)[0]; + } + else { + var unit_1 = (_b = sender.unit()) !== null && _b !== void 0 ? _b : (0, commands_1.fail)("Cannot remove the closest label because you are dead."); + var dist_1 = function (label) { + if (label.x == null || label.y == null) + return Infinity; + return Mathf.dst(label.x, label.y, unit_1.x, unit_1.y); + }; + var index = __spreadArray([], __read(globals_1.fishState.labels.entries()), false).reduce(function (a, b) { return dist_1(a[1]) < dist_1(b[1]) ? a : b; })[0]; + label = globals_1.fishState.labels.splice(index, 1)[0]; + } + label.task.cancel(); + outputSuccess("Removed one label."); + } + }, + member: { + args: ["value:boolean", "player:player"], + description: "Sets a player's member status.", + perm: commands_1.Perm.admin, + handler: function (_a) { + return __awaiter(this, arguments, void 0, function (_b) { + var args = _b.args, outputSuccess = _b.outputSuccess, f = _b.f; + return __generator(this, function (_c) { + switch (_c.label) { + case 0: return [4 /*yield*/, args.player.setFlag("member", args.value)]; + case 1: + _c.sent(); + outputSuccess(f(templateObject_29 || (templateObject_29 = __makeTemplateObject(["Set membership status of player ", " to ", "."], ["Set membership status of player ", " to ", "."])), args.player, args.value)); + return [2 /*return*/]; + } + }); + }); + } + }, + remind: { + args: ["rule:number", "target:player?"], + description: "Remind players in chat of a specific rule.", + perm: commands_1.Perm.mod, + handler: function (_a) { + var _b; + var args = _a.args, outputSuccess = _a.outputSuccess, f = _a.f; + var rule = (_b = config_1.rules[args.rule - 1]) !== null && _b !== void 0 ? _b : (0, commands_1.fail)("The rule you requested does not exist."); + if (args.target) { + args.target.sendMessage("A staff member wants to remind you of the following rule:\n" + rule); + outputSuccess(f(templateObject_30 || (templateObject_30 = __makeTemplateObject(["Reminded ", " of rule ", ""], ["Reminded ", " of rule ", ""])), args.target, args.rule)); + } + else { + Call.sendMessage("A staff member wants to remind everyone of the following rule:\n" + rule); + } + }, + }, + ban: { + args: ["uuid_or_ip:string?"], + description: "Bans a player by UUID and IP.", + perm: commands_1.Perm.admin, + handler: function (_a) { + return __awaiter(this, arguments, void 0, function (_b) { + var uuid, data, name, ip, ip, info, alreadyBanned, option; + var args = _b.args, sender = _b.sender, outputSuccess = _b.outputSuccess, f = _b.f, admins = _b.admins; + return __generator(this, function (_c) { + switch (_c.label) { + case 0: + if (!(args.uuid_or_ip && globals_1.uuidPattern.test(args.uuid_or_ip))) return [3 /*break*/, 2]; + uuid = args.uuid_or_ip; + data = void 0; + if ((data = admins.getInfoOptional(uuid)) != null && data.admin) + (0, commands_1.fail)("Cannot ban an admin."); + name = data ? "".concat((0, funcs_1.escapeStringColorsClient)(data.lastName), " (").concat(uuid, "/").concat(data.lastIP, ")") : uuid; + return [4 /*yield*/, menus_1.Menu.confirmDangerous(sender, "Are you sure you want to ban ".concat(name, "?"))]; + case 1: + _c.sent(); + admins.banPlayerID(uuid); + if (data) { + ip = data.lastIP; + admins.banPlayerIP(ip); + api.ban({ ip: ip, uuid: uuid }); + Log.info("".concat(uuid, "/").concat(ip, " was banned.")); + (0, utils_1.logAction)("banned", sender, data); + outputSuccess(f(templateObject_31 || (templateObject_31 = __makeTemplateObject(["Banned player ", " (", "/", ")"], ["Banned player ", " (", "/", ")"])), (0, funcs_1.escapeStringColorsClient)(data.lastName), uuid, ip)); + //TODO add way to specify whether to activate or escape color tags + } + else { + api.ban({ uuid: uuid }); + Log.info("".concat(uuid, " was banned.")); + (0, utils_1.logAction)("banned", sender, uuid); + outputSuccess(f(templateObject_32 || (templateObject_32 = __makeTemplateObject(["Banned player ", ". [yellow]Unable to determine IP.[]"], ["Banned player ", ". [yellow]Unable to determine IP.[]"])), uuid)); + } + (0, utils_1.updateBans)(function (player) { return "[scarlet]Player [yellow]".concat(player.name, "[scarlet] has been whacked by ").concat(sender.prefixedName, "."); }); + return [2 /*return*/]; + case 2: + if (!(args.uuid_or_ip && globals_1.ipPattern.test(args.uuid_or_ip))) return [3 /*break*/, 4]; + ip = args.uuid_or_ip; + return [4 /*yield*/, menus_1.Menu.confirmDangerous(sender, "Are you sure you want to ban IP ".concat(ip, "?"))]; + case 3: + _c.sent(); + api.ban({ ip: ip }); + info = admins.findByIP(ip); + if (info) + (0, utils_1.logAction)("banned", sender, info); + else + (0, utils_1.logAction)("banned ".concat(ip), sender); + alreadyBanned = admins.banPlayerIP(ip); + if (alreadyBanned) { + outputSuccess(f(templateObject_33 || (templateObject_33 = __makeTemplateObject(["IP ", " is already banned. Ban was synced to other servers."], ["IP ", " is already banned. Ban was synced to other servers."])), ip)); + } + else { + outputSuccess(f(templateObject_34 || (templateObject_34 = __makeTemplateObject(["IP ", " has been banned. Ban was synced to other servers."], ["IP ", " has been banned. Ban was synced to other servers."])), ip)); + } + (0, utils_1.updateBans)(function (player) { return "[scarlet]Player [yellow]".concat(player.name, "[scarlet] has been whacked by ").concat(sender.prefixedName, "."); }); + return [2 /*return*/]; + case 4: return [4 /*yield*/, menus_1.Menu.menu("[scarlet]BAN[]", "Choose a player to ban.", (0, funcs_1.setToArray)(Groups.player), sender, { + includeCancel: true, + optionStringifier: function (opt) { return opt.name; } + })]; + case 5: + option = _c.sent(); + if (option.admin) + (0, commands_1.fail)("Cannot ban an admin."); + return [4 /*yield*/, menus_1.Menu.confirmDangerous(sender, "Are you sure you want to ban ".concat(option.name, "?"))]; + case 6: + _c.sent(); + admins.banPlayerIP(option.ip()); //this also bans the UUID + api.ban({ ip: option.ip(), uuid: option.uuid() }); + Log.info("".concat(option.ip(), "/").concat(option.uuid(), " was banned.")); + (0, utils_1.logAction)("banned", sender, option.getInfo()); + outputSuccess(f(templateObject_35 || (templateObject_35 = __makeTemplateObject(["Banned player ", "."], ["Banned player ", "."])), option)); + (0, utils_1.updateBans)(function (player) { return "[scarlet]Player [yellow]".concat(player.name, "[scarlet] has been whacked by ").concat(sender.prefixedName, "."); }); + return [2 /*return*/]; + } + }); + }); + } + }, + kill: { + args: ["player:player"], + description: "Kills a player's unit.", + perm: commands_1.Perm.admin, + requirements: [commands_1.Req.moderate("player", true)], + handler: function (_a) { + var args = _a.args, outputFail = _a.outputFail, outputSuccess = _a.outputSuccess, f = _a.f; + var unit = args.player.unit(); + if (unit) { + unit.kill(); + outputSuccess(f(templateObject_36 || (templateObject_36 = __makeTemplateObject(["Killed the unit of player ", "."], ["Killed the unit of player ", "."])), args.player)); + } + else { + outputFail(f(templateObject_37 || (templateObject_37 = __makeTemplateObject(["Player ", " does not have a unit."], ["Player ", " does not have a unit."])), args.player)); + } + } + }, + killunits: { + args: ["team:team?", "unit:unittype?"], + description: "Kills all units, optionally specifying a team and unit type.", + perm: commands_1.Perm.massKill, + handler: function (_a) { + return __awaiter(this, arguments, void 0, function (_b) { + var i_1, before, i_2, before; + var _c = _b.args, team = _c.team, unit = _c.unit, sender = _b.sender, outputSuccess = _b.outputSuccess, f = _b.f; + return __generator(this, function (_d) { + switch (_d.label) { + case 0: + if (!team) return [3 /*break*/, 2]; + return [4 /*yield*/, menus_1.Menu.confirmDangerous(sender, "This will kill [scarlet]every ".concat(unit ? unit.localizedName : "unit", "[] on the team ").concat(team.coloredName(), "."), { confirmText: "[orange]Kill units[]" })]; + case 1: + _d.sent(); + if (unit) { + i_1 = 0; + team.data().units.each(function (u) { return u.type == unit; }, function (u) { + u.kill(); + i_1++; + }); + outputSuccess(f(templateObject_38 || (templateObject_38 = __makeTemplateObject(["Killed ", " units on ", "."], ["Killed ", " units on ", "."])), i_1, team)); + } + else { + before = team.data().units.size; + team.data().units.each(function (u) { return u.kill(); }); + outputSuccess(f(templateObject_39 || (templateObject_39 = __makeTemplateObject(["Killed ", " units on ", "."], ["Killed ", " units on ", "."])), before, team)); + } + return [3 /*break*/, 4]; + case 2: return [4 /*yield*/, menus_1.Menu.confirmDangerous(sender, "This will kill [scarlet]every single ".concat(unit ? unit.localizedName : "unit", "[]."), { confirmText: "[orange]Kill all units[]" })]; + case 3: + _d.sent(); + if (unit) { + i_2 = 0; + Groups.unit.each(function (u) { return u.type == unit; }, function (u) { + u.kill(); + i_2++; + }); + outputSuccess(f(templateObject_40 || (templateObject_40 = __makeTemplateObject(["Killed ", " units."], ["Killed ", " units."])), i_2)); + } + else { + before = Groups.unit.size(); + Groups.unit.each(function (u) { return u.kill(); }); + outputSuccess(f(templateObject_41 || (templateObject_41 = __makeTemplateObject(["Killed ", " units."], ["Killed ", " units."])), before)); + } + _d.label = 4; + case 4: return [2 /*return*/]; + } + }); + }); + } + }, + killbuildings: { + args: ["team:team?"], + description: "Kills all buildings (except cores), optionally specifying a team.", + perm: commands_1.Perm.massKill, + handler: function (_a) { + return __awaiter(this, arguments, void 0, function (_b) { + var count, count; + var team = _b.args.team, sender = _b.sender, outputSuccess = _b.outputSuccess, f = _b.f; + return __generator(this, function (_c) { + switch (_c.label) { + case 0: + if (!team) return [3 /*break*/, 2]; + return [4 /*yield*/, menus_1.Menu.confirmDangerous(sender, "This will kill [scarlet]every building[] on the team ".concat(team.coloredName(), ", except cores."), { confirmText: "[orange]Kill buildings[]" })]; + case 1: + _c.sent(); + count = team.data().buildings.size; + team.data().buildings.each(function (b) { return !(b.block instanceof CoreBlock); }, function (b) { return b.tile.remove(); }); + outputSuccess(f(templateObject_42 || (templateObject_42 = __makeTemplateObject(["Killed ", " buildings on ", "."], ["Killed ", " buildings on ", "."])), count, team)); + return [3 /*break*/, 4]; + case 2: return [4 /*yield*/, menus_1.Menu.confirmDangerous(sender, "This will kill [scarlet]every building[] except cores.", { confirmText: "[orange]Kill buildings[]" })]; + case 3: + _c.sent(); + count = Groups.build.size(); + Groups.build.each(function (b) { return !(b.block instanceof CoreBlock); }, function (b) { return b.tile.remove(); }); + outputSuccess(f(templateObject_43 || (templateObject_43 = __makeTemplateObject(["Killed ", " buildings."], ["Killed ", " buildings."])), count)); + _c.label = 4; + case 4: return [2 /*return*/]; + } + }); + }); + } + }, + respawn: { + args: ["player:player"], + description: "Forces a player to respawn.", + perm: commands_1.Perm.mod, + requirements: [commands_1.Req.moderate("player", true, "mod", true)], + handler: function (_a) { + var args = _a.args, outputSuccess = _a.outputSuccess, f = _a.f; + args.player.forceRespawn(); + outputSuccess(f(templateObject_44 || (templateObject_44 = __makeTemplateObject(["Respawned player ", "."], ["Respawned player ", "."])), args.player)); + } + }, + clearunit: { + args: ["target:player", "duration:time?"], + description: "Forces a player out of the unit they are controlling, and blocks them from possessing units for a specified duration.", + perm: commands_1.Perm.mod, + requirements: [commands_1.Req.moderate("target", false, "mod", false)], + handler: function (_a) { + var _b = _a.args, target = _b.target, duration = _b.duration, sender = _a.sender, outputSuccess = _a.outputSuccess, f = _a.f; + if (Date.now() > 1000 + target.blockedFromPossessingUnitsUntil) + duration !== null && duration !== void 0 ? duration : (duration = funcs_1.Duration.minutes(1)); + else + duration !== null && duration !== void 0 ? duration : (duration = 0); + if (duration == 0) { + target.blockedFromPossessingUnitsUntil = 0; + target.sendMessage("You are allowed to control units again."); + outputSuccess(f(templateObject_45 || (templateObject_45 = __makeTemplateObject(["Restored ", "'s ability to control units."], ["Restored ", "'s ability to control units."])), target)); + (0, utils_1.logAction)("restored unit possession for", sender, target); + } + else { + target.forceRespawn(); + target.blockedFromPossessingUnitsUntil = Date.now() + duration; + target.sendMessage("You have been blocked from controlling units for ".concat((0, utils_1.formatTime)(duration), ".")); + outputSuccess(f(templateObject_46 || (templateObject_46 = __makeTemplateObject(["Blocked ", " from controlling units for ", "."], ["Blocked ", " from controlling units for ", "."])), target, (0, utils_1.formatTime)(duration))); + (0, utils_1.logAction)("revoked unit possession for", sender, target, undefined, duration); + } + } + }, + clearcommand: { + args: ["target:player", "duration:time?"], + description: "Blocks a player from commanding units for a specified duration.", + perm: commands_1.Perm.mod, + requirements: [commands_1.Req.moderate("target", false, "mod", false)], + handler: function (_a) { + var _b = _a.args, target = _b.target, duration = _b.duration, sender = _a.sender, outputSuccess = _a.outputSuccess, f = _a.f; + if (Date.now() > 1000 + target.blockedFromCommandingUnitsUntil) + duration !== null && duration !== void 0 ? duration : (duration = funcs_1.Duration.minutes(1)); + else + duration !== null && duration !== void 0 ? duration : (duration = 0); + if (duration == 0) { + target.blockedFromCommandingUnitsUntil = 0; + target.sendMessage("You are allowed to command units again."); + outputSuccess(f(templateObject_47 || (templateObject_47 = __makeTemplateObject(["Restored ", "'s ability to command units."], ["Restored ", "'s ability to command units."])), target)); + (0, utils_1.logAction)("restored command mode for", sender, target, undefined, duration); + } + else { + target.blockedFromCommandingUnitsUntil = Date.now() + duration; + target.sendMessage("You have been blocked from commanding units for ".concat((0, utils_1.formatTime)(duration), ".")); + outputSuccess(f(templateObject_48 || (templateObject_48 = __makeTemplateObject(["Blocked ", " from commanding units for ", "."], ["Blocked ", " from commanding units for ", "."])), target, (0, utils_1.formatTime)(duration))); + (0, utils_1.logAction)("revoked command mode for", sender, target, undefined, duration); + } + } + }, + stealunit: { + args: ["target:player", "newcontroller:player?"], + description: "Steals the unit of a player, putting you in their unit and forcing them to respawn.", + perm: commands_1.Perm.mod, + requirements: [commands_1.Req.moderate("target", true, "mod", true), commands_1.Req.moderate("newcontroller", true, "mod", true)], + handler: function (_a) { + var _b; + var sender = _a.sender, _c = _a.args, target = _c.target, _d = _c.newcontroller, newcontroller = _d === void 0 ? sender : _d, outputSuccess = _a.outputSuccess, f = _a.f; + var unit = (_b = target.unit()) !== null && _b !== void 0 ? _b : (0, commands_1.fail)(f(templateObject_49 || (templateObject_49 = __makeTemplateObject(["Targeted player ", " is not in a unit."], ["Targeted player ", " is not in a unit."])), target)); + if (target.team() !== newcontroller.team()) { + if (!sender.hasPerm("changeTeamExternal")) { + if (!sender.hasPerm("changeTeam")) + (0, commands_1.fail)("You do not have permission to change teams."); + newcontroller.setTeam(unit.team); + } + } + target.forceRespawn(); + newcontroller.unit(unit); + if (newcontroller == sender) { + outputSuccess(f(templateObject_50 || (templateObject_50 = __makeTemplateObject(["Commandeered the unit of player ", "."], ["Commandeered the unit of player ", "."])), target)); + } + else { + outputSuccess(f(templateObject_51 || (templateObject_51 = __makeTemplateObject(["Transferred player ", " into the unit of ", "."], ["Transferred player ", " into the unit of ", "."])), newcontroller, target)); + newcontroller.sendMessage(f(templateObject_52 || (templateObject_52 = __makeTemplateObject(["[green]You were transferred to the unit of player ", " by ", "."], ["[green]You were transferred to the unit of player ", " by ", "."])), target, sender)('[green]')); + } + } + }, + m: { + args: ["message:string"], + description: "Sends a message to muted players only.", + perm: commands_1.Perm.mod, + handler: function (_a) { + var sender = _a.sender, args = _a.args; + players_1.FishPlayer.messageMuted(sender.prefixedName, args.message); + } + }, + info: { + args: ["target:player", "showColors:boolean?"], + description: "Displays information about an online player.", + perm: commands_1.Perm.none, + handler: function (_a) { + var sender = _a.sender, args = _a.args, output = _a.output, f = _a.f; + var info = args.target.info(); + var names = args.showColors + ? info.names.map(funcs_1.escapeStringColorsClient).toString(", ") + : __spreadArray([], __read(new Set(info.names.map(function (n) { return Strings.stripColors(n); }).toArray())), false).join(", "); + output(f(templateObject_53 || (templateObject_53 = __makeTemplateObject(["[accent]Info for player ", " [gray](", ") (#", ")\n\t[accent]Rank: ", "\n\t[accent]Role flags: ", "\n\t[accent]Stopped: ", "\n\t[accent]marked: ", "\n\t[accent]muted: ", "\n\t[accent]autoflagged: ", "\n\t[accent]VPN detected: ", "\n\t[accent]times joined / kicked: ", "/", "\n\t[accent]First joined: ", "\n\t[accent]Names used: [[", "]"], ["\\\n[accent]Info for player ", " [gray](", ") (#", ")\n\t[accent]Rank: ", "\n\t[accent]Role flags: ", "\n\t[accent]Stopped: ", "\n\t[accent]marked: ", "\n\t[accent]muted: ", "\n\t[accent]autoflagged: ", "\n\t[accent]VPN detected: ", "\n\t[accent]times joined / kicked: ", "/", "\n\t[accent]First joined: ", "\n\t[accent]Names used: [[", "]"])), args.target, (0, funcs_1.escapeStringColorsClient)(args.target.name), args.target.player.id.toString(), args.target.rank, Array.from(args.target.flags).map(function (f) { return f.coloredName(); }).join(" "), f.boolBad(!args.target.hasPerm("play")), args.target.marked() ? "until ".concat((0, utils_1.formatTimeRelative)(args.target.unmarkTime)) : "[green]false", f.boolBad(args.target.muted), f.boolBad(args.target.autoflagged), f.boolBad(args.target.ipDetectedVpn), info.timesJoined, info.timesKicked, args.target.firstJoined < 1 ? "unknown" : (0, utils_1.formatTimeRelative)(args.target.firstJoined), names)); + if (sender.hasPerm("viewUUIDs")) + output(f(templateObject_54 || (templateObject_54 = __makeTemplateObject(["\t[#FFAAAA]UUID: ", ""], ["\\t[#FFAAAA]UUID: ", ""])), args.target.uuid)); + if (sender.hasPerm("viewIPs")) + output(f(templateObject_55 || (templateObject_55 = __makeTemplateObject(["\t[#FFAAAA]IP: ", ""], ["\\t[#FFAAAA]IP: ", ""])), args.target.ip())); + } + }, + spawn: { + args: ["type:unittype", "x:number?", "y:number?", "count:number?", "team:team?", "effects:string?", "stack:boolean?"], + description: "Spawns a unit of specified type at your position. [scarlet]Usage will be logged.[]", + perm: commands_1.Perm.admin.exceptModes({ + testsrv: commands_1.Perm.trusted, + }), + data: [], + requirements: [commands_1.Req.positiveInteger("count")], + handler: function (_a) { + var _b, _c; + var sender = _a.sender, args = _a.args, data = _a.data, outputSuccess = _a.outputSuccess, f = _a.f; + var x = args.x ? (args.x * 8) : sender.player.x; + var y = args.y ? (args.y * 8) : sender.player.y; + var team = (_b = args.team) !== null && _b !== void 0 ? _b : sender.team(); + var count = Math.min((_c = args.count) !== null && _c !== void 0 ? _c : 1, 1000); + for (var i = 0; i < count; i++) { + var unit = args.type.create(team); + var xOffset = args.stack ? 0 : 0.01 * i; + var yOffset = args.stack ? 0 : 0.5 * (i % 10); + unit.set(x + xOffset, y + yOffset); + if (args.effects) + (0, utils_1.applyEffectMode)(args.effects, unit, 1e12); + unit.add(); + data.push(unit); + } + if (!(config_1.Gamemode.sandbox() || config_1.Gamemode.testsrv()) && args.effects !== 'paper') + (0, utils_1.logAction)("spawned unit ".concat(args.type.name).concat(count == 1 ? '' : " x".concat(count), " at ").concat(Math.round(x / 8), ", ").concat(Math.round(y / 8)) + (args.effects ? "with ".concat(args.effects, " effects") : ''), sender); + outputSuccess(f(templateObject_56 || (templateObject_56 = __makeTemplateObject(["Spawned unit ", " at (", ", ", ")"], ["Spawned unit ", " at (", ", ", ")"])), args.type, Math.round(x / 8), Math.round(y / 8))); + } + }, + setblock: { + args: ["x:number", "y:number", "block:block", "team:team?", "rotation:number?"], + description: "Sets the block at a location.", + perm: commands_1.Perm.admin.exceptModes({ + testsrv: commands_1.Perm.trusted, + }), + requirements: [commands_1.Req.integerRange("rotation", 0, 3)], + handler: function (_a) { + var _b, _c; + var args = _a.args, sender = _a.sender, outputSuccess = _a.outputSuccess, f = _a.f; + var team = (_b = args.team) !== null && _b !== void 0 ? _b : sender.team(); + var tile = Vars.world.tile(args.x, args.y); + if (tile == null) + (0, commands_1.fail)(f(templateObject_57 || (templateObject_57 = __makeTemplateObject(["Position (", ", ", ") is out of bounds."], ["Position (", ", ", ") is out of bounds."])), args.x, args.y)); + tile.setNet(args.block, team, (_c = args.rotation) !== null && _c !== void 0 ? _c : 0); + (0, utils_1.addToTileHistory)({ + pos: "".concat(args.x, ",").concat(args.y), + uuid: sender.uuid, + action: "setblocked", + type: args.block.localizedName + }); + if (!config_1.Gamemode.sandbox()) + (0, utils_1.logAction)("set block to ".concat(args.block.localizedName, " at ").concat(args.x, ",").concat(args.y), sender); + outputSuccess(f(templateObject_58 || (templateObject_58 = __makeTemplateObject(["Set block at ", ", ", " to ", ""], ["Set block at ", ", ", " to ", ""])), args.x, args.y, args.block)); + } + }, + setblockr: { + args: ["block:block?", "team:team?", "rotation:number?"], + description: "Sets the block at tapped locations, repeatedly.", + perm: commands_1.Perm.admin, + requirements: [commands_1.Req.integerRange("rotation", 0, 3)], + tapped: function (_a) { + var _b, _c; + var args = _a.args, sender = _a.sender, f = _a.f, x = _a.x, y = _a.y, outputSuccess = _a.outputSuccess; + if (!args.block) + (0, funcs_1.crash)("uh oh"); + var team = (_b = args.team) !== null && _b !== void 0 ? _b : sender.team(); + var tile = Vars.world.tile(x, y); + if (tile == null) + (0, commands_1.fail)(f(templateObject_59 || (templateObject_59 = __makeTemplateObject(["Position (", ", ", ") is out of bounds."], ["Position (", ", ", ") is out of bounds."])), x, y)); + tile.setNet(args.block, team, (_c = args.rotation) !== null && _c !== void 0 ? _c : 0); + (0, utils_1.addToTileHistory)({ + pos: "".concat(x, ",").concat(y), + uuid: sender.uuid, + action: "setblocked", + type: args.block.localizedName + }); + if (!config_1.Gamemode.sandbox()) + (0, utils_1.logAction)("set block to ".concat(args.block.localizedName, " at ").concat(x, ",").concat(y), sender); + outputSuccess(f(templateObject_60 || (templateObject_60 = __makeTemplateObject(["Set block at ", ", ", " to ", ""], ["Set block at ", ", ", " to ", ""])), x, y, args.block)); + }, + handler: function (_a) { + var args = _a.args, outputSuccess = _a.outputSuccess, handleTaps = _a.handleTaps, currentTapMode = _a.currentTapMode, f = _a.f; + if (args.block) { + handleTaps("on"); + if (currentTapMode == "off") { + outputSuccess("setblockr enabled.\n[scarlet]Be careful, you have the midas touch now![] Turn it off by running /setblockr again."); + } + else { + outputSuccess(f(templateObject_61 || (templateObject_61 = __makeTemplateObject(["Changed setblockr's block to ", ""], ["Changed setblockr's block to ", ""])), args.block)); + } + } + else { + if (currentTapMode == "off") { + (0, commands_1.fail)("Please specify the block to place."); + } + else { + handleTaps("off"); + outputSuccess("setblockr disabled."); + } + } + } + }, + exterminate: { + args: [], + description: "Removes all spawned units.", + perm: commands_1.Perm.admin.exceptModes({ + testsrv: commands_1.Perm.trusted, + }), + handler: function (_a) { + var sender = _a.sender, outputSuccess = _a.outputSuccess, f = _a.f, allCommands = _a.allCommands; + var numKilled = 0; + allCommands.spawn.data.forEach(function (u) { + if (u.isAdded() && !u.dead) { + u.kill(); + numKilled++; + } + }); + if (!config_1.Gamemode.sandbox()) + (0, utils_1.logAction)("exterminated ".concat(numKilled, " units"), sender); + outputSuccess(f(templateObject_62 || (templateObject_62 = __makeTemplateObject(["Exterminated ", " units."], ["Exterminated ", " units."])), numKilled)); + } + }, + js: { + args: ["javascript:string"], + description: "Run arbitrary javascript.", + perm: commands_1.Perm.runJS, + customUnauthorizedMessage: "[scarlet]You are not in the jsers file. This incident will be reported.[]", + handler: function (_a) { + var javascript = _a.args.javascript, output = _a.output, outputFail = _a.outputFail, sender = _a.sender; + //Additional validation couldn't hurt... + var playerInfo_AdminUsid = sender.info().adminUsid; + if (!playerInfo_AdminUsid || playerInfo_AdminUsid != sender.player.usid() || sender.usid != sender.player.usid()) { + api.sendModerationMessage("# !!!!! /js authentication failed !!!!!\nServer: ".concat(config_1.Gamemode.name(), " Player: ").concat((0, funcs_1.escapeTextDiscord)(sender.cleanedName), "/`").concat(sender.uuid, "`\n<@!709904412033810533>")); + (0, commands_1.fail)("Authentication failure"); + } + if (javascript == "Timer.instance().clear()") + (0, commands_1.fail)("Are you really sure you want to do that? If so, prepend \"void\" to your command."); + try { + var scripts = Vars.mods.getScripts(); + var out = scripts.context.evaluateString(scripts.scope, javascript, "fish-js-console.js", 1); + if (out instanceof Array) { + output("[cyan]Array: [[[]" + out.join(", ") + "[cyan]]"); + } + else if (out === undefined) { + output("[blue]undefined[]"); + } + else if (out === null) { + output("[blue]null[]"); + } + else if (out instanceof Error) { + outputFail((0, funcs_1.parseError)(out)); + } + else if (typeof out == "number") { + output("[blue]".concat(out, "[]")); + } + else { + output(out); + } + } + catch (err) { + outputFail((0, funcs_1.parseError)(err)); + } + } + }, + fjs: { + args: ["javascript:string"], + description: "Run arbitrary javascript in the fish-commands context.", + perm: commands_1.Perm.runJS, + customUnauthorizedMessage: "[scarlet]You are not in the jsers file. This incident will be reported.[]", + handler: function (_a) { + var javascript = _a.args.javascript, output = _a.output, outputFail = _a.outputFail, sender = _a.sender; + //Additional validation couldn't hurt... + var playerInfo_AdminUsid = sender.info().adminUsid; + if (!playerInfo_AdminUsid || playerInfo_AdminUsid != sender.player.usid() || sender.usid != sender.player.usid()) { + api.sendModerationMessage("# !!!!! /js authentication failed !!!!!\nServer: ".concat(config_1.Gamemode.name(), " Player: ").concat((0, funcs_1.escapeTextDiscord)(sender.cleanedName), "/`").concat(sender.uuid, "`\n<@!709904412033810533>")); + (0, commands_1.fail)("Authentication failure"); + } + fjsContext.runJS(javascript, output, outputFail, sender); + } + }, + antibot: { + args: ["timeout:time?"], + description: "Checks anti bot stats, or force enables anti bot mode.", + perm: commands_1.Perm.mod, + handler: function (_a) { + var args = _a.args, sender = _a.sender, outputSuccess = _a.outputSuccess, output = _a.output, f = _a.f; + if (args.timeout != undefined) { + args.timeout = Math.min(args.timeout, sender.hasPerm("admin") ? funcs_1.Duration.hours(1) : funcs_1.Duration.minutes(10)); + players_1.FishPlayer.triggerAntibot(args.timeout, "Manually triggered by player ".concat(sender.name), "manual"); + outputSuccess("Set antibot mode override for ".concat((0, utils_1.formatTime)(args.timeout), ".")); + } + else { + output("[acid]Antibot status:\n[acid]Enabled: ".concat(f.boolBad(players_1.FishPlayer.antiBotMode()), "\n").concat((0, utils_1.getAntiBotInfo)("client"))); + } + } + }, + chatstrictness: { + args: ["player:player", "value:string"], + description: "Sets chat strictness for a player.", + perm: commands_1.Perm.mod, + handler: function (_a) { + var _b = _a.args, player = _b.player, value = _b.value, sender = _a.sender, outputSuccess = _a.outputSuccess, f = _a.f; + if (!sender.canModerate(player, true)) + (0, commands_1.fail)("You do not have permission to set the chat strictness level of this player."); + if (!(value == "chat" || value == "strict")) + (0, commands_1.fail)("Invalid chat strictness level: valid levels are \"chat\", \"strict\""); + player.chatStrictness = value; + (0, utils_1.logAction)("set chat strictness to ".concat(value, " for"), sender, player); + outputSuccess(f(templateObject_63 || (templateObject_63 = __makeTemplateObject(["Set chat strictness for player ", " to \"", "\"."], ["Set chat strictness for player ", " to \"", "\"."])), player, value)); + } + }, + emanate: (0, commands_1.command)(function () { + var unitMapping = {}; + Timer.schedule(function () { + var e_1, _a; + try { + for (var _b = __values(Object.entries(unitMapping)), _c = _b.next(); !_c.done; _c = _b.next()) { + var _d = __read(_c.value, 2), uuid = _d[0], unit = _d[1]; + var fishP = players_1.FishPlayer.getById(uuid); + if (!fishP || !fishP.connected() || (unit.getPlayer() != fishP.player)) { + delete unitMapping[uuid]; + unit === null || unit === void 0 ? void 0 : unit.kill(); + } + } + } + catch (e_1_1) { e_1 = { error: e_1_1 }; } + finally { + try { + if (_c && !_c.done && (_a = _b.return)) _a.call(_b); + } + finally { if (e_1) throw e_1.error; } + } + }, 1, 0.5); + return { + args: [], + description: "Puts you in an emanate.", + perm: commands_1.Perm.admin, + data: { unitMapping: unitMapping }, + requirements: [], + handler: function (_a) { + var sender = _a.sender, outputSuccess = _a.outputSuccess; + var emanate = UnitTypes.emanate.spawn(sender.team(), sender.player.x, sender.player.y); + sender.player.unit(emanate); + unitMapping[sender.uuid] = emanate; + if (!config_1.Gamemode.sandbox()) + (0, utils_1.logAction)("spawned an emanate", sender); + outputSuccess("Spawned an emanate."); + } + }; + }), + updatemaps: { + args: [], + description: 'Attempt to fetch and update all map files', + perm: commands_1.Perm.trusted, + requirements: function (_a) { + var sender = _a.sender; + return [commands_1.Req.cooldownGlobal(config_1.Gamemode.testsrv() || sender.hasPerm("mod") ? 15000 : funcs_1.Duration.minutes(5))]; + }, + handler: function (_a) { + var output = _a.output, outputSuccess = _a.outputSuccess, outputFail = _a.outputFail; + output("Updating maps... (this may take a while)"); + (0, files_1.updateMaps)() + .then(function (changed) { + Log.info("Maps updated."); + if (changed) { + outputSuccess("Map update completed."); + Call.sendMessage("[orange]Maps have been updated. Run [white]/maps[] to view available maps."); + } + else { + outputSuccess("Map update completed; already up to date."); + } + }) + .catch(function (message) { + outputFail("Map update failed: ".concat(String(message))); + Log.err("Map updates failed: ".concat(String(message))); + }); + } + }, + clearfire: { + args: [], + description: "Clears all the fires.", + perm: commands_1.Perm.admin, + handler: function (_a) { + var output = _a.output, outputSuccess = _a.outputSuccess; + output("Removing fires..."); + var totalRemoved = 0; + Call.sendMessage("[scarlet][[Fire Department]:[yellow] Fires were reported. Trucks are en-route. Removing all fires shortly."); + Timer.schedule(function () { + totalRemoved += Groups.fire.size(); + Groups.fire.each(function (f) { return f.remove(); }); + Groups.fire.clear(); + }, 2, 0.1, 40); + Timer.schedule(function () { + outputSuccess("Removed ".concat(totalRemoved, " fires.")); + Call.sendMessage("[scarlet][[Fire Department]:[yellow] We've extinguished ".concat(totalRemoved, " fires.")); + }, 6.1); + } + }, + search: { + args: ["input:string"], + description: "Searches playerinfo by name, IP, or UUID.", + perm: commands_1.Perm.admin, + handler: function (_a) { + return __awaiter(this, arguments, void 0, function (_b) { + var fishP, info, matches, matches_1, displayMatches; + var input = _b.args.input, admins = _b.admins, output = _b.output, f = _b.f, sender = _b.sender; + return __generator(this, function (_c) { + switch (_c.label) { + case 0: + if (!globals_1.uuidPattern.test(input)) return [3 /*break*/, 1]; + fishP = players_1.FishPlayer.getById(input); + info = admins.getInfoOptional(input); + if (fishP == null && info == null) + (0, commands_1.fail)(f(templateObject_64 || (templateObject_64 = __makeTemplateObject(["No stored data matched uuid ", "."], ["No stored data matched uuid ", "."])), input)); + else if (fishP == null && info) + output(f(templateObject_65 || (templateObject_65 = __makeTemplateObject(["[accent]Found player info (but no fish player data) for uuid ", "\nLast name used: \"", "\" [gray](", ")[] [[", "]\nIPs used: ", ""], ["[accent]\\\nFound player info (but no fish player data) for uuid ", "\nLast name used: \"", "\" [gray](", ")[] [[", "]\nIPs used: ", ""])), input, info.plainLastName(), (0, funcs_1.escapeStringColorsClient)(info.lastName), info.names.map(funcs_1.escapeStringColorsClient).items.join(", "), info.ips.map(function (i) { return "[blue]".concat(i, "[]"); }).toString(", "))); + else if (fishP && info) + output(f(templateObject_66 || (templateObject_66 = __makeTemplateObject(["[accent]Found fish player data for uuid ", "\nLast name used: \"", "\" [gray](", ")[] [[", "]\nIPs used: ", ""], ["[accent]\\\nFound fish player data for uuid ", "\nLast name used: \"", "\" [gray](", ")[] [[", "]\nIPs used: ", ""])), input, fishP.name, (0, funcs_1.escapeStringColorsClient)(info.lastName), info.names.map(funcs_1.escapeStringColorsClient).items.join(", "), info.ips.map(function (i) { return "[blue]".concat(i, "[]"); }).toString(", "))); + else + (0, commands_1.fail)(f(templateObject_67 || (templateObject_67 = __makeTemplateObject(["Super weird edge case: found fish player data but no player info for uuid ", "."], ["Super weird edge case: found fish player data but no player info for uuid ", "."])), input)); + return [3 /*break*/, 5]; + case 1: + if (!globals_1.ipPattern.test(input)) return [3 /*break*/, 2]; + matches = admins.findByIPs(input); + if (matches.isEmpty()) + (0, commands_1.fail)(f(templateObject_68 || (templateObject_68 = __makeTemplateObject(["No stored data matched IP ", ""], ["No stored data matched IP ", ""])), input)); + output(f(templateObject_69 || (templateObject_69 = __makeTemplateObject(["[accent]Found ", " match", " for search \"", "\"."], ["[accent]Found ", " match", " for search \"", "\"."])), matches.size, matches.size == 1 ? "" : "es", input)); + matches.each(function (info) { return output(f(templateObject_70 || (templateObject_70 = __makeTemplateObject(["[accent]Player with uuid ", "\nLast name used: \"", "\" [gray](", ")[] [[", "]\nIPs used: ", ""], ["[accent]\\\nPlayer with uuid ", "\nLast name used: \"", "\" [gray](", ")[] [[", "]\nIPs used: ", ""])), info.id, info.plainLastName(), (0, funcs_1.escapeStringColorsClient)(info.lastName), info.names.map(funcs_1.escapeStringColorsClient).items.join(", "), info.ips.map(function (i) { return "[blue]".concat(i, "[]"); }).toString(", "))); }); + return [3 /*break*/, 5]; + case 2: + matches_1 = Vars.netServer.admins.searchNames(input); + if (matches_1.isEmpty()) + (0, commands_1.fail)(f(templateObject_71 || (templateObject_71 = __makeTemplateObject(["No stored data matched name ", ""], ["No stored data matched name ", ""])), input)); + output(f(templateObject_72 || (templateObject_72 = __makeTemplateObject(["[accent]Found ", " match", " for search \"", "\"."], ["[accent]Found ", " match", " for search \"", "\"."])), matches_1.size, matches_1.size == 1 ? "" : "es", input)); + displayMatches = function () { + matches_1.each(function (info) { return output(f(templateObject_73 || (templateObject_73 = __makeTemplateObject(["[accent]Player with uuid ", "\nLast name used: \"", "\" [gray](", ")[] [[", "]\nIPs used: ", ""], ["[accent]\\\nPlayer with uuid ", "\nLast name used: \"", "\" [gray](", ")[] [[", "]\nIPs used: ", ""])), info.id, info.plainLastName(), (0, funcs_1.escapeStringColorsClient)(info.lastName), info.names.map(funcs_1.escapeStringColorsClient).items.join(", "), info.ips.map(function (i) { return "[blue]".concat(i, "[]"); }).toString(", "))); }); + }; + if (!(matches_1.size > 20)) return [3 /*break*/, 4]; + return [4 /*yield*/, menus_1.Menu.confirm(sender, "Are you sure you want to view all ".concat(matches_1.size, " matches?"))]; + case 3: + _c.sent(); + _c.label = 4; + case 4: + displayMatches(); + _c.label = 5; + case 5: return [2 /*return*/]; + } + }); + }); + } + }, + peace: { + args: ["peace:boolean"], + description: "Toggles peaceful mode for sandbox.", + perm: commands_1.Perm.mod, + requirements: [commands_1.Req.mode('sandbox')], + handler: function (_a) { + var args = _a.args; + if (args.peace) { + globals_1.fishState.peacefulMode = true; + Groups.player.each(function (p) { + if (p.team() != Vars.state.rules.defaultTeam) { + p.team(Vars.state.rules.defaultTeam); + } + }); + Call.sendMessage("[[Sandbox] [green]Enabled peaceful mode."); + } + else { + globals_1.fishState.peacefulMode = false; + Call.sendMessage("[[Sandbox] [red]Disabled peaceful mode."); + } + }, + }, + effects: { + args: ["mode:string", "player:player?", "duration:time?"], + description: "Applies effects to a player's unit.", + perm: commands_1.Perm.admin.exceptModes({ + testsrv: commands_1.Perm.trusted, + }), + handler: function (_a) { + var _b, _c, _d; + var args = _a.args, sender = _a.sender, f = _a.f, outputSuccess = _a.outputSuccess; + if ((_b = args.player) === null || _b === void 0 ? void 0 : _b.hasPerm("blockTrolling")) + (0, commands_1.fail)(f(templateObject_74 || (templateObject_74 = __makeTemplateObject(["Player ", " is insufficiently trollable."], ["Player ", " is insufficiently trollable."])), args.player)); + if (args.player && !sender.canModerate(args.player, false)) + (0, commands_1.fail)("You do not have permission to perform moderation actions on this player."); + var target = (_c = args.player) !== null && _c !== void 0 ? _c : sender; + var unit = target.unit(); + if (!unit || unit.dead) + (0, commands_1.fail)(f(templateObject_75 || (templateObject_75 = __makeTemplateObject(["", "'s unit is dead."], ["", "'s unit is dead."])), target)); + var ticks = ((_d = args.duration) !== null && _d !== void 0 ? _d : 1e12) / 1000 * 60; + (0, utils_1.applyEffectMode)(args.mode, unit, ticks); + outputSuccess("".concat(args.mode === "clear" ? "Cleared" : "Applied", " effects.")); + if (!config_1.Gamemode.sandbox()) + (0, utils_1.logAction)("applied **".concat(args.mode, "** effects to"), sender, target); + } + }, + items: { + args: ["team:team", "item:item", "amount:number"], + description: "Gives items to a team.", + perm: commands_1.Perm.admin, + requirements: [commands_1.Req.integer("amount")], + handler: function (_a) { + var _b; + var _c = _a.args, team = _c.team, item = _c.item, amount = _c.amount, sender = _a.sender, outputSuccess = _a.outputSuccess, f = _a.f; + var core = (_b = team.data().cores.firstOpt()) !== null && _b !== void 0 ? _b : (0, commands_1.fail)(f(templateObject_76 || (templateObject_76 = __makeTemplateObject(["Team ", " has no cores."], ["Team ", " has no cores."])), team)); + core.items.add(item, amount); + outputSuccess(f(templateObject_77 || (templateObject_77 = __makeTemplateObject(["Gave ", " ", " to ", "."], ["Gave ", " ", " to ", "."])), amount, item, team)); + if (!config_1.Gamemode.sandbox()) + (0, utils_1.logAction)("gave ".concat(amount, " ").concat(item.localizedName.toLowerCase(), " to ").concat(team.name), sender); + } + }, + explosion: { + args: ["radius:number", "x:number", "y:number", "team:team?", "damage:number?", "damageMode:string?"], + description: "Causes an explosion at specified coordinates.", + perm: commands_1.Perm.admin, + handler: function (_a) { + var _b; + var _c = _a.args, radius = _c.radius, x = _c.x, y = _c.y, _d = _c.team, team = _d === void 0 ? Team.derelict : _d, _e = _c.damage, damage = _e === void 0 ? 1e12 : _e, _f = _c.damageMode, damageMode = _f === void 0 ? "both" : _f, outputSuccess = _a.outputSuccess; + var _g = __read((_b = (0, utils_1.match)(damageMode, { + air: [true, false], + ground: [false, true], + both: [true, true], + none: [false, false], + })) !== null && _b !== void 0 ? _b : (0, commands_1.fail)("Valid values of damageMode: air, ground, both, none"), 2), air = _g[0], ground = _g[1]; + if (radius > 100) + (0, commands_1.fail)("Maximum radius is 100"); + if (damage < 0) + Call.effect(Fx.dynamicSpikes, x * 8, y * 8, radius * 8, Pal.heal); + else + Call.effect(Fx.dynamicExplosion, x * 8, y * 8, Math.max(radius, 8) / 7, Color.white); + Damage.damage(team, x * 8, y * 8, radius * 8, damage, true, air, ground); + outputSuccess("Created an explosion at (".concat(x, ", ").concat(y, ").")); + } + }, + memorycorruption: { + args: [], + description: "Triggers a fake memory corruption prank.", + perm: commands_1.Perm.mod, + requirements: [commands_1.Req.cooldownGlobal(funcs_1.Duration.minutes(30))], + handler: function () { + (0, utils_1.definitelyRealMemoryCorruption)(); + } + }, + editor: { + args: ["editor:boolean"], + description: "Toggles the in-game editor mode.", + perm: commands_1.Perm.trusted, + requirements: [commands_1.Req.mode("testsrv"), commands_1.Req.cooldownGlobal(20000)], + handler: function (_a) { + var editor = _a.args.editor; + Vars.state.rules.editor = editor; + Call.setRules(Vars.state.rules); + } + }, + mapruns: { + args: ["map:map", "lowestHighscores:boolean?"], + description: "Displays all map runs for a selected map, and allows deleting invalid/cheated runs.", + perm: commands_1.Perm.admin, + handler: function (_a) { + return __awaiter(this, arguments, void 0, function (_b) { + var fmap, _c, initialLength, runs, _d, index, _, deleted; + var _e; + var _f = _b.args, map = _f.map, lowestHighscores = _f.lowestHighscores, sender = _b.sender, outputSuccess = _b.outputSuccess; + return __generator(this, function (_g) { + switch (_g.label) { + case 0: + fmap = (_e = maps_1.FMap.getCreate(map)) !== null && _e !== void 0 ? _e : (0, commands_1.fail)("Map data is still loading, please try again."); + if (!(lowestHighscores !== null && lowestHighscores !== void 0)) return [3 /*break*/, 1]; + _c = lowestHighscores; + return [3 /*break*/, 3]; + case 1: return [4 /*yield*/, menus_1.Menu.buttons(sender, "[accent]Map runs", "Select a view", [ + [{ data: true, text: "Lowest highscores" }], + [{ data: false, text: "All runs" }], + ], { + includeCancel: true, + onCancel: "reject" + })]; + case 2: + _c = (lowestHighscores = _g.sent()); + _g.label = 3; + case 3: + _c; + initialLength = fmap.runs.length; + runs = fmap.runs.slice(); + if (lowestHighscores) + runs = runs.filter(function (r) { return r.success; }) + .sort(function (a, b) { return a.duration() - b.duration(); }); + return [4 /*yield*/, menus_1.Menu.textPages(sender, runs.map(function (r) { return [ + (0, utils_1.formatTimestamp)(r.startTime), + function () { + return "Duration: ".concat((0, utils_1.formatTime)(r.duration()), "\nMax player count: ").concat(r.maxPlayerCount, "\nOutcome: ").concat(r.outcome()[1], "\nWave: ").concat(r.wave); + } + ]; }), ["[scarlet]\uE86FDelete"], { + onCancel: "reject" + })]; + case 4: + _d = __read.apply(void 0, [_g.sent(), 2]), index = _d[0], _ = _d[1]; + return [4 /*yield*/, menus_1.Menu.confirmDangerous(sender, "Are you sure you want to delete this map run? This action is irreversible.")]; + case 5: + _g.sent(); + if (initialLength != fmap.runs.length) + (0, commands_1.fail)("Someone else deleted a run, please try again."); + deleted = fmap.runs.splice(index, 1)[0]; + outputSuccess("Deleted run (".concat((0, utils_1.formatTimestamp)(deleted.startTime), ") with duration ").concat((0, utils_1.formatTime)(deleted.duration()), ".")); + return [2 /*return*/]; + } + }); + }); + } + } +}); +var templateObject_1, templateObject_2, templateObject_3, templateObject_4, templateObject_5, templateObject_6, templateObject_7, templateObject_8, templateObject_9, templateObject_10, templateObject_11, templateObject_12, templateObject_13, templateObject_14, templateObject_15, templateObject_16, templateObject_17, templateObject_18, templateObject_19, templateObject_20, templateObject_21, templateObject_22, templateObject_23, templateObject_24, templateObject_25, templateObject_26, templateObject_27, templateObject_28, templateObject_29, templateObject_30, templateObject_31, templateObject_32, templateObject_33, templateObject_34, templateObject_35, templateObject_36, templateObject_37, templateObject_38, templateObject_39, templateObject_40, templateObject_41, templateObject_42, templateObject_43, templateObject_44, templateObject_45, templateObject_46, templateObject_47, templateObject_48, templateObject_49, templateObject_50, templateObject_51, templateObject_52, templateObject_53, templateObject_54, templateObject_55, templateObject_56, templateObject_57, templateObject_58, templateObject_59, templateObject_60, templateObject_61, templateObject_62, templateObject_63, templateObject_64, templateObject_65, templateObject_66, templateObject_67, templateObject_68, templateObject_69, templateObject_70, templateObject_71, templateObject_72, templateObject_73, templateObject_74, templateObject_75, templateObject_76, templateObject_77; diff --git a/build/scripts/config.js b/build/scripts/config.js index 0f8151f4..3af914ac 100644 --- a/build/scripts/config.js +++ b/build/scripts/config.js @@ -1,358 +1,381 @@ -"use strict"; -/* -Copyright © BalaM314, 2026. All Rights Reserved. -This file contains configurable constants. -*/ -var __makeTemplateObject = (this && this.__makeTemplateObject) || function (cooked, raw) { - if (Object.defineProperty) { Object.defineProperty(cooked, "raw", { value: raw }); } else { cooked.raw = raw; } - return cooked; -}; -var __read = (this && this.__read) || function (o, n) { - var m = typeof Symbol === "function" && o[Symbol.iterator]; - if (!m) return o; - var i = m.call(o), r, ar = [], e; - try { - while ((n === void 0 || n-- > 0) && !(r = i.next()).done) ar.push(r.value); - } - catch (error) { e = { error: error }; } - finally { - try { - if (r && !r.done && (m = i["return"])) m.call(i); - } - finally { if (e) throw e.error; } - } - return ar; -}; -var __spreadArray = (this && this.__spreadArray) || function (to, from, pack) { - if (pack || arguments.length === 2) for (var i = 0, l = from.length, ar; i < l; i++) { - if (ar || !(i in from)) { - if (!ar) ar = Array.prototype.slice.call(from, 0, i); - ar[i] = from[i]; - } - } - return to.concat(ar || Array.prototype.slice.call(from)); -}; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.rules = exports.tips = exports.FColor = exports.text = exports.prefixes = exports.GamemodeNames = exports.Gamemode = exports.FishServer = exports.mapRepoURLs = exports.Mode = exports.backendIP = exports.stopAntiEvadeTime = exports.heuristics = exports.adminNames = exports.multiCharSubstitutions = exports.substitutions = exports.bannedWords = void 0; -var globals_1 = require("/globals"); -var ranks_1 = require("/ranks"); -var funcs_1 = require("/funcs"); -function processBannedWordList(words) { - return words.map(function (word) { - return (typeof word == "string" || word instanceof RegExp) ? - [word, []] - : [word[0], word.slice(1)]; - }); -} -exports.bannedWords = { - // README: Information on how to update this list - // All words must be in *lowercase*. - // Words need to be separated by commas, even if they are on a new line. - // If a word can be contained in another word that should be allowed (the scunthorpe problem), - // surround the entire thing in square brackets, then list out the words after - // like this: ["badw", "goodbadw"] - /** Normal: banned always. */ - normal: processBannedWordList([ - "fanum tax", "gyatt", ["rizz", "grizzly", "frizz", "horizzon"], "skibidi", //With love, DarthScion - //>:( -dart - // "uwu", //lol - "nig" + "ger", "nig" + "ga", "niger", "ni8" + "8er", "nig" + "gre", "негр", "ниг" + "гер", "нигер", "нігер", "ніг" + "гер", /\bnegr\b/, //our apologies to citizens of the Republic of Niger - ["ni" + "ga", "anniga", "inniga", "unniga", "aniga", "iniga", "eniga", "oniga"], - "re" + "tard", - 'kill yourself', 'kill urself', /\bkys\b/, - "kill blacks", "heil hitler", "heil nazis", "heil the nazis", "sieg heil", "hail hitler", "hail nazis", "hail the nazis", "sieg hail", /\b1488\b/, //nazi-related words - ["co" + "ck", "cockroach", "poppycock", "cocktail"], "suck dick", "sucking dick", - "iamasussyimposter", - ["cu" + "nt", "scunthorpe"], - ["penis", "peniston"], - "hawk tuah", - ["rape", "grape", "therap", "drape", "scrape", "trapez", "earrape", "atrape", "traped"], - ["raping", "draping", "graping", "scraping", "craping"], - /\bf(a)g\b/, "fa" + "gg" + "ot", - /\bc(u)m\b/, ["semen", "sement", "horsemen", "housemen", "defensemen", "those", "menders"], - ["porn", "maporn"], - "futa" + "nari", "futa", - "ur gay", "your gay", "youre gay", "you're gay", - "gooning", "gooner", "dildo", "loli", /\banal\b/, "cunny" - ]), - /** Strict: banned in names and for players with a chat strictness level of 'strict'. */ - strict: processBannedWordList([ - "fu" + "ck", "bi" + "tch", ["sh" + "it", "harshit"], /\ba(s)s\b/, "as" + "shole", ["dick", "medick", "dickens"], - ]), - /** Names: banned only in names. */ - names: processBannedWordList([ - "sex", /\bgoldberg\b/, "hitler", "stalin", "putin", "lenin", /^something$/, "[something]", "[[something]", "卐", "diddy", "epstein", - globals_1.uuidPattern, globals_1.ipPattern, globals_1.ipPortPattern - ]), - /** autoWhack: new players saying one of these words will be automatically stopped and muted. Comes with \b so no need to add it. */ - autoWhack: [ - "nig" + "ger", "nig" + "ga", "ni8" + "8er", "nig" + "g3r", "hit" + "ler", "fa" + "gg" + "ot", "nazis", "негр", "ниг" + "гер", "нигер", "нігер", "ніг" + "гер", "negr" - ], -}; -//for some reason the external mindustry server does not read the files correctly, so we can only use ASCII -exports.substitutions = Object.fromEntries(Object.entries({ - "a": "\u0430\u1E9A\u1EA1\u1E01\u00E4\u03B1@\u0101\u0103\u0105\u03AC", - "b": "\u1E03\u1E07\u1E03\u0253\u0185", - "c": "\u0441\u217D\u00E7\u03C2\u010B", - "d": "\u217E\u1E0B\u1E11\u010F\u1E13\u1E0D\u1E0F\u0257\u20AB\u0256\u056A", - "e": "\u0435\u1E1B\u0113\u1E17\u0229\u0451\u011B\u0205\u03F5\u03B5\u025B3", - "f": "\u1E1F\u0493\u0192", - "g": "\u0581\u0123\u01F5\u0260\u011F\u011D\u01E5\u1E21", - "h": "\u1E23\u021F\u1E25\u1E2B\u0570\u056B\u1E29\u0266\u1E27\u1E23\u0266\u1E96\u0127", - "i": "\u0456\u012F\u03B9\u1EC9\u1F31\u1F77\u012B1\u00A1\u0457\u0390\u03CA", - "j": "\u0458\u029D\u0575\u025F\u0135\u0237\u01F0", - "k": "\u049F\u1E31\u0137\u0138\u043A\u0199\u049D", - "l": "\u217C\u1E3D\u1E3B\u013E\u0140\u013C\u1E39\u0142\u038A\u00CC\u00CD\u00CE\u00CF\u0128\u012A\u012C\u012E\u0130\u0196\u0208\u020A\u0399\u03AA\u0406\u0407\u04C0\u04CF\u1E2C\u1EC8\u1F38\u1F39\u1FD8\u1FD9\u1FDA\u01D0\u03B9", - "m": "\u217F\u1E43\u0271\u1E41\u1E3F", - "n": "\u00F1\u0144\u0146\u0148\u0149\u01F9\u03AE\u03B7\u0578\u057C\u0580\u1E45\u1E47\u03A0", - "o": "\u00F2\u1ED9\u1EDB\u1EDD\u1EDF\u1EE1\u1EE3\u1F40\u1F41\u1F42\u1F43\u1F44\u1F45\u1F78\u1F79\u03C3\u0E50\u00F6\u014D\u014F\u0151\u01A1\u01D2\u03BF\u03CC0", - "p": "\u03C1\u0440\u048F\u1E55\u1E57\u1FE4\u1FE5\u2374", - "q": "\u051B\u0563\u0566\u0563\u0566", - "r": "\u0155\u0157\u0159\u0211\u0213\u027C\u027D\u0433\u0453\u0491\u04F7\u1E59\u1E5B\u1E5D", - "s": "\u015B\u015D\u015F\u0161\u0219\u0282\u0455\u1E61\u1E63\u1E65\u1E67\u1E69\u03C2", - "t": "\u0163\u0165\u01AB\u021B\u0288\u1E6B\u1E6D\u1E6F\u1E71\u1E97\u0236\u2020\u04AD", - "u": "\u00B5\u03BC\u00F9\u00FA\u00FB\u00FC\u0169\u016B\u016D\u016F\u0171\u0173\u01B0\u01D4\u0215\u0217\u0265\u1EE9\u1EEB\u1EED\u1EEF\u1EF1\u03BC\u03C5\u03CB\u03CD", - "v": "\u03BD\u0475\u0477\u1E7D\u1E7F\u2174\u2228\u03C5\u03CB\u03CD", - "w": "\u0175\u051D\u1E81\u1E83\u1E85\u1E87\u1E89\u1E98\u03C9\u03CE", - "x": "\u0445\u04B3\u1E8B\u1E8D\u03C7", - "y": "\u00FD\u00FF\u0177\u01B4\u0233\u03B3\u0443\u045E\u04EF\u04F1\u04F3\u1E8F\u1E99\u1EF3\u1EF5\u1EF7\u1EF9\u04AF\u04B1", - "z": "\u017A\u017C\u017E\u01B6\u0225\u0290\u1E91\u1E93\u1E95", - "A": "\u1E00\u1EA0\u1EA2\u1EA4\u1EA6\u1EA8\u1EAC\u1F08\u1F09\u1F88\u1F89\u1FB8\u1FB9\u1FBA\u1FBC\u212B\u0100\u0102\u0104\u0386\u0391\u0410", - "B": "\u0181\u0392\u0412\u1E02\u1E04\u1E06", - "C": "\u00C7\u0106\u0108\u010A\u010C\u0187\u0421\u04AA\u1E08\u216D\u03F9", - "D": "\u00D0\u010E\u0110\u0189\u018A\u1E0A\u1E0C\u1E0E\u216E", - "E": "\u00C8\u00C9\u00CA\u00CB\u0112\u0114\u0116\u0118\u011A\u0204\u0206\u0228\u0395\u0400\u0415\u04D6\u1E18\u0510\u2107\u0190\u1F19\u1FC8\u0404\u0388\u03AD\u03B5\u03B7\u0415", - "F": "\u03DC\u1E1E\u0492\u0191\u0492\u0493", - "G": "\u011C\u011E\u0120\u0122\u0193\u01E6\u01F4\u1E20", - "H": "\u0124\u021E\u0397\u041D\u04A2\u04A4\u04C7\u04C9\u1E22\u1E24\u1E26\u1E28\u1E2A\u1FCC\uA726\u0389", - "I": "\u038A\u00CC\u00CD\u00CE\u00CF\u0128\u012A\u012C\u012E\u0130\u0196\u0208\u020A\u0399\u03AA\u0406\u0407\u04C0\u04CF\u1E2C\u1EC8\u1F38\u1F39\u1FD8\u1FD9\u1FDA\u01D0\u217C\u1E3D\u1E3B\u026B\u013E\u0140\u013C\u1E39\u038A", - "J": "\u0134\u0408\u037F", - "K": "\u0136\u0198\u01E8\u039A\u040C\u041A\u051E\u1E30\u1E32\u1E34\u20AD\u212A\u03BA", - "L": "\u0139\u013B\u013D\u013F\u0141\u053C\u1E36\u1E38\u1E3A\u1E3C\u216C", - "M": "\u039C\u041C\u04CD\u1E3E\u1E40\u1E42\u216F", - "N": "\u00D1\u0143\u0145\u0147\u01F8\u039D\u1E44\u1E46\u1E48\u1E4A\u019D", - "O": "\u03B8\u236C\u00D2\u00D3\u00D4\u00D5\u00D6\u014C\u014E\u0150\u019F\u01A0\u01D1\u020E\u022E\u0230\u0398\u039F\u041E\u04E6\u0555\u1ECC\u1ECE\u1ED4\u1FF9\u038C", - "P": "\u01A4\u03A1\u0420\u048E\u1E54\u1E56\u1FEC", - "Q": "\u051A", - "R": "\u0154\u0156\u0158\u0210\u0212\u1E58\u1E5A\u1E5C\u1E5E\u211E\u024C\u2C64", - "S": "\u015A\u015C\u015E\u0160\u0218\u0405\u054F\u1E60\u1E62\u1E68\u1E64\u1E66", - "T": "\u0162\u0164\u0166\u01AE\u021A\u03A4\u0422\u04AC\u1E6A\u1E6C\u1E6E\u1E70\u038A\u1FDB\uA68C\u0372\u0373\u03C4", - "U": "\u016A\u016C\u016E\u0170\u0172\u01AF\u01D3\u1EE8\u1EEA\u1EEC\u1EEE\u0544", - "V": "\u0474\u0476\u1E7C\u1E7E\u22C1\u2164", - "W": "\u051C\u1E80\u1E82\u1E84\u1E86\u1E88\u019C", - "X": "\u03A7\u0425\u04B2\u1E8A\u1E8C\u2169", - "Y": "\u01B3\u0232\u03A5\u03AB\u03D3\u0423\u04AE\u04B0\u1E8E\u1EF2\u1EF4\u038E", - "Z": "\u0179\u017B\u017D\u0224\u0396\u1E90\u1E92\u1E94", - "": "\u200B\u200C\u200D", -}).map(function (_a) { - var _b = __read(_a, 2), char = _b[0], alts = _b[1]; - return alts.split("").map(function (alt) { return [alt, char]; }); -}).flat(1)); -exports.multiCharSubstitutions = [ - [/\|-\|/g, "H"] -]; -//#endregion -//#region misc -/** Used for anti-impersonation. Make sure to replace numbers with letters, for example, balam314 -> balamei4. */ -exports.adminNames = ["fish", "balamei4", "clashgone", "darthscion", "firefridge", "aricia", "rawsewage", "skeledragon", "edh8e", "everydayhuman8e", "benjamonsrl"]; -exports.heuristics = { - /** Will trip if more than this many blocks are broken within 25 seconds of joining. */ - blocksBrokenAfterJoin: 40, -}; -exports.stopAntiEvadeTime = funcs_1.Duration.minutes(30); -exports.backendIP = '45.79.202.111:5082'; -exports.Mode = { - localDebug: new Fi("config/.debug").exists(), - noBackend: new Fi("config/.debug").exists() && !exports.backendIP.startsWith("127.0.0.1:"), - isChristmas: new Date().getMonth() == 11, - isAprilFools: new Date().getMonth() == 3 && new Date().getDate() == 1, -}; -//#endregion -//#region servers -/** Stores the repository url for the maps for each gamemode. */ -exports.mapRepoURLs = { - attack: "https://api.github.com/repos/Fish-Community/fish-maps/contents/attack", - survival: "https://api.github.com/repos/Fish-Community/fish-maps/contents/survival", - pvp: "https://api.github.com/repos/Fish-Community/fish-maps/contents/pvp", - hexed: "https://api.github.com/repos/Fish-Community/fish-maps/contents/hexed", - sandbox: "https://api.github.com/repos/Fish-Community/fish-maps/contents/sandbox", - hardcore: "https://api.github.com/repos/Fish-Community/fish-maps/contents/hardcore", - testsrv: "https://api.github.com/repos/Fish-Community/fish-maps/contents/testsrv", - minigame: "https://api.github.com/repos/Fish-Community/fish-maps/contents/minigame", -}; -/** Stores the names and addresses of each active server. */ -var FishServer = /** @class */ (function () { - function FishServer(name, ip, port, aliases, - /** If set, this permission is required to switch to or get information about this server. */ - requiredPerm) { - this.name = name; - this.ip = ip; - this.port = port; - this.aliases = aliases; - this.requiredPerm = requiredPerm; - FishServer.all.push(this); - } - FishServer.byName = function (input) { - var _a; - input = input.toLowerCase(); - return (_a = FishServer.all.find(function (s) { return s.aliases.concat(s.name).includes(input); })) !== null && _a !== void 0 ? _a : null; - }; - FishServer.all = []; - FishServer.attack = new FishServer("attack", "162.248.100.98", "6567", ["attac", "atack", "atak", "atck", "atk", "a"]); - FishServer.survival = new FishServer("survival", "162.248.101.95", "6567", ["surviv", "surv", "sur", "su", "s", "sl"]); - FishServer.pvp = new FishServer("pvp", "162.248.102.101", "6567", ["pv", "p", "v", "playerversusplayer"]); - FishServer.sandbox = new FishServer("sandbox", "162.248.101.53", "6567", ["sand", "box", "sa", "sb"]); - FishServer.hexed = new FishServer("hexed", "162.248.100.133", "6567", ["h", "hx", "hxd", "hpvp", "hxpvp", "hexpvp"]); - FishServer.minigame = new FishServer("minigame", "162.248.101.116", "6567", ["m", "mg", "mini", "minig", "mgame", "mng", "minigame", "mpvp"]); - FishServer.testing = new FishServer("testing", "162.248.101.52", "6567", ["test", "testsrv", "t", "testingserver", "testserver"]); - return FishServer; -}()); -exports.FishServer = FishServer; -; -/** Stores functions that return whether the specified gamemode is the current gamemode. */ -exports.Gamemode = { - attack: function () { return exports.Gamemode.name() == "attack"; }, - survival: function () { return exports.Gamemode.name() == "survival"; }, - pvp: function () { return exports.Gamemode.name() == "pvp" || exports.Gamemode.name() == "hexed" || exports.Gamemode.name() == "minigame"; }, - sandbox: function () { return exports.Gamemode.name() == "sandbox"; }, - hexed: function () { return exports.Gamemode.name() == "hexed"; }, - hardcore: function () { return exports.Gamemode.name() == "hardcore"; }, - testsrv: function () { return exports.Gamemode.name() == "testsrv"; }, - minigame: function () { return exports.Gamemode.name() == "minigame"; }, - name: function () { return Core.settings.get("mode", Vars.state.rules.mode().name()); }, -}; -exports.GamemodeNames = Object.keys(exports.Gamemode).filter(function (x) { return x !== "name"; }); -//#endregion -//#region text content -exports.prefixes = { - marked: '[yellow]\u26A0[scarlet]Marked Griefer[]\u26A0[]', - flagged: '[yellow]\u26A0[orange]Flagged[]\u26A0[]', - muted: '[white](muted)', -}; -exports.text = { - discordURL: "https://discord.gg/VpzcYSQ33Y", - membershipURL: "https://patreon.com/FishServers", - reportsPing: "<@&1040193678817378305>", - welcomeMessage: function () { return (0, funcs_1.random)([ - "[gold]Welcome![]" - ]); }, - chatFilterReplacement: { - message: function () { return "I really hope everyone is having a fun time :) <3"; }, - messageShort: function () { return "I hope we're all having a fun time :) <3"; }, - highlight: function () { return "[#f456f]"; }, - // `[#22AA22]Merry [#EC4444]Christmas!`, - // `[gold]Happy Holidays! [white]•*•☃*•`, - // `[gold]Happy Hanukkah!`, - // `[#EC4444]May your days be merry and bright!`, - // `[gold]Merry Fishmas! >|||> [white]☃`, - // `[gold]Deck the halls with lots of fun!`, - // `[gold]>|||> Fish wishes you [#22AA22]a merry Christmas!`, - // ]), - // chatFilterReplacement: { - // message: () => random([ - // `Have a holly jolly Christmas :) <3`, - // `I really hope everyone is jolly for the season! :D`, - // `All I want for Christmaaaaaaas is everyone having a fun time! :)`, - // `Remember to be nice in chat: Santa is watching! <3`, - // `All I want for Christmas is Fish! >|||>`, - // ]), - // highlight: () => random([ - // `[#22AA22]`, `[#EC4444]`, `[#FFFFFF]` - // ]), - }, - dataFetchFailed: "[scarlet]\u26A0 Data fetch failed!\n[white]Please disconnect and rejoin the server if you encounter further issues, such as missing rank or statistics.", -}; -//TODO use this -exports.FColor = (function (data) { - return Object.fromEntries(Object.entries(data).map(function (_a) { - var _b = __read(_a, 2), k = _b[0], c = _b[1]; - return [k, function (str) { - var varChunks = []; - for (var _i = 1; _i < arguments.length; _i++) { - varChunks[_i - 1] = arguments[_i]; - } - return str != null ? - "".concat(c).concat(Array.isArray(str) ? String.raw.apply(String, __spreadArray([{ raw: str }], __read(varChunks.map(function (v) { return String(v) + c; })), false)) : str, "[]") - : c; - } - ]; - })); -})({ - discord: "[#7289DA]", - /** Used for tips and welcome messages. */ - tip: "[gold]", - member: "[pink]", - achievement: "[lime]", -}); -/** Tips that are shown to players randomly. */ -exports.tips = { - ads: [ - "".concat(exports.FColor.member(templateObject_1 || (templateObject_1 = __makeTemplateObject(["Fish Membership"], ["Fish Membership"]))), " subscribers can access the ").concat(exports.FColor.member(templateObject_2 || (templateObject_2 = __makeTemplateObject(["/pet"], ["/pet"]))), " command, which spawns a merui that follows you around. Get a Fish Membership at[sky] ").concat(exports.text.membershipURL, " []"), - "".concat(exports.FColor.member(templateObject_3 || (templateObject_3 = __makeTemplateObject(["Fish Membership"], ["Fish Membership"]))), " subscribers can use the ").concat(exports.FColor.member(templateObject_4 || (templateObject_4 = __makeTemplateObject(["/highlight"], ["/highlight"]))), " command, which turns your chat messages to a color of your choice. Get a Fish Membership at[sky] ").concat(exports.text.membershipURL, " []"), - "".concat(exports.FColor.member(templateObject_5 || (templateObject_5 = __makeTemplateObject(["Fish Membership"], ["Fish Membership"]))), " subscribers can use the ").concat(exports.FColor.member(templateObject_6 || (templateObject_6 = __makeTemplateObject(["/rainbow"], ["/rainbow"]))), " command, which makes your name flash different colors. Get a Fish Membership at[sky] ").concat(exports.text.membershipURL, " []"), - "Want to support the server and get some perks? Get a ".concat(exports.FColor.member(templateObject_7 || (templateObject_7 = __makeTemplateObject(["Fish Membership"], ["Fish Membership"]))), " at[sky] ").concat(exports.text.membershipURL, " []"), - "Join our ".concat(exports.FColor.discord(templateObject_8 || (templateObject_8 = __makeTemplateObject(["Discord server"], ["Discord server"]))), "[]! ").concat(exports.FColor.discord(exports.text.discordURL), " or type ").concat(exports.FColor.discord(templateObject_9 || (templateObject_9 = __makeTemplateObject(["/discord"], ["/discord"])))), - ], - normal: [ - //commands - "You can spawn an [scarlet]Ohno[] with the [scarlet]/ohno[] command. Ohnos are harmless creatures that were created by fusing an alpha and an atrax.", - "Ohnos cannot be spawned near enemy buildings, because they are peaceful and do not want to be used for attacks.", - "You can use [white]/tp[] to teleport directly to any other player! (But only when you're in a core unit)", - "You can unload bulk conveyors (\uF819 or \uF732) with unloaders (\uF864 or \uF731).", - "Hate boulders? You can remove them with [white]/clean[].", - "You can check our rules at any time by running [white]/rules[].", - // `You can kill your unit by running [white]/die[].`, - "We have a tilelog system to help catch griefers. Run [white]/tilelog[], then click a tile to see what's happened there.", - "Run [white]/tilelog 1[] to check the tile history of multiple tiles.", - "Tilelog stores when a building is placed, broken, rotated, configured, and picked up/dropped by a payload unit. Access it with [white]/tilelog[]", - "Tilelog doesn't just log tile actions, it also logs unit deaths! Access it with [white]/tilelog[]", - "Did someone kill a T5 with commands? Run [white]/aoelog 0 15 killed[] to check tilelogs for unit deaths in a large area.", - "Aoelog can show the history of tiles in an area. Select the opposite corners of a rectangle to view the history of its tiles.", - "Aoelog is the plural version of tilelog, access it via [white]/aoelog[]", - "You can mark yourself as AFK(away from keyboard) with [white]/afk[].", - "Run /survival, /attack, /pvp, /sandbox, /hexed or /minigame to quickly change to another server.", - "Need to get rid of an active griefer? Use [#6FFC7C]/s[] to send a message to all staff members across all servers.", - "Use [white]/help to get more information about a specific command.", - "If you want to send a message to just one player, you can use the [white]/msg[] command.", - "Use [white]/r[] to reply to a message sent by another player.", - "[white]/trail[] can be used to give your unit a trail of particle effects.", - "Run [white]/ranks[] to see all the ranks on our server.", - "Is someone impersonating a staff member? Run [white]/rank[] to see their real rank.", - "Don't like the map? Vote to change it with [white]/rtv[].", - "If you want to end the current map, DO NOT BREAK DEFENCES! Vote to change the map with [white]/rtv[].", - //misc - "Anyone attempting to impersonate a ranked player, or the server, will have [scarlet]SUSSY IMPOSTOR[] prepended to their name. Beware!", - "Griefers will often be found with the text ".concat(exports.prefixes.marked, " prepended to their name: they are harmless and cannot grief again."), - "Don't votekick ".concat(exports.prefixes.marked.slice(0, -3), "[][scarlet]s [gold]if they aren't breaking the rules: they are incapable of griefing more."), - "Players marked as ".concat(exports.prefixes.flagged, " have been flagged as suspicious by our detection systems, but they may not be griefers."), - "Need to appeal a moderation action? Join the discord at ".concat(exports.FColor.discord(exports.text.discordURL), " or type /discord"), - "Want to send the phrase [white]\"/command\"[] in chat? Type [white]\"./command\"[] and the [white].[] will be removed.", - "All commands with a player as an argument support using a menu to specify the player. Just run the command leaving the argument blank (using two spaces if necessary), and a menu will show up.", - "Players with a ".concat(ranks_1.Rank.trusted.prefix, " in front of their name aren't staff members, but they do have extra powers."), - "Staff members will have the following prefixes in front of their name: ".concat(ranks_1.Rank.manager.prefix, ", ").concat(ranks_1.Rank.admin.prefix, ", ").concat(ranks_1.Rank.mod.prefix), - "Wave cooldown too long? Skip the wait with [white]/vnw[]", - "You can tell new players not to break power voids with [white]/void[]", - "You can add [pink]color[] to things with color tags! Try typing \"[[".concat(["pink", "green", "cyan", "acid", "royal", "coral"][Math.floor(Math.random() * 6)], "]Hello\" in chat, and see what happens!") - ], - christmas: [ - "Remember to be nice in-game, Santa is watching!", - "Santa's checking his list, so be nice!", - "Have a merry christmas and a happy new year!", - "Fish becomes a bit more jolly around Christmastime!", - "Many server maps have been changed for the season.", - ], - staff: [], -}; -exports.rules = [ - "# 1: [red]No griefing. This refers to intentionally hurting your own team in any way.", - "# 2: [orange]False votekicking isn't allowed. Avoid votekicking if there's an active staff member in the server.", - "# 3: [yellow]Gore, pornography, suggestive content and jokes, and flashing images aren't allowed here. Being horny and a creep in chat will result in a ban.", - "# 4: [green]Do not harass other people. We have zero tolerance for any bigotry. Please respect everyone.", - "# 5: [#00D8D8]Spamming is prohibited. Be reasonable with messaging staff in-game. Misuse may result in a mute.", - "# 6: [blue]Impersonating people or ranks is prohibited.", - "# 7: [purple]Talking about controversial or sensitive topics is not allowed in-game. Hate symbols, such as swastikas, are not permitted.", - "# 8: [pink]No uncomfortable trolling or intentionally causing chaos. This includes any actions or messages that create an unpleasant atmosphere.", - "Failure to follow these rules will result in consequences: likely a ".concat(exports.prefixes.marked, " tag for any game disruption, mute for broken chat rules, and bans for repeated offenses or bypasses.") -].map(function (r) { return "[white]".concat(r); }); -var templateObject_1, templateObject_2, templateObject_3, templateObject_4, templateObject_5, templateObject_6, templateObject_7, templateObject_8, templateObject_9; -//#endregion +"use strict"; +/* +Copyright © BalaM314, 2026. All Rights Reserved. +This file contains configurable constants. +*/ +var __makeTemplateObject = (this && this.__makeTemplateObject) || function (cooked, raw) { + if (Object.defineProperty) { Object.defineProperty(cooked, "raw", { value: raw }); } else { cooked.raw = raw; } + return cooked; +}; +var __read = (this && this.__read) || function (o, n) { + var m = typeof Symbol === "function" && o[Symbol.iterator]; + if (!m) return o; + var i = m.call(o), r, ar = [], e; + try { + while ((n === void 0 || n-- > 0) && !(r = i.next()).done) ar.push(r.value); + } + catch (error) { e = { error: error }; } + finally { + try { + if (r && !r.done && (m = i["return"])) m.call(i); + } + finally { if (e) throw e.error; } + } + return ar; +}; +var __spreadArray = (this && this.__spreadArray) || function (to, from, pack) { + if (pack || arguments.length === 2) for (var i = 0, l = from.length, ar; i < l; i++) { + if (ar || !(i in from)) { + if (!ar) ar = Array.prototype.slice.call(from, 0, i); + ar[i] = from[i]; + } + } + return to.concat(ar || Array.prototype.slice.call(from)); +}; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.noMultiVnwTag = exports.rules = exports.tips = exports.FColor = exports.text = exports.prefixes = exports.GamemodeNames = exports.Gamemode = exports.FishServer = exports.mapRepoURLs = exports.Mode = exports.campaigns = exports.Campaign = exports.throwbackMapFileNames = exports.backendIP = exports.stopAntiEvadeTime = exports.heuristics = exports.adminNames = exports.multiCharSubstitutions = exports.substitutions = exports.bannedWords = void 0; +var globals_1 = require("/globals"); +var ranks_1 = require("/ranks"); +var funcs_1 = require("/funcs"); +function processBannedWordList(words) { + return words.map(function (word) { + return (typeof word == "string" || word instanceof RegExp) ? + [word, []] + : [word[0], word.slice(1)]; + }); +} +exports.bannedWords = { + // README: Information on how to update this list + // All words must be in *lowercase*. + // Words need to be separated by commas, even if they are on a new line. + // If a word can be contained in another word that should be allowed (the scunthorpe problem), + // surround the entire thing in square brackets, then list out the words after + // like this: ["badw", "goodbadw"] + /** Normal: banned always. */ + normal: processBannedWordList([ + "fanum tax", "gyatt", ["rizz", "grizzly", "frizz", "horizzon"], "skibidi", //With love, DarthScion + //>:( -dart + // "uwu", //lol + "nig" + "ger", "nig" + "ga", "niger", "ni8" + "8er", "nig" + "gre", "негр", "ниг" + "гер", "нигер", "нігер", "ніг" + "гер", /\bnegr\b/, //our apologies to citizens of the Republic of Niger + ["ni" + "ga", "anniga", "inniga", "unniga", "aniga", "iniga", "eniga", "oniga"], + "re" + "tard", + 'kill yourself', 'kill urself', /\bkys\b/, + "kill blacks", "heil hitler", "heil nazis", "heil the nazis", "sieg heil", "hail hitler", "hail nazis", "hail the nazis", "sieg hail", /\b1488\b/, //nazi-related words + ["co" + "ck", "cockroach", "poppycock", "cocktail"], "suck dick", "sucking dick", + "iamasussyimposter", + ["cu" + "nt", "scunthorpe"], + ["penis", "peniston"], + "hawk tuah", + ["rape", "grape", "therap", "drape", "scrape", "trapez", "earrape", "atrape", "traped"], + ["raping", "draping", "graping", "scraping", "craping"], + /\bf(a)g\b/, "fa" + "gg" + "ot", + /\bc(u)m\b/, ["semen", "sement", "horsemen", "housemen", "defensemen", "those", "menders"], + ["porn", "maporn"], + "futa" + "nari", "futa", + "ur gay", "your gay", "youre gay", "you're gay", + "gooning", "gooner", "dildo", "loli", /\banal\b/, "cunny" + ]), + /** Strict: banned in names and for players with a chat strictness level of 'strict'. */ + strict: processBannedWordList([ + "fu" + "ck", "bi" + "tch", ["sh" + "it", "harshit"], /\ba(s)s\b/, "as" + "shole", ["dick", "medick", "dickens"], + ]), + /** Names: banned only in names. */ + names: processBannedWordList([ + "sex", /\bgoldberg\b/, "hitler", "stalin", "putin", "lenin", /^something$/, "[something]", "[[something]", "卐", "diddy", "epstein", + globals_1.uuidPattern, globals_1.ipPattern, globals_1.ipPortPattern + ]), + /** autoWhack: new players saying one of these words will be automatically stopped and muted. Comes with \b so no need to add it. */ + autoWhack: [ + "nig" + "ger", "nig" + "ga", "ni8" + "8er", "nig" + "g3r", "hit" + "ler", "fa" + "gg" + "ot", "nazis", "негр", "ниг" + "гер", "нигер", "нігер", "ніг" + "гер", "negr" + ], +}; +//for some reason the external mindustry server does not read the files correctly, so we can only use ASCII +exports.substitutions = Object.fromEntries(Object.entries({ + "a": "\u0430\u1E9A\u1EA1\u1E01\u00E4\u03B1@\u0101\u0103\u0105\u03AC", + "b": "\u1E03\u1E07\u1E03\u0253\u0185", + "c": "\u0441\u217D\u00E7\u03C2\u010B", + "d": "\u217E\u1E0B\u1E11\u010F\u1E13\u1E0D\u1E0F\u0257\u20AB\u0256\u056A", + "e": "\u0435\u1E1B\u0113\u1E17\u0229\u0451\u011B\u0205\u03F5\u03B5\u025B3", + "f": "\u1E1F\u0493\u0192", + "g": "\u0581\u0123\u01F5\u0260\u011F\u011D\u01E5\u1E21", + "h": "\u1E23\u021F\u1E25\u1E2B\u0570\u056B\u1E29\u0266\u1E27\u1E23\u0266\u1E96\u0127", + "i": "\u0456\u012F\u03B9\u1EC9\u1F31\u1F77\u012B1\u00A1\u0457\u0390\u03CA", + "j": "\u0458\u029D\u0575\u025F\u0135\u0237\u01F0", + "k": "\u049F\u1E31\u0137\u0138\u043A\u0199\u049D", + "l": "\u217C\u1E3D\u1E3B\u013E\u0140\u013C\u1E39\u0142\u038A\u00CC\u00CD\u00CE\u00CF\u0128\u012A\u012C\u012E\u0130\u0196\u0208\u020A\u0399\u03AA\u0406\u0407\u04C0\u04CF\u1E2C\u1EC8\u1F38\u1F39\u1FD8\u1FD9\u1FDA\u01D0\u03B9", + "m": "\u217F\u1E43\u0271\u1E41\u1E3F", + "n": "\u00F1\u0144\u0146\u0148\u0149\u01F9\u03AE\u03B7\u0578\u057C\u0580\u1E45\u1E47\u03A0", + "o": "\u00F2\u1ED9\u1EDB\u1EDD\u1EDF\u1EE1\u1EE3\u1F40\u1F41\u1F42\u1F43\u1F44\u1F45\u1F78\u1F79\u03C3\u0E50\u00F6\u014D\u014F\u0151\u01A1\u01D2\u03BF\u03CC0", + "p": "\u03C1\u0440\u048F\u1E55\u1E57\u1FE4\u1FE5\u2374", + "q": "\u051B\u0563\u0566\u0563\u0566", + "r": "\u0155\u0157\u0159\u0211\u0213\u027C\u027D\u0433\u0453\u0491\u04F7\u1E59\u1E5B\u1E5D", + "s": "\u015B\u015D\u015F\u0161\u0219\u0282\u0455\u1E61\u1E63\u1E65\u1E67\u1E69\u03C2", + "t": "\u0163\u0165\u01AB\u021B\u0288\u1E6B\u1E6D\u1E6F\u1E71\u1E97\u0236\u2020\u04AD", + "u": "\u00B5\u03BC\u00F9\u00FA\u00FB\u00FC\u0169\u016B\u016D\u016F\u0171\u0173\u01B0\u01D4\u0215\u0217\u0265\u1EE9\u1EEB\u1EED\u1EEF\u1EF1\u03BC\u03C5\u03CB\u03CD", + "v": "\u03BD\u0475\u0477\u1E7D\u1E7F\u2174\u2228\u03C5\u03CB\u03CD", + "w": "\u0175\u051D\u1E81\u1E83\u1E85\u1E87\u1E89\u1E98\u03C9\u03CE", + "x": "\u0445\u04B3\u1E8B\u1E8D\u03C7", + "y": "\u00FD\u00FF\u0177\u01B4\u0233\u03B3\u0443\u045E\u04EF\u04F1\u04F3\u1E8F\u1E99\u1EF3\u1EF5\u1EF7\u1EF9\u04AF\u04B1", + "z": "\u017A\u017C\u017E\u01B6\u0225\u0290\u1E91\u1E93\u1E95", + "A": "\u1E00\u1EA0\u1EA2\u1EA4\u1EA6\u1EA8\u1EAC\u1F08\u1F09\u1F88\u1F89\u1FB8\u1FB9\u1FBA\u1FBC\u212B\u0100\u0102\u0104\u0386\u0391\u0410", + "B": "\u0181\u0392\u0412\u1E02\u1E04\u1E06", + "C": "\u00C7\u0106\u0108\u010A\u010C\u0187\u0421\u04AA\u1E08\u216D\u03F9", + "D": "\u00D0\u010E\u0110\u0189\u018A\u1E0A\u1E0C\u1E0E\u216E", + "E": "\u00C8\u00C9\u00CA\u00CB\u0112\u0114\u0116\u0118\u011A\u0204\u0206\u0228\u0395\u0400\u0415\u04D6\u1E18\u0510\u2107\u0190\u1F19\u1FC8\u0404\u0388\u03AD\u03B5\u03B7\u0415", + "F": "\u03DC\u1E1E\u0492\u0191\u0492\u0493", + "G": "\u011C\u011E\u0120\u0122\u0193\u01E6\u01F4\u1E20", + "H": "\u0124\u021E\u0397\u041D\u04A2\u04A4\u04C7\u04C9\u1E22\u1E24\u1E26\u1E28\u1E2A\u1FCC\uA726\u0389", + "I": "\u038A\u00CC\u00CD\u00CE\u00CF\u0128\u012A\u012C\u012E\u0130\u0196\u0208\u020A\u0399\u03AA\u0406\u0407\u04C0\u04CF\u1E2C\u1EC8\u1F38\u1F39\u1FD8\u1FD9\u1FDA\u01D0\u217C\u1E3D\u1E3B\u026B\u013E\u0140\u013C\u1E39\u038A", + "J": "\u0134\u0408\u037F", + "K": "\u0136\u0198\u01E8\u039A\u040C\u041A\u051E\u1E30\u1E32\u1E34\u20AD\u212A\u03BA", + "L": "\u0139\u013B\u013D\u013F\u0141\u053C\u1E36\u1E38\u1E3A\u1E3C\u216C", + "M": "\u039C\u041C\u04CD\u1E3E\u1E40\u1E42\u216F", + "N": "\u00D1\u0143\u0145\u0147\u01F8\u039D\u1E44\u1E46\u1E48\u1E4A\u019D", + "O": "\u03B8\u236C\u00D2\u00D3\u00D4\u00D5\u00D6\u014C\u014E\u0150\u019F\u01A0\u01D1\u020E\u022E\u0230\u0398\u039F\u041E\u04E6\u0555\u1ECC\u1ECE\u1ED4\u1FF9\u038C", + "P": "\u01A4\u03A1\u0420\u048E\u1E54\u1E56\u1FEC", + "Q": "\u051A", + "R": "\u0154\u0156\u0158\u0210\u0212\u1E58\u1E5A\u1E5C\u1E5E\u211E\u024C\u2C64", + "S": "\u015A\u015C\u015E\u0160\u0218\u0405\u054F\u1E60\u1E62\u1E68\u1E64\u1E66", + "T": "\u0162\u0164\u0166\u01AE\u021A\u03A4\u0422\u04AC\u1E6A\u1E6C\u1E6E\u1E70\u038A\u1FDB\uA68C\u0372\u0373\u03C4", + "U": "\u016A\u016C\u016E\u0170\u0172\u01AF\u01D3\u1EE8\u1EEA\u1EEC\u1EEE\u0544", + "V": "\u0474\u0476\u1E7C\u1E7E\u22C1\u2164", + "W": "\u051C\u1E80\u1E82\u1E84\u1E86\u1E88\u019C", + "X": "\u03A7\u0425\u04B2\u1E8A\u1E8C\u2169", + "Y": "\u01B3\u0232\u03A5\u03AB\u03D3\u0423\u04AE\u04B0\u1E8E\u1EF2\u1EF4\u038E", + "Z": "\u0179\u017B\u017D\u0224\u0396\u1E90\u1E92\u1E94", + "": "\u200B\u200C\u200D", +}).map(function (_a) { + var _b = __read(_a, 2), char = _b[0], alts = _b[1]; + return alts.split("").map(function (alt) { return [alt, char]; }); +}).flat(1)); +exports.multiCharSubstitutions = [ + [/\|-\|/g, "H"] +]; +//#endregion +//#region misc +/** Used for anti-impersonation. Make sure to replace numbers with letters, for example, balam314 -> balamei4. */ +exports.adminNames = ["fish", "balamei4", "clashgone", "darthscion", "firefridge", "aricia", "rawsewage", "skeledragon", "edh8e", "everydayhuman8e", "benjamonsrl"]; +exports.heuristics = { + /** Will trip if more than this many blocks are broken within 25 seconds of joining. */ + blocksBrokenAfterJoin: 40, +}; +exports.stopAntiEvadeTime = funcs_1.Duration.minutes(30); +exports.backendIP = '45.79.202.111:5082'; +/** File names of throwback (retired) maps. These will not appear in the normal maps list, but will be available in the throwback maps tab. */ +exports.throwbackMapFileNames = []; +/** A campaign is a curated sequence of maps that overrides normal map rotation. */ +var Campaign = /** @class */ (function () { + function Campaign( + /** Display name of the campaign */ + name, + /** Short description shown in the menu */ + description, + /** Ordered list of map file names in the campaign */ + mapFileNames) { + this.name = name; + this.description = description; + this.mapFileNames = mapFileNames; + } + return Campaign; +}()); +exports.Campaign = Campaign; +/** Available campaigns. Maps are listed in order; when a campaign is active, rotation follows this sequence. */ +exports.campaigns = []; +exports.Mode = { + localDebug: new Fi("config/.debug").exists(), + noBackend: new Fi("config/.debug").exists() && !exports.backendIP.startsWith("127.0.0.1:"), + isChristmas: new Date().getMonth() == 11, + isAprilFools: new Date().getMonth() == 3 && new Date().getDate() == 1, +}; +//#endregion +//#region servers +/** Stores the repository url for the maps for each gamemode. */ +exports.mapRepoURLs = { + attack: "https://api.github.com/repos/Fish-Community/fish-maps/contents/attack", + survival: "https://api.github.com/repos/Fish-Community/fish-maps/contents/survival", + pvp: "https://api.github.com/repos/Fish-Community/fish-maps/contents/pvp", + hexed: "https://api.github.com/repos/Fish-Community/fish-maps/contents/hexed", + sandbox: "https://api.github.com/repos/Fish-Community/fish-maps/contents/sandbox", + hardcore: "https://api.github.com/repos/Fish-Community/fish-maps/contents/hardcore", + testsrv: "https://api.github.com/repos/Fish-Community/fish-maps/contents/testsrv", + minigame: "https://api.github.com/repos/Fish-Community/fish-maps/contents/minigame", +}; +/** Stores the names and addresses of each active server. */ +var FishServer = /** @class */ (function () { + function FishServer(name, ip, port, aliases, + /** If set, this permission is required to switch to or get information about this server. */ + requiredPerm) { + this.name = name; + this.ip = ip; + this.port = port; + this.aliases = aliases; + this.requiredPerm = requiredPerm; + FishServer.all.push(this); + } + FishServer.byName = function (input) { + var _a; + input = input.toLowerCase(); + return (_a = FishServer.all.find(function (s) { return s.aliases.concat(s.name).includes(input); })) !== null && _a !== void 0 ? _a : null; + }; + FishServer.all = []; + FishServer.attack = new FishServer("attack", "162.248.100.98", "6567", ["attac", "atack", "atak", "atck", "atk", "a"]); + FishServer.survival = new FishServer("survival", "162.248.101.95", "6567", ["surviv", "surv", "sur", "su", "s", "sl"]); + FishServer.pvp = new FishServer("pvp", "162.248.102.101", "6567", ["pv", "p", "v", "playerversusplayer"]); + FishServer.sandbox = new FishServer("sandbox", "162.248.101.53", "6567", ["sand", "box", "sa", "sb"]); + FishServer.hexed = new FishServer("hexed", "162.248.100.133", "6567", ["h", "hx", "hxd", "hpvp", "hxpvp", "hexpvp"]); + FishServer.minigame = new FishServer("minigame", "162.248.101.116", "6567", ["m", "mg", "mini", "minig", "mgame", "mng", "minigame", "mpvp"]); + FishServer.testing = new FishServer("testing", "162.248.101.52", "6567", ["test", "testsrv", "t", "testingserver", "testserver"]); + return FishServer; +}()); +exports.FishServer = FishServer; +; +/** Stores functions that return whether the specified gamemode is the current gamemode. */ +exports.Gamemode = { + attack: function () { return exports.Gamemode.name() == "attack"; }, + survival: function () { return exports.Gamemode.name() == "survival"; }, + pvp: function () { return exports.Gamemode.name() == "pvp" || exports.Gamemode.name() == "hexed" || exports.Gamemode.name() == "minigame"; }, + sandbox: function () { return exports.Gamemode.name() == "sandbox"; }, + hexed: function () { return exports.Gamemode.name() == "hexed"; }, + hardcore: function () { return exports.Gamemode.name() == "hardcore"; }, + testsrv: function () { return exports.Gamemode.name() == "testsrv"; }, + minigame: function () { return exports.Gamemode.name() == "minigame"; }, + name: function () { return Core.settings.get("mode", Vars.state.rules.mode().name()); }, +}; +exports.GamemodeNames = Object.keys(exports.Gamemode).filter(function (x) { return x !== "name"; }); +//#endregion +//#region text content +exports.prefixes = { + marked: '[yellow]\u26A0[scarlet]Marked Griefer[]\u26A0[]', + flagged: '[yellow]\u26A0[orange]Flagged[]\u26A0[]', + muted: '[white](muted)', +}; +exports.text = { + discordURL: "https://discord.gg/VpzcYSQ33Y", + membershipURL: "https://patreon.com/FishServers", + reportsPing: "<@&1040193678817378305>", + welcomeMessage: function () { return (0, funcs_1.random)([ + "[gold]Welcome![]" + ]); }, + chatFilterReplacement: { + message: function () { return "I really hope everyone is having a fun time :) <3"; }, + messageShort: function () { return "I hope we're all having a fun time :) <3"; }, + highlight: function () { return "[#f456f]"; }, + // `[#22AA22]Merry [#EC4444]Christmas!`, + // `[gold]Happy Holidays! [white]•*•☃*•`, + // `[gold]Happy Hanukkah!`, + // `[#EC4444]May your days be merry and bright!`, + // `[gold]Merry Fishmas! >|||> [white]☃`, + // `[gold]Deck the halls with lots of fun!`, + // `[gold]>|||> Fish wishes you [#22AA22]a merry Christmas!`, + // ]), + // chatFilterReplacement: { + // message: () => random([ + // `Have a holly jolly Christmas :) <3`, + // `I really hope everyone is jolly for the season! :D`, + // `All I want for Christmaaaaaaas is everyone having a fun time! :)`, + // `Remember to be nice in chat: Santa is watching! <3`, + // `All I want for Christmas is Fish! >|||>`, + // ]), + // highlight: () => random([ + // `[#22AA22]`, `[#EC4444]`, `[#FFFFFF]` + // ]), + }, + dataFetchFailed: "[scarlet]\u26A0 Data fetch failed!\n[white]Please disconnect and rejoin the server if you encounter further issues, such as missing rank or statistics.", +}; +//TODO use this +exports.FColor = (function (data) { + return Object.fromEntries(Object.entries(data).map(function (_a) { + var _b = __read(_a, 2), k = _b[0], c = _b[1]; + return [k, function (str) { + var varChunks = []; + for (var _i = 1; _i < arguments.length; _i++) { + varChunks[_i - 1] = arguments[_i]; + } + return str != null ? + "".concat(c).concat(Array.isArray(str) ? String.raw.apply(String, __spreadArray([{ raw: str }], __read(varChunks.map(function (v) { return String(v) + c; })), false)) : str, "[]") + : c; + } + ]; + })); +})({ + discord: "[#7289DA]", + /** Used for tips and welcome messages. */ + tip: "[gold]", + member: "[pink]", + achievement: "[lime]", +}); +/** Tips that are shown to players randomly. */ +exports.tips = { + ads: [ + "".concat(exports.FColor.member(templateObject_1 || (templateObject_1 = __makeTemplateObject(["Fish Membership"], ["Fish Membership"]))), " subscribers can access the ").concat(exports.FColor.member(templateObject_2 || (templateObject_2 = __makeTemplateObject(["/pet"], ["/pet"]))), " command, which spawns a merui that follows you around. Get a Fish Membership at[sky] ").concat(exports.text.membershipURL, " []"), + "".concat(exports.FColor.member(templateObject_3 || (templateObject_3 = __makeTemplateObject(["Fish Membership"], ["Fish Membership"]))), " subscribers can use the ").concat(exports.FColor.member(templateObject_4 || (templateObject_4 = __makeTemplateObject(["/highlight"], ["/highlight"]))), " command, which turns your chat messages to a color of your choice. Get a Fish Membership at[sky] ").concat(exports.text.membershipURL, " []"), + "".concat(exports.FColor.member(templateObject_5 || (templateObject_5 = __makeTemplateObject(["Fish Membership"], ["Fish Membership"]))), " subscribers can use the ").concat(exports.FColor.member(templateObject_6 || (templateObject_6 = __makeTemplateObject(["/rainbow"], ["/rainbow"]))), " command, which makes your name flash different colors. Get a Fish Membership at[sky] ").concat(exports.text.membershipURL, " []"), + "Want to support the server and get some perks? Get a ".concat(exports.FColor.member(templateObject_7 || (templateObject_7 = __makeTemplateObject(["Fish Membership"], ["Fish Membership"]))), " at[sky] ").concat(exports.text.membershipURL, " []"), + "Join our ".concat(exports.FColor.discord(templateObject_8 || (templateObject_8 = __makeTemplateObject(["Discord server"], ["Discord server"]))), "[]! ").concat(exports.FColor.discord(exports.text.discordURL), " or type ").concat(exports.FColor.discord(templateObject_9 || (templateObject_9 = __makeTemplateObject(["/discord"], ["/discord"])))), + ], + normal: [ + //commands + "You can spawn an [scarlet]Ohno[] with the [scarlet]/ohno[] command. Ohnos are harmless creatures that were created by fusing an alpha and an atrax.", + "Ohnos cannot be spawned near enemy buildings, because they are peaceful and do not want to be used for attacks.", + "You can use [white]/tp[] to teleport directly to any other player! (But only when you're in a core unit)", + "You can unload bulk conveyors (\uF819 or \uF732) with unloaders (\uF864 or \uF731).", + "Hate boulders? You can remove them with [white]/clean[].", + "You can check our rules at any time by running [white]/rules[].", + // `You can kill your unit by running [white]/die[].`, + "We have a tilelog system to help catch griefers. Run [white]/tilelog[], then click a tile to see what's happened there.", + "Run [white]/tilelog 1[] to check the tile history of multiple tiles.", + "Tilelog stores when a building is placed, broken, rotated, configured, and picked up/dropped by a payload unit. Access it with [white]/tilelog[]", + "Tilelog doesn't just log tile actions, it also logs unit deaths! Access it with [white]/tilelog[]", + "Did someone kill a T5 with commands? Run [white]/aoelog 0 15 killed[] to check tilelogs for unit deaths in a large area.", + "Aoelog can show the history of tiles in an area. Select the opposite corners of a rectangle to view the history of its tiles.", + "Aoelog is the plural version of tilelog, access it via [white]/aoelog[]", + "You can mark yourself as AFK(away from keyboard) with [white]/afk[].", + "Run /survival, /attack, /pvp, /sandbox, /hexed or /minigame to quickly change to another server.", + "Need to get rid of an active griefer? Use [#6FFC7C]/s[] to send a message to all staff members across all servers.", + "Use [white]/help to get more information about a specific command.", + "If you want to send a message to just one player, you can use the [white]/msg[] command.", + "Use [white]/r[] to reply to a message sent by another player.", + "[white]/trail[] can be used to give your unit a trail of particle effects.", + "Run [white]/ranks[] to see all the ranks on our server.", + "Is someone impersonating a staff member? Run [white]/rank[] to see their real rank.", + "Don't like the map? Vote to change it with [white]/rtv[].", + "If you want to end the current map, DO NOT BREAK DEFENCES! Vote to change the map with [white]/rtv[].", + //misc + "Anyone attempting to impersonate a ranked player, or the server, will have [scarlet]SUSSY IMPOSTOR[] prepended to their name. Beware!", + "Griefers will often be found with the text ".concat(exports.prefixes.marked, " prepended to their name: they are harmless and cannot grief again."), + "Don't votekick ".concat(exports.prefixes.marked.slice(0, -3), "[][scarlet]s [gold]if they aren't breaking the rules: they are incapable of griefing more."), + "Players marked as ".concat(exports.prefixes.flagged, " have been flagged as suspicious by our detection systems, but they may not be griefers."), + "Need to appeal a moderation action? Join the discord at ".concat(exports.FColor.discord(exports.text.discordURL), " or type /discord"), + "Want to send the phrase [white]\"/command\"[] in chat? Type [white]\"./command\"[] and the [white].[] will be removed.", + "All commands with a player as an argument support using a menu to specify the player. Just run the command leaving the argument blank (using two spaces if necessary), and a menu will show up.", + "Players with a ".concat(ranks_1.Rank.trusted.prefix, " in front of their name aren't staff members, but they do have extra powers."), + "Staff members will have the following prefixes in front of their name: ".concat(ranks_1.Rank.manager.prefix, ", ").concat(ranks_1.Rank.admin.prefix, ", ").concat(ranks_1.Rank.mod.prefix), + "Wave cooldown too long? Skip the wait with [white]/vnw[]", + "You can tell new players not to break power voids with [white]/void[]", + "You can add [pink]color[] to things with color tags! Try typing \"[[".concat(["pink", "green", "cyan", "acid", "royal", "coral"][Math.floor(Math.random() * 6)], "]Hello\" in chat, and see what happens!") + ], + christmas: [ + "Remember to be nice in-game, Santa is watching!", + "Santa's checking his list, so be nice!", + "Have a merry christmas and a happy new year!", + "Fish becomes a bit more jolly around Christmastime!", + "Many server maps have been changed for the season.", + ], + staff: [], +}; +exports.rules = [ + "# 1: [red]No griefing. This refers to intentionally hurting your own team in any way.", + "# 2: [orange]False votekicking isn't allowed. Avoid votekicking if there's an active staff member in the server.", + "# 3: [yellow]Gore, pornography, suggestive content and jokes, and flashing images aren't allowed here. Being horny and a creep in chat will result in a ban.", + "# 4: [green]Do not harass other people. We have zero tolerance for any bigotry. Please respect everyone.", + "# 5: [#00D8D8]Spamming is prohibited. Be reasonable with messaging staff in-game. Misuse may result in a mute.", + "# 6: [blue]Impersonating people or ranks is prohibited.", + "# 7: [purple]Talking about controversial or sensitive topics is not allowed in-game. Hate symbols, such as swastikas, are not permitted.", + "# 8: [pink]No uncomfortable trolling or intentionally causing chaos. This includes any actions or messages that create an unpleasant atmosphere.", + "Failure to follow these rules will result in consequences: likely a ".concat(exports.prefixes.marked, " tag for any game disruption, mute for broken chat rules, and bans for repeated offenses or bypasses.") +].map(function (r) { return "[white]".concat(r); }); +/** Map tag key for maps that opt out of multi-wave skipping (Issue #151). + * Maps with this tag set to "true" will only allow 1 wave to be skipped per /vnw call. */ +exports.noMultiVnwTag = "no-multi-vnw"; +var templateObject_1, templateObject_2, templateObject_3, templateObject_4, templateObject_5, templateObject_6, templateObject_7, templateObject_8, templateObject_9; +//#endregion diff --git a/build/scripts/files.js b/build/scripts/files.js index f08cfe8f..350e2975 100644 --- a/build/scripts/files.js +++ b/build/scripts/files.js @@ -1,96 +1,96 @@ -"use strict"; -/* -Copyright © BalaM314, 2026. All Rights Reserved. -This file contains the code for automated map syncing. -Original contributor: @author Jurorno9 -Maintenance: @author BalaM314 -*/ -Object.defineProperty(exports, "__esModule", { value: true }); -exports.updateMaps = updateMaps; -var config_1 = require("/config"); -var funcs_1 = require("/funcs"); -var promise_1 = require("/promise"); -var utils_1 = require("/utils"); -//if we switch to a self-hosted setup, just make it respond with the githubfile object for a drop-in replacement -function fetchGithubContents() { - return new promise_1.Promise(function (resolve, reject) { - var url = config_1.mapRepoURLs[config_1.Gamemode.name()]; - if (!url) - return reject("No recognized gamemode detected. please enter \"host \" and try again"); - Http.get(url, function (res) { - try { - //Trust github to return valid JSON data - resolve(JSON.parse(res.getResultAsString())); - } - catch (e) { - reject("Failed to parse GitHub repository contents: ".concat(String(e))); - } - }, function () { return reject("Network error while fetching github repository contents"); }); - }); -} -function downloadFile(address, filename) { - if (!/^https?:\/\//i.test(address)) { - (0, funcs_1.crash)("Invalid address, please start with 'http://' or 'https://'"); - } - return new promise_1.Promise(function (resolve, reject) { - var instream = null; - var outstream = null; - Log.info("Downloading ".concat(filename, "...")); - Http.get(address, function (res) { - try { - instream = res.getResultAsStream(); - outstream = new Fi(filename).write(); - instream.transferTo(outstream); - resolve(); - } - finally { - instream === null || instream === void 0 ? void 0 : instream.close(); - outstream === null || outstream === void 0 ? void 0 : outstream.close(); - } - }, function () { - Log.err("Download failed."); - reject("Network error while downloading a map file: ".concat(address)); - }); - }); -} -function downloadMaps(githubListing) { - return promise_1.Promise.all(githubListing.map(function (fileEntry) { - if (!(typeof fileEntry.download_url == "string")) { - Log.warn("Map ".concat(fileEntry.name, " has no valid download link, skipped.")); - return promise_1.Promise.resolve(null); - } - return downloadFile(fileEntry.download_url, Vars.customMapDirectory.child(fileEntry.name).absolutePath()); - })).then(function (v) { }); -} -/** - * @returns whether any maps were changed - */ -function updateMaps() { - //get github map listing - return fetchGithubContents().then(function (listing) { - //filter only valid mindustry maps - var mapList = listing - .filter(function (entry) { return entry.type == 'file'; }) - .filter(function (entry) { return entry.name.endsWith(".msav"); }); - var mapFiles = Vars.customMapDirectory.list(); - var mapsToDelete = mapFiles.filter(function (localFile) { - return !mapList.some(function (remoteFile) { - return remoteFile.name === localFile.name(); - }) - && !localFile.name().startsWith("$$"); - }); - mapsToDelete.forEach(function (map) { return map.delete(); }); - var mapsToDownload = mapList - .filter(function (entry) { - var file = Vars.customMapDirectory.child(entry.name); - return !file.exists() || entry.sha !== (0, utils_1.getHash)(file); //sha'd - }); - if (mapsToDownload.length == 0) { - return mapsToDelete.length > 0 ? true : false; - } - return downloadMaps(mapsToDownload).then(function () { - Vars.maps.reload(); - return true; - }); - }); -} +"use strict"; +/* +Copyright © BalaM314, 2026. All Rights Reserved. +This file contains the code for automated map syncing. +Original contributor: @author Jurorno9 +Maintenance: @author BalaM314 +*/ +Object.defineProperty(exports, "__esModule", { value: true }); +exports.updateMaps = updateMaps; +var config_1 = require("/config"); +var funcs_1 = require("/funcs"); +var promise_1 = require("/promise"); +var utils_1 = require("/utils"); +//if we switch to a self-hosted setup, just make it respond with the githubfile object for a drop-in replacement +function fetchGithubContents() { + return new promise_1.Promise(function (resolve, reject) { + var url = config_1.mapRepoURLs[config_1.Gamemode.name()]; + if (!url) + return reject("No recognized gamemode detected. please enter \"host \" and try again"); + Http.get(url, function (res) { + try { + //Trust github to return valid JSON data + resolve(JSON.parse(res.getResultAsString())); + } + catch (e) { + reject("Failed to parse GitHub repository contents: ".concat(String(e))); + } + }, function () { return reject("Network error while fetching github repository contents"); }); + }); +} +function downloadFile(address, filename) { + if (!/^https?:\/\//i.test(address)) { + (0, funcs_1.crash)("Invalid address, please start with 'http://' or 'https://'"); + } + return new promise_1.Promise(function (resolve, reject) { + var instream = null; + var outstream = null; + Log.info("Downloading ".concat(filename, "...")); + Http.get(address, function (res) { + try { + instream = res.getResultAsStream(); + outstream = new Fi(filename).write(); + instream.transferTo(outstream); + resolve(); + } + finally { + instream === null || instream === void 0 ? void 0 : instream.close(); + outstream === null || outstream === void 0 ? void 0 : outstream.close(); + } + }, function () { + Log.err("Download failed."); + reject("Network error while downloading a map file: ".concat(address)); + }); + }); +} +function downloadMaps(githubListing) { + return promise_1.Promise.all(githubListing.map(function (fileEntry) { + if (!(typeof fileEntry.download_url == "string")) { + Log.warn("Map ".concat(fileEntry.name, " has no valid download link, skipped.")); + return promise_1.Promise.resolve(null); + } + return downloadFile(fileEntry.download_url, Vars.customMapDirectory.child(fileEntry.name).absolutePath()); + })).then(function (v) { }); +} +/** + * @returns whether any maps were changed + */ +function updateMaps() { + //get github map listing + return fetchGithubContents().then(function (listing) { + //filter only valid mindustry maps + var mapList = listing + .filter(function (entry) { return entry.type == 'file'; }) + .filter(function (entry) { return entry.name.endsWith(".msav"); }); + var mapFiles = Vars.customMapDirectory.list(); + var mapsToDelete = mapFiles.filter(function (localFile) { + return !mapList.some(function (remoteFile) { + return remoteFile.name === localFile.name(); + }) + && !localFile.name().startsWith("$$"); + }); + mapsToDelete.forEach(function (map) { return map.delete(); }); + var mapsToDownload = mapList + .filter(function (entry) { + var file = Vars.customMapDirectory.child(entry.name); + return !file.exists() || entry.sha !== (0, utils_1.getHash)(file); //sha'd + }); + if (mapsToDownload.length == 0) { + return mapsToDelete.length > 0 ? true : false; + } + return downloadMaps(mapsToDownload).then(function () { + Vars.maps.reload(); + return true; + }); + }); +} diff --git a/build/scripts/fjsContext.js b/build/scripts/fjsContext.js index 50f5cfdd..ba28a49d 100644 --- a/build/scripts/fjsContext.js +++ b/build/scripts/fjsContext.js @@ -1,101 +1,101 @@ -"use strict"; -/* -Copyright © BalaM314, 2026. All Rights Reserved. -This file contains the context for the "fjs" command, -which executes code with access to the plugin's internals. -*/ -Object.defineProperty(exports, "__esModule", { value: true }); -exports.runJS = runJS; -var achievements = require("/achievements"); -var api = require("/api"); -var commands = require("/frameworks/commands"); -var config = require("/config"); -var consoleCommands = require("/commands/console").commands; -var files = require("/files"); -var funcs = require("/funcs"); -var globals = require("/globals"); -var io = require("/frameworks/io"); -var maps = require("/maps"); -var memberCommands = require("/commands/member").commands; -var menus = require("/frameworks/menus"); -var packetHandlers = require("/packetHandlers"); -var playerCommands = require("/commands/general").commands; -var players = require("/players"); -var ranks = require("/ranks"); -var staffCommands = require("/commands/staff").commands; -var timers = require("/timers"); -var utils = require("/utils"); -var votes = require("/votes"); -var Promise = require("/promise").Promise; -var Achievement = achievements.Achievement, Achievements = achievements.Achievements; -var Perm = commands.Perm, allCommands = commands.allCommands; -var bannedWords = config.bannedWords, FishServer = config.FishServer, Mode = config.Mode, Gamemode = config.Gamemode, FColor = config.FColor, mapRepoURLs = config.mapRepoURLs; -var FishPlayer = players.FishPlayer; -var Serializer = io.Serializer; -var FishEvents = globals.FishEvents, fishPlugin = globals.fishPlugin, fishState = globals.fishState, tileHistory = globals.tileHistory; -var FMap = maps.FMap; -var Rank = ranks.Rank, RoleFlag = ranks.RoleFlag; -var Menu = menus.Menu; -Object.assign(this, utils, funcs); //global scope goes brrrrr, I'm sure this will not cause any bugs whatsoever -var Ranks = null; -var $ = Object.assign(function $(input) { - if (typeof input == "string") { - if (Pattern.matches("[a-zA-Z0-9+/]{22}==", input)) { - return FishPlayer.getById(input); - } - } - return null; -}, { - sussy: true, - info: function (input) { - if (typeof input == "string") { - if (Pattern.matches("[a-zA-Z0-9+/]{22}==", input)) { - return Vars.netServer.admins.getInfo(input); - } - } - return null; - }, - create: function (input) { - if (typeof input == "string") { - if (Pattern.matches("[a-zA-Z0-9+/]{22}==", input)) { - return FishPlayer.getFromInfo(Vars.netServer.admins.getInfo(input)); - } - } - return null; - }, - me: null, - meM: null, -}); -/** Used to persist variables. */ -var vars = {}; -function runJS(input, outputFunction, errorFunction, player) { - if (outputFunction === void 0) { outputFunction = Log.info; } - if (errorFunction === void 0) { errorFunction = Log.err; } - if (player) { - $.me = player; - $.meM = player.player; - } - else if (Groups.player.size() == 1) { - $.meM = Groups.player.first(); - $.me = players.FishPlayer.get($.meM); - } - try { - var admins = Vars.netServer.admins; - var output = eval(input); - if (output instanceof Array) { - outputFunction("&cArray: [&fr" + output.join(", ") + "&c]&fr"); - } - else if (output === undefined) { - outputFunction("undefined"); - } - else if (output === null) { - outputFunction("null"); - } - else { - outputFunction(output); - } - } - catch (err) { - errorFunction(err); - } -} +"use strict"; +/* +Copyright © BalaM314, 2026. All Rights Reserved. +This file contains the context for the "fjs" command, +which executes code with access to the plugin's internals. +*/ +Object.defineProperty(exports, "__esModule", { value: true }); +exports.runJS = runJS; +var achievements = require("/achievements"); +var api = require("/api"); +var commands = require("/frameworks/commands"); +var config = require("/config"); +var consoleCommands = require("/commands/console").commands; +var files = require("/files"); +var funcs = require("/funcs"); +var globals = require("/globals"); +var io = require("/frameworks/io"); +var maps = require("/maps"); +var memberCommands = require("/commands/member").commands; +var menus = require("/frameworks/menus"); +var packetHandlers = require("/packetHandlers"); +var playerCommands = require("/commands/general").commands; +var players = require("/players"); +var ranks = require("/ranks"); +var staffCommands = require("/commands/staff").commands; +var timers = require("/timers"); +var utils = require("/utils"); +var votes = require("/votes"); +var Promise = require("/promise").Promise; +var Achievement = achievements.Achievement, Achievements = achievements.Achievements; +var Perm = commands.Perm, allCommands = commands.allCommands; +var bannedWords = config.bannedWords, FishServer = config.FishServer, Mode = config.Mode, Gamemode = config.Gamemode, FColor = config.FColor, mapRepoURLs = config.mapRepoURLs; +var FishPlayer = players.FishPlayer; +var Serializer = io.Serializer; +var FishEvents = globals.FishEvents, fishPlugin = globals.fishPlugin, fishState = globals.fishState, tileHistory = globals.tileHistory; +var FMap = maps.FMap; +var Rank = ranks.Rank, RoleFlag = ranks.RoleFlag; +var Menu = menus.Menu; +Object.assign(this, utils, funcs); //global scope goes brrrrr, I'm sure this will not cause any bugs whatsoever +var Ranks = null; +var $ = Object.assign(function $(input) { + if (typeof input == "string") { + if (Pattern.matches("[a-zA-Z0-9+/]{22}==", input)) { + return FishPlayer.getById(input); + } + } + return null; +}, { + sussy: true, + info: function (input) { + if (typeof input == "string") { + if (Pattern.matches("[a-zA-Z0-9+/]{22}==", input)) { + return Vars.netServer.admins.getInfo(input); + } + } + return null; + }, + create: function (input) { + if (typeof input == "string") { + if (Pattern.matches("[a-zA-Z0-9+/]{22}==", input)) { + return FishPlayer.getFromInfo(Vars.netServer.admins.getInfo(input)); + } + } + return null; + }, + me: null, + meM: null, +}); +/** Used to persist variables. */ +var vars = {}; +function runJS(input, outputFunction, errorFunction, player) { + if (outputFunction === void 0) { outputFunction = Log.info; } + if (errorFunction === void 0) { errorFunction = Log.err; } + if (player) { + $.me = player; + $.meM = player.player; + } + else if (Groups.player.size() == 1) { + $.meM = Groups.player.first(); + $.me = players.FishPlayer.get($.meM); + } + try { + var admins = Vars.netServer.admins; + var output = eval(input); + if (output instanceof Array) { + outputFunction("&cArray: [&fr" + output.join(", ") + "&c]&fr"); + } + else if (output === undefined) { + outputFunction("undefined"); + } + else if (output === null) { + outputFunction("null"); + } + else { + outputFunction(output); + } + } + catch (err) { + errorFunction(err); + } +} diff --git a/build/scripts/frameworks/commands.js b/build/scripts/frameworks/commands.js index 50e79d93..63ab5097 100644 --- a/build/scripts/frameworks/commands.js +++ b/build/scripts/frameworks/commands.js @@ -1,22 +1,22 @@ -"use strict"; -var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { - if (k2 === undefined) k2 = k; - var desc = Object.getOwnPropertyDescriptor(m, k); - if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { - desc = { enumerable: true, get: function() { return m[k]; } }; - } - Object.defineProperty(o, k2, desc); -}) : (function(o, m, k, k2) { - if (k2 === undefined) k2 = k; - o[k2] = m[k]; -})); -var __exportStar = (this && this.__exportStar) || function(m, exports) { - for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p); -}; -Object.defineProperty(exports, "__esModule", { value: true }); -__exportStar(require("/frameworks/commands/commands"), exports); -__exportStar(require("/frameworks/commands/errors"), exports); -__exportStar(require("/frameworks/commands/formatting"), exports); -__exportStar(require("/frameworks/commands/perm"), exports); -__exportStar(require("/frameworks/commands/requirements"), exports); -__exportStar(require("/frameworks/commands/types"), exports); +"use strict"; +var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + var desc = Object.getOwnPropertyDescriptor(m, k); + if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { + desc = { enumerable: true, get: function() { return m[k]; } }; + } + Object.defineProperty(o, k2, desc); +}) : (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + o[k2] = m[k]; +})); +var __exportStar = (this && this.__exportStar) || function(m, exports) { + for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p); +}; +Object.defineProperty(exports, "__esModule", { value: true }); +__exportStar(require("/frameworks/commands/commands"), exports); +__exportStar(require("/frameworks/commands/errors"), exports); +__exportStar(require("/frameworks/commands/formatting"), exports); +__exportStar(require("/frameworks/commands/perm"), exports); +__exportStar(require("/frameworks/commands/requirements"), exports); +__exportStar(require("/frameworks/commands/types"), exports); diff --git a/build/scripts/frameworks/commands/commands.js b/build/scripts/frameworks/commands/commands.js index 28bf248b..fdad59da 100644 --- a/build/scripts/frameworks/commands/commands.js +++ b/build/scripts/frameworks/commands/commands.js @@ -1,767 +1,767 @@ -"use strict"; -/* -Copyright © BalaM314, 2026. All Rights Reserved. -This file contains the commands framework. -For usage information, see docs/framework-usage-guide.md -For maintenance information, see docs/frameworks.md -*/ -//Behold, the power of typescript! -var __assign = (this && this.__assign) || function () { - __assign = Object.assign || function(t) { - for (var s, i = 1, n = arguments.length; i < n; i++) { - s = arguments[i]; - for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p)) - t[p] = s[p]; - } - return t; - }; - return __assign.apply(this, arguments); -}; -var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { - function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } - return new (P || (P = Promise))(function (resolve, reject) { - function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } - function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } - function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } - step((generator = generator.apply(thisArg, _arguments || [])).next()); - }); -}; -var __generator = (this && this.__generator) || function (thisArg, body) { - var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g = Object.create((typeof Iterator === "function" ? Iterator : Object).prototype); - return g.next = verb(0), g["throw"] = verb(1), g["return"] = verb(2), typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g; - function verb(n) { return function (v) { return step([n, v]); }; } - function step(op) { - if (f) throw new TypeError("Generator is already executing."); - while (g && (g = 0, op[0] && (_ = 0)), _) try { - if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t; - if (y = 0, t) op = [op[0] & 2, t.value]; - switch (op[0]) { - case 0: case 1: t = op; break; - case 4: _.label++; return { value: op[1], done: false }; - case 5: _.label++; y = op[1]; op = [0]; continue; - case 7: op = _.ops.pop(); _.trys.pop(); continue; - default: - if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; } - if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; } - if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; } - if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; } - if (t[2]) _.ops.pop(); - _.trys.pop(); continue; - } - op = body.call(thisArg, _); - } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; } - if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true }; - } -}; -var __read = (this && this.__read) || function (o, n) { - var m = typeof Symbol === "function" && o[Symbol.iterator]; - if (!m) return o; - var i = m.call(o), r, ar = [], e; - try { - while ((n === void 0 || n-- > 0) && !(r = i.next()).done) ar.push(r.value); - } - catch (error) { e = { error: error }; } - finally { - try { - if (r && !r.done && (m = i["return"])) m.call(i); - } - finally { if (e) throw e.error; } - } - return ar; -}; -var __values = (this && this.__values) || function(o) { - var s = typeof Symbol === "function" && Symbol.iterator, m = s && o[s], i = 0; - if (m) return m.call(o); - if (o && typeof o.length === "number") return { - next: function () { - if (o && i >= o.length) o = void 0; - return { value: o && o[i++], done: !o }; - } - }; - throw new TypeError(s ? "Object is not iterable." : "Symbol.iterator is not defined."); -}; -var __spreadArray = (this && this.__spreadArray) || function (to, from, pack) { - if (pack || arguments.length === 2) for (var i = 0, l = from.length, ar; i < l; i++) { - if (ar || !(i in from)) { - if (!ar) ar = Array.prototype.slice.call(from, 0, i); - ar[i] = from[i]; - } - } - return to.concat(ar || Array.prototype.slice.call(from)); -}; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.consoleCommandList = exports.commandList = exports.allConsoleCommands = exports.allCommands = void 0; -exports.command = command; -exports.processArgString = processArgString; -exports.formatArg = formatArg; -exports.joinArgs = joinArgs; -exports.disambiguateArgument = disambiguateArgument; -exports.processArgs = processArgs; -exports.convertArgs = convertArgs; -exports.handleTapEvent = handleTapEvent; -exports.register = register; -exports.registerConsole = registerConsole; -exports.initialize = initialize; -exports.reset = reset; -var config_1 = require("/config"); -var errors_1 = require("/frameworks/commands/errors"); -var formatting_1 = require("/frameworks/commands/formatting"); -var types_1 = require("/frameworks/commands/types"); -var menus_1 = require("/frameworks/menus"); -var funcs_1 = require("/funcs"); -var globals_1 = require("/globals"); -var players_1 = require("/players"); -var ranks_1 = require("/ranks"); -var utils_1 = require("/utils"); -var hiddenUnauthorizedMessage = "[scarlet]Unknown command. Check [lightgray]/help[scarlet]."; -/** Flag to prevent double initialization */ -var initialized = false; -/** Stores all chat comamnds by their name. */ -exports.allCommands = {}; -/** Stores all console commands by their name. */ -exports.allConsoleCommands = {}; -/** Stores the last usage data for chat commands by their name. */ -var globalUsageData = {}; -/** Helper function to get the correct type for command lists. */ -var commandList = function (list) { return list; }; -exports.commandList = commandList; -/** Helper function to get the correct type for command lists. */ -var consoleCommandList = function (list) { return list; }; -exports.consoleCommandList = consoleCommandList; -/** - * Helper function to get the correct type definitions for commands that use "data" or init(). - * Necessary because, while typescript is capable of inferring A1, A2... - * ``` - * { - * prop1: Type; - * prop2: Type; - * } - * ``` - * it cannot handle inferring A1 and B1. - * ``` - * { - * prop1: Type; - * prop2: Type; - * } - * ``` - */ -function command(input) { - return input; -} -/** Takes an arg string, like `reason:string?` and converts it to a CommandArg. */ -function processArgString(str) { - //this was copypasted from mlogx haha - var matchResult = str.match(/(\w+):(\w+)(\?)?/); - if (!matchResult) { - (0, funcs_1.crash)("Bad arg string ".concat(str, ": does not match pattern word:word(?)")); - } - var _a = __read(matchResult, 4), name = _a[1], type = _a[2], isOptional = _a[3]; - if (types_1.commandArgTypes.includes(type)) { - return { name: name, type: type, isOptional: !!isOptional }; - } - else { - (0, funcs_1.crash)("Bad arg string ".concat(str, ": invalid type ").concat(type)); - } -} -function formatArg(a) { - var isOptional = a.at(-1) == "?"; - var brackets = isOptional ? ["[", "]"] : ["<", ">"]; - return brackets[0] + a.split(":")[0] + brackets[1]; -} -/** Joins multi-word arguments that have been groups with quotes. Ex: turns [`"a`, `b"`] into [`a b`]*/ -function joinArgs(rawArgs) { - var e_1, _a; - var outputArgs = []; - var groupedArg = null; - try { - for (var rawArgs_1 = __values(rawArgs), rawArgs_1_1 = rawArgs_1.next(); !rawArgs_1_1.done; rawArgs_1_1 = rawArgs_1.next()) { - var arg = rawArgs_1_1.value; - if (arg.startsWith("\"") && groupedArg == null) { - groupedArg = []; - } - if (groupedArg) { - groupedArg.push(arg); - if (arg.endsWith("\"")) { - outputArgs.push(groupedArg.join(" ").slice(1, -1)); - groupedArg = null; - } - } - else { - outputArgs.push(arg); - } - } - } - catch (e_1_1) { e_1 = { error: e_1_1 }; } - finally { - try { - if (rawArgs_1_1 && !rawArgs_1_1.done && (_a = rawArgs_1.return)) _a.call(rawArgs_1); - } - finally { if (e_1) throw e_1.error; } - } - if (groupedArg != null) { - //return `Unterminated string literal.`; - outputArgs.push(groupedArg.join(" ")); - } - return outputArgs; -} -function disambiguateArgument(options_1, arg_1, _a, sender_1, outputArgs_1, optionStringifier_1) { - return __awaiter(this, arguments, void 0, function (options, arg, _b, sender, outputArgs, optionStringifier, columns) { - var word, a_an_word, _c, _d; - var name = _b.name, type = _b.type; - if (columns === void 0) { columns = 3; } - return __generator(this, function (_e) { - switch (_e.label) { - case 0: - if (!(options == null)) return [3 /*break*/, 1]; - (0, errors_1.fail)("".concat((0, funcs_1.capitalizeText)(types_1.commandArgNames[type]), " \"").concat(arg, "\" not found.")); - return [3 /*break*/, 4]; - case 1: - if (!(options instanceof Array)) return [3 /*break*/, 3]; - word = types_1.commandArgNames[type]; - if (!sender) - (0, errors_1.fail)("Name \"".concat(arg, "\" could refer to more than one ").concat(word, ".")); - a_an_word = (0, funcs_1.indefiniteArticle)(word); - _c = outputArgs; - _d = name; - return [4 /*yield*/, menus_1.Menu.menu("Select ".concat(a_an_word), "Select ".concat(a_an_word, " for the argument \"").concat(name, "\""), options, sender, { - includeCancel: true, - optionStringifier: optionStringifier, - columns: columns, - })]; - case 2: - _c[_d] = _e.sent(); - return [3 /*break*/, 4]; - case 3: - outputArgs[name] = options; - _e.label = 4; - case 4: return [2 /*return*/]; - } - }); - }); -} -var argsSupportingBlank = ["player", "offlinePlayer", "unittype", "map", "mapOrRandom", "rank", "roleflag", "item"]; -/** Takes a list of joined args passed to the command, and processes it, turning it into a kwargs style object. */ -function processArgs(args, processedCmdArgs, sender) { - return __awaiter(this, void 0, void 0, function () { - var outputArgs, _a, _b, _c, i, cmdArg, commonArgs, _d, player, team, number, milliseconds, block, e_2_1; - var e_2, _e; - return __generator(this, function (_f) { - switch (_f.label) { - case 0: - outputArgs = {}; - _f.label = 1; - case 1: - _f.trys.push([1, 32, 33, 34]); - _a = __values(processedCmdArgs.entries()), _b = _a.next(); - _f.label = 2; - case 2: - if (!!_b.done) return [3 /*break*/, 31]; - _c = __read(_b.value, 2), i = _c[0], cmdArg = _c[1]; - if (!(i in args) || args[i] === "") { - //if the arg was not provided or it was empty - if (cmdArg.isOptional) { - outputArgs[cmdArg.name] = undefined; - return [3 /*break*/, 30]; - } - else if (sender && argsSupportingBlank.includes(cmdArg.type)) { - //it will be resolved later - } - else { - (0, errors_1.fail)("No value specified for arg ".concat(cmdArg.name, ". Did you type two spaces instead of one?")); - } - } - commonArgs = [args[i], cmdArg, sender, outputArgs]; - _d = cmdArg.type; - switch (_d) { - case "player": return [3 /*break*/, 3]; - case "offlinePlayer": return [3 /*break*/, 5]; - case "team": return [3 /*break*/, 10]; - case "number": return [3 /*break*/, 11]; - case "time": return [3 /*break*/, 12]; - case "string": return [3 /*break*/, 13]; - case "boolean": return [3 /*break*/, 14]; - case "block": return [3 /*break*/, 15]; - case "unittype": return [3 /*break*/, 16]; - case "uuid": return [3 /*break*/, 18]; - case "map": return [3 /*break*/, 19]; - case "mapOrRandom": return [3 /*break*/, 21]; - case "rank": return [3 /*break*/, 23]; - case "roleflag": return [3 /*break*/, 25]; - case "item": return [3 /*break*/, 27]; - } - return [3 /*break*/, 29]; - case 3: return [4 /*yield*/, disambiguateArgument.apply(void 0, __spreadArray(__spreadArray([players_1.FishPlayer.search(players_1.FishPlayer.getAllOnline(), args[i])], __read(commonArgs), false), [function (player) { return (player.marked() ? config_1.prefixes.marked : player.autoflagged ? config_1.prefixes.flagged : "") + (Strings.stripColors(player.name).length >= 3 ? - player.name - : (0, funcs_1.escapeStringColorsClient)(player.name)); }, - 2], false))]; - case 4: - _f.sent(); - return [3 /*break*/, 30]; - case 5: - if (!globals_1.uuidPattern.test(args[i])) return [3 /*break*/, 6]; - player = players_1.FishPlayer.getById(args[i]); - if (player == null) - (0, errors_1.fail)("Player with uuid \"".concat(args[i], "\" not found. Specify \"create:").concat(args[i], "\" to create the player.")); - outputArgs[cmdArg.name] = player; - return [3 /*break*/, 9]; - case 6: - if (!(args[i].startsWith("create:") && globals_1.uuidPattern.test(args[i].split("create:")[1]))) return [3 /*break*/, 7]; - outputArgs[cmdArg.name] = players_1.FishPlayer.getFromInfo(Vars.netServer.admins.getInfo(args[i].split("create:")[1])); - return [3 /*break*/, 9]; - case 7: return [4 /*yield*/, disambiguateArgument.apply(void 0, __spreadArray(__spreadArray([players_1.FishPlayer.search(Object.values(players_1.FishPlayer.cachedPlayers), args[i])], __read(commonArgs), false), [function (player) { return Strings.stripColors(player.name).length >= 3 ? - player.name - : (0, funcs_1.escapeStringColorsClient)(player.name); }, - 2], false))]; - case 8: - _f.sent(); - _f.label = 9; - case 9: return [3 /*break*/, 30]; - case 10: - { - team = (0, utils_1.getTeam)(args[i]); - if (typeof team == "string") - (0, errors_1.fail)(team); - outputArgs[cmdArg.name] = team; - return [3 /*break*/, 30]; - } - _f.label = 11; - case 11: - { - number = Number(args[i]); - if (isNaN(number)) { - if (/\(\d+,/.test(args[i])) - number = Number(args[i].slice(1, -1)); - else if (/\d+\)/.test(args[i])) - number = Number(args[i].slice(0, -1)); - if (isNaN(number)) - (0, errors_1.fail)("Invalid number \"".concat(args[i], "\"")); - } - outputArgs[cmdArg.name] = number; - return [3 /*break*/, 30]; - } - _f.label = 12; - case 12: - { - milliseconds = (0, utils_1.parseTimeString)(args[i]); - if (milliseconds == null) - (0, errors_1.fail)("Invalid time string \"".concat(args[i], "\"")); - outputArgs[cmdArg.name] = milliseconds; - return [3 /*break*/, 30]; - } - _f.label = 13; - case 13: - outputArgs[cmdArg.name] = args[i]; - return [3 /*break*/, 30]; - case 14: - switch (args[i].toLowerCase()) { - case "true": - case "yes": - case "yeah": - case "ya": - case "ye": - case "t": - case "y": - case "1": - outputArgs[cmdArg.name] = true; - break; - case "false": - case "no": - case "nah": - case "nay": - case "nope": - case "f": - case "n": - case "0": - outputArgs[cmdArg.name] = false; - break; - default: (0, errors_1.fail)("Argument ".concat(args[i], " is not a boolean. Try \"true\" or \"false\".")); - } - return [3 /*break*/, 30]; - case 15: - { - block = (0, utils_1.getBlock)(args[i], "air"); - if (typeof block == "string") - (0, errors_1.fail)(block); - outputArgs[cmdArg.name] = block; - return [3 /*break*/, 30]; - } - _f.label = 16; - case 16: return [4 /*yield*/, disambiguateArgument.apply(void 0, __spreadArray(__spreadArray([(0, utils_1.getUnitType)(args[i])], __read(commonArgs), false), [function (u) { return u.emoji() + (0, funcs_1.capitalizeText)(u.name); }], false))]; - case 17: - _f.sent(); - return [3 /*break*/, 30]; - case 18: - if (!globals_1.uuidPattern.test(args[i])) - (0, errors_1.fail)("Invalid uuid string \"".concat(args[i], "\"")); - outputArgs[cmdArg.name] = args[i]; - return [3 /*break*/, 30]; - case 19: return [4 /*yield*/, disambiguateArgument.apply(void 0, __spreadArray(__spreadArray([(0, utils_1.getMap)(args[i])], __read(commonArgs), false), [function (r) { return r.name(); }, - 2], false))]; - case 20: - _f.sent(); - return [3 /*break*/, 30]; - case 21: - if (["rand", "random"].includes(args[i].toLowerCase())) { - outputArgs[cmdArg.name] = "random"; - return [3 /*break*/, 30]; - } - return [4 /*yield*/, disambiguateArgument.apply(void 0, __spreadArray(__spreadArray([(0, utils_1.getMap)(args[i])], __read(commonArgs), false), [function (r) { return r.name(); }, - 2], false))]; - case 22: - _f.sent(); - return [3 /*break*/, 30]; - case 23: return [4 /*yield*/, disambiguateArgument.apply(void 0, __spreadArray(__spreadArray([ranks_1.Rank.search(args[i])], __read(commonArgs), false), [function (r) { return r.coloredName(); }], false))]; - case 24: - _f.sent(); - return [3 /*break*/, 30]; - case 25: return [4 /*yield*/, disambiguateArgument.apply(void 0, __spreadArray(__spreadArray([ranks_1.RoleFlag.search(args[i])], __read(commonArgs), false), [function (f) { return f.coloredName(); }], false))]; - case 26: - _f.sent(); - return [3 /*break*/, 30]; - case 27: return [4 /*yield*/, disambiguateArgument.apply(void 0, __spreadArray(__spreadArray([(0, utils_1.getItem)(args[i])], __read(commonArgs), false), [function (i) { return i.emoji() + (0, funcs_1.capitalizeText)(i.name, "-"); }, - 2], false))]; - case 28: - _f.sent(); - return [3 /*break*/, 30]; - case 29: - cmdArg.type; - (0, funcs_1.crash)("impossible"); - _f.label = 30; - case 30: - _b = _a.next(); - return [3 /*break*/, 2]; - case 31: return [3 /*break*/, 34]; - case 32: - e_2_1 = _f.sent(); - e_2 = { error: e_2_1 }; - return [3 /*break*/, 34]; - case 33: - try { - if (_b && !_b.done && (_e = _a.return)) _e.call(_a); - } - finally { if (e_2) throw e_2.error; } - return [7 /*endfinally*/]; - case 34: return [2 /*return*/, outputArgs]; - } - }); - }); -} -var variadicArgumentTypes = ["player", "string", "map", "mapOrRandom"]; -function isArgOptional(arg, allowMenus) { - return arg.isOptional || (argsSupportingBlank.includes(arg.type) && allowMenus); -} -/** Converts the CommandArg[] to the format accepted by Arc CommandHandler */ -function convertArgs(processedCmdArgs, allowMenus) { - return processedCmdArgs.map(function (arg, index, array) { - var isOptional = isArgOptional(arg, allowMenus) && - !array.slice(index + 1).some(function (c) { return !isArgOptional(c, allowMenus); }); //this is enforced by the arc command handler - //TODO internalize command handler - var brackets = isOptional ? ["[", "]"] : ["<", ">"]; - //if the arg is a string and last argument, make it variadic (so if `/warn player a b c d` is run, the last arg is "a b c d" not "a") - return brackets[0] + arg.name + (variadicArgumentTypes.includes(arg.type) && index + 1 == array.length ? "..." : "") + brackets[1]; - }).join(" "); -} -function handleTapEvent(event) { - var _a; - var sender = players_1.FishPlayer.get(event.player); - if (sender.tapInfo.commandName == null) - return; - var command = exports.allCommands[sender.tapInfo.commandName]; - var usageData = sender.getUsageData(sender.tapInfo.commandName); - var handleTapsUpdated = false; - try { - var failed_1 = false; - (_a = command.tapped) === null || _a === void 0 ? void 0 : _a.call(command, { - args: sender.tapInfo.lastArgs, - data: command.data, - outputFail: function (message) { (0, utils_1.outputFail)(message, sender); failed_1 = true; }, - outputSuccess: function (message) { return (0, utils_1.outputSuccess)(message, sender); }, - output: function (message) { return (0, utils_1.outputMessage)(message, sender); }, - f: formatting_1.outputFormatter_client, - admins: Vars.netServer.admins, - commandLastUsed: usageData.lastUsed, - commandLastUsedSuccessfully: usageData.lastUsedSuccessfully, - lastUsed: usageData.tapLastUsed, - lastUsedSuccessfully: usageData.tapLastUsedSuccessfully, - sender: sender, - tile: event.tile, - x: event.tile.x, - y: event.tile.y, - currentTapMode: sender.tapInfo.commandName == null ? "off" : sender.tapInfo.mode, - handleTaps: function (mode) { - if (mode == "off") { - sender.tapInfo.commandName = null; - return; - } - sender.tapInfo.mode = mode; - handleTapsUpdated = true; - }, - }); - if (!failed_1) - usageData.tapLastUsedSuccessfully = Date.now(); - } - catch (err) { - (0, utils_1.handleError)(err, sender, utils_1.outputFail, "".concat(sender.cleanedName, " ran /").concat(sender.tapInfo.commandName, " and tapped")); - } - finally { - if (sender.tapInfo.mode == "once" && !handleTapsUpdated) { - sender.tapInfo.commandName = null; - } - usageData.tapLastUsed = Date.now(); - } -} -/** - * Registers all commands in a list to a client command handler. - **/ -function register(commands, clientHandler, serverHandler) { - var e_3, _a; - var _loop_1 = function (name, _data) { - //Invoke thunk if necessary - var data = typeof _data == "function" ? _data() : _data; - //Process the args - var processedCmdArgs = data.args.map(processArgString); - clientHandler.removeCommand(name); //The function silently fails if the argument doesn't exist so this is safe - clientHandler.register(name, convertArgs(processedCmdArgs, true), data.description, new CommandHandler.CommandRunner({ accept: function (unjoinedRawArgs, sender) { - return __awaiter(this, void 0, void 0, function () { - var fishSender, rawArgs, resolvedArgs, err_1, usageData, failed, args_1, requirements, err_2; - var _a; - return __generator(this, function (_b) { - switch (_b.label) { - case 0: - if (!initialized) - (0, funcs_1.crash)("Commands not initialized!"); - fishSender = players_1.FishPlayer.get(sender); - players_1.FishPlayer.onPlayerCommand(fishSender, name, unjoinedRawArgs); - //Verify authorization - //as a bonus, this crashes if data.perm is undefined - if (!data.perm.check(fishSender)) { - if (data.customUnauthorizedMessage) { - (0, utils_1.outputFail)(data.customUnauthorizedMessage, sender); - globals_1.FishEvents.fire("commandUnauthorized", [fishSender, name]); - } - else if (data.isHidden) - (0, utils_1.outputMessage)(hiddenUnauthorizedMessage, sender); - else - (0, utils_1.outputFail)(data.perm.unauthorizedMessage, sender); - return [2 /*return*/]; - } - rawArgs = joinArgs(unjoinedRawArgs); - _b.label = 1; - case 1: - _b.trys.push([1, 3, , 4]); - return [4 /*yield*/, processArgs(rawArgs, processedCmdArgs, fishSender)]; - case 2: - resolvedArgs = _b.sent(); - return [3 /*break*/, 4]; - case 3: - err_1 = _b.sent(); - (0, utils_1.handleError)(err_1, fishSender, utils_1.outputFail, "".concat(fishSender.cleanedName, " ran /").concat(name)); - return [2 /*return*/]; - case 4: - usageData = fishSender.getUsageData(name); - failed = false; - _b.label = 5; - case 5: - _b.trys.push([5, 7, 8, 9]); - args_1 = { - rawArgs: rawArgs, - args: resolvedArgs, - sender: fishSender, - data: data.data, - outputFail: function (message) { (0, utils_1.outputFail)(message, sender); failed = true; }, - outputSuccess: function (message) { return (0, utils_1.outputSuccess)(message, sender); }, - output: function (message) { return (0, utils_1.outputMessage)(message, sender); }, - f: formatting_1.f_client, - execServer: function (command) { return serverHandler.handleMessage(command); }, - admins: Vars.netServer.admins, - lastUsedSender: usageData.lastUsed, - lastUsedSuccessfullySender: usageData.lastUsedSuccessfully, - lastUsedSuccessfully: ((_a = globalUsageData[name]) !== null && _a !== void 0 ? _a : (globalUsageData[name] = { lastUsed: -1, lastUsedSuccessfully: -1 })).lastUsedSuccessfully, - allCommands: exports.allCommands, - currentTapMode: fishSender.tapInfo.commandName == null ? "off" : fishSender.tapInfo.mode, - handleTaps: function (mode) { - if (data.tapped == undefined) - (0, funcs_1.crash)("No tap handler to activate: command \"".concat(name, "\"")); - if (mode == "off") { - fishSender.tapInfo.commandName = null; - } - else { - fishSender.tapInfo.commandName = name; - fishSender.tapInfo.mode = mode; - } - fishSender.tapInfo.lastArgs = resolvedArgs; - }, - }; - requirements = typeof data.requirements == "function" ? data.requirements(args_1) : data.requirements; - requirements === null || requirements === void 0 ? void 0 : requirements.forEach(function (r) { return r(args_1); }); - return [4 /*yield*/, data.handler(args_1)]; - case 6: - _b.sent(); - //Update usage data - if (!failed) { - usageData.lastUsedSuccessfully = globalUsageData[name].lastUsedSuccessfully = Date.now(); - } - return [3 /*break*/, 9]; - case 7: - err_2 = _b.sent(); - (0, utils_1.handleError)(err_2, fishSender, utils_1.outputFail, "".concat(fishSender.cleanedName, " ran /").concat(name)); - return [3 /*break*/, 9]; - case 8: - usageData.lastUsed = globalUsageData[name].lastUsed = Date.now(); - return [7 /*endfinally*/]; - case 9: return [2 /*return*/]; - } - }); - }); - } })); - exports.allCommands[name] = data; - }; - try { - for (var _b = __values(Object.entries(commands)), _c = _b.next(); !_c.done; _c = _b.next()) { - var _d = __read(_c.value, 2), name = _d[0], _data = _d[1]; - _loop_1(name, _data); - } - } - catch (e_3_1) { e_3 = { error: e_3_1 }; } - finally { - try { - if (_c && !_c.done && (_a = _b.return)) _a.call(_b); - } - finally { if (e_3) throw e_3.error; } - } -} -function registerConsole(commands, serverHandler) { - var e_4, _a; - var _loop_2 = function (name, data) { - //Process the args - var processedCmdArgs = data.args.map(processArgString); - serverHandler.removeCommand(name); //The function silently fails if the argument doesn't exist so this is safe - serverHandler.register(name, convertArgs(processedCmdArgs, false), data.description, new CommandHandler.CommandRunner({ accept: function (rawArgs) { - return __awaiter(this, void 0, void 0, function () { - var resolvedArgs, err_3, usageData, failed_2; - var _a; - var _b; - return __generator(this, function (_c) { - switch (_c.label) { - case 0: - if (!initialized) - (0, funcs_1.crash)("Commands not initialized!"); - _c.label = 1; - case 1: - _c.trys.push([1, 3, , 4]); - return [4 /*yield*/, processArgs(rawArgs, processedCmdArgs, null)]; - case 2: - resolvedArgs = _c.sent(); - return [3 /*break*/, 4]; - case 3: - err_3 = _c.sent(); - //if args are invalid - Log.err(err_3); - return [2 /*return*/]; - case 4: - usageData = ((_a = globalUsageData[_b = "_console_" + name]) !== null && _a !== void 0 ? _a : (globalUsageData[_b] = { lastUsed: -1, lastUsedSuccessfully: -1 })); - try { - failed_2 = false; - data.handler(__assign({ rawArgs: rawArgs, args: resolvedArgs, data: data.data, outputFail: function (message) { (0, utils_1.outputConsole)(message, Log.err); failed_2 = true; }, outputSuccess: utils_1.outputConsole, output: utils_1.outputConsole, f: formatting_1.f_server, execServer: function (command) { return serverHandler.handleMessage(command); }, admins: Vars.netServer.admins }, usageData)); - usageData.lastUsed = Date.now(); - if (!failed_2) - usageData.lastUsedSuccessfully = Date.now(); - } - catch (err) { - usageData.lastUsed = Date.now(); - if (err instanceof errors_1.CommandError) { - Log.warn(typeof err.data == "function" ? err.data("&fr") : err.data); - } - else { - Log.err("&lrAn error occured while executing the command!&fr"); - Log.err((0, funcs_1.parseError)(err)); - } - } - return [2 /*return*/]; - } - }); - }); - } })); - exports.allConsoleCommands[name] = data; - }; - try { - for (var _b = __values(Object.entries(commands)), _c = _b.next(); !_c.done; _c = _b.next()) { - var _d = __read(_c.value, 2), name = _d[0], data = _d[1]; - _loop_2(name, data); - } - } - catch (e_4_1) { e_4 = { error: e_4_1 }; } - finally { - try { - if (_c && !_c.done && (_a = _b.return)) _a.call(_b); - } - finally { if (e_4) throw e_4.error; } - } -} -function initialize() { - var e_5, _a, e_6, _b; - if (initialized) { - (0, funcs_1.crash)("Already initialized commands."); - } - try { - for (var _c = __values(Object.entries(exports.allConsoleCommands)), _d = _c.next(); !_d.done; _d = _c.next()) { - var _e = __read(_d.value, 2), key = _e[0], command_1 = _e[1]; - if (command_1.init) - command_1.data = command_1.init(); - } - } - catch (e_5_1) { e_5 = { error: e_5_1 }; } - finally { - try { - if (_d && !_d.done && (_a = _c.return)) _a.call(_c); - } - finally { if (e_5) throw e_5.error; } - } - try { - for (var _f = __values(Object.entries(exports.allCommands)), _g = _f.next(); !_g.done; _g = _f.next()) { - var _h = __read(_g.value, 2), key = _h[0], command_2 = _h[1]; - if (command_2.init) - command_2.data = command_2.init(); - } - } - catch (e_6_1) { e_6 = { error: e_6_1 }; } - finally { - try { - if (_g && !_g.done && (_b = _f.return)) _b.call(_f); - } - finally { if (e_6) throw e_6.error; } - } - initialized = true; -} -function reset() { - var e_7, _a, e_8, _b; - initialized = false; - try { - for (var _c = __values(Object.entries(exports.allConsoleCommands)), _d = _c.next(); !_d.done; _d = _c.next()) { - var _e = __read(_d.value, 2), key = _e[0], command_3 = _e[1]; - if (command_3.init) - command_3.data = undefined; - } - } - catch (e_7_1) { e_7 = { error: e_7_1 }; } - finally { - try { - if (_d && !_d.done && (_a = _c.return)) _a.call(_c); - } - finally { if (e_7) throw e_7.error; } - } - try { - for (var _f = __values(Object.entries(exports.allCommands)), _g = _f.next(); !_g.done; _g = _f.next()) { - var _h = __read(_g.value, 2), key = _h[0], command_4 = _h[1]; - if (command_4.init) - command_4.data = undefined; - } - } - catch (e_8_1) { e_8 = { error: e_8_1 }; } - finally { - try { - if (_g && !_g.done && (_b = _f.return)) _b.call(_f); - } - finally { if (e_8) throw e_8.error; } - } -} +"use strict"; +/* +Copyright © BalaM314, 2026. All Rights Reserved. +This file contains the commands framework. +For usage information, see docs/framework-usage-guide.md +For maintenance information, see docs/frameworks.md +*/ +//Behold, the power of typescript! +var __assign = (this && this.__assign) || function () { + __assign = Object.assign || function(t) { + for (var s, i = 1, n = arguments.length; i < n; i++) { + s = arguments[i]; + for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p)) + t[p] = s[p]; + } + return t; + }; + return __assign.apply(this, arguments); +}; +var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { + function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } + return new (P || (P = Promise))(function (resolve, reject) { + function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } + function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } + function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } + step((generator = generator.apply(thisArg, _arguments || [])).next()); + }); +}; +var __generator = (this && this.__generator) || function (thisArg, body) { + var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g = Object.create((typeof Iterator === "function" ? Iterator : Object).prototype); + return g.next = verb(0), g["throw"] = verb(1), g["return"] = verb(2), typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g; + function verb(n) { return function (v) { return step([n, v]); }; } + function step(op) { + if (f) throw new TypeError("Generator is already executing."); + while (g && (g = 0, op[0] && (_ = 0)), _) try { + if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t; + if (y = 0, t) op = [op[0] & 2, t.value]; + switch (op[0]) { + case 0: case 1: t = op; break; + case 4: _.label++; return { value: op[1], done: false }; + case 5: _.label++; y = op[1]; op = [0]; continue; + case 7: op = _.ops.pop(); _.trys.pop(); continue; + default: + if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; } + if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; } + if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; } + if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; } + if (t[2]) _.ops.pop(); + _.trys.pop(); continue; + } + op = body.call(thisArg, _); + } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; } + if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true }; + } +}; +var __read = (this && this.__read) || function (o, n) { + var m = typeof Symbol === "function" && o[Symbol.iterator]; + if (!m) return o; + var i = m.call(o), r, ar = [], e; + try { + while ((n === void 0 || n-- > 0) && !(r = i.next()).done) ar.push(r.value); + } + catch (error) { e = { error: error }; } + finally { + try { + if (r && !r.done && (m = i["return"])) m.call(i); + } + finally { if (e) throw e.error; } + } + return ar; +}; +var __values = (this && this.__values) || function(o) { + var s = typeof Symbol === "function" && Symbol.iterator, m = s && o[s], i = 0; + if (m) return m.call(o); + if (o && typeof o.length === "number") return { + next: function () { + if (o && i >= o.length) o = void 0; + return { value: o && o[i++], done: !o }; + } + }; + throw new TypeError(s ? "Object is not iterable." : "Symbol.iterator is not defined."); +}; +var __spreadArray = (this && this.__spreadArray) || function (to, from, pack) { + if (pack || arguments.length === 2) for (var i = 0, l = from.length, ar; i < l; i++) { + if (ar || !(i in from)) { + if (!ar) ar = Array.prototype.slice.call(from, 0, i); + ar[i] = from[i]; + } + } + return to.concat(ar || Array.prototype.slice.call(from)); +}; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.consoleCommandList = exports.commandList = exports.allConsoleCommands = exports.allCommands = void 0; +exports.command = command; +exports.processArgString = processArgString; +exports.formatArg = formatArg; +exports.joinArgs = joinArgs; +exports.disambiguateArgument = disambiguateArgument; +exports.processArgs = processArgs; +exports.convertArgs = convertArgs; +exports.handleTapEvent = handleTapEvent; +exports.register = register; +exports.registerConsole = registerConsole; +exports.initialize = initialize; +exports.reset = reset; +var config_1 = require("/config"); +var errors_1 = require("/frameworks/commands/errors"); +var formatting_1 = require("/frameworks/commands/formatting"); +var types_1 = require("/frameworks/commands/types"); +var menus_1 = require("/frameworks/menus"); +var funcs_1 = require("/funcs"); +var globals_1 = require("/globals"); +var players_1 = require("/players"); +var ranks_1 = require("/ranks"); +var utils_1 = require("/utils"); +var hiddenUnauthorizedMessage = "[scarlet]Unknown command. Check [lightgray]/help[scarlet]."; +/** Flag to prevent double initialization */ +var initialized = false; +/** Stores all chat comamnds by their name. */ +exports.allCommands = {}; +/** Stores all console commands by their name. */ +exports.allConsoleCommands = {}; +/** Stores the last usage data for chat commands by their name. */ +var globalUsageData = {}; +/** Helper function to get the correct type for command lists. */ +var commandList = function (list) { return list; }; +exports.commandList = commandList; +/** Helper function to get the correct type for command lists. */ +var consoleCommandList = function (list) { return list; }; +exports.consoleCommandList = consoleCommandList; +/** + * Helper function to get the correct type definitions for commands that use "data" or init(). + * Necessary because, while typescript is capable of inferring A1, A2... + * ``` + * { + * prop1: Type; + * prop2: Type; + * } + * ``` + * it cannot handle inferring A1 and B1. + * ``` + * { + * prop1: Type; + * prop2: Type; + * } + * ``` + */ +function command(input) { + return input; +} +/** Takes an arg string, like `reason:string?` and converts it to a CommandArg. */ +function processArgString(str) { + //this was copypasted from mlogx haha + var matchResult = str.match(/(\w+):(\w+)(\?)?/); + if (!matchResult) { + (0, funcs_1.crash)("Bad arg string ".concat(str, ": does not match pattern word:word(?)")); + } + var _a = __read(matchResult, 4), name = _a[1], type = _a[2], isOptional = _a[3]; + if (types_1.commandArgTypes.includes(type)) { + return { name: name, type: type, isOptional: !!isOptional }; + } + else { + (0, funcs_1.crash)("Bad arg string ".concat(str, ": invalid type ").concat(type)); + } +} +function formatArg(a) { + var isOptional = a.at(-1) == "?"; + var brackets = isOptional ? ["[", "]"] : ["<", ">"]; + return brackets[0] + a.split(":")[0] + brackets[1]; +} +/** Joins multi-word arguments that have been groups with quotes. Ex: turns [`"a`, `b"`] into [`a b`]*/ +function joinArgs(rawArgs) { + var e_1, _a; + var outputArgs = []; + var groupedArg = null; + try { + for (var rawArgs_1 = __values(rawArgs), rawArgs_1_1 = rawArgs_1.next(); !rawArgs_1_1.done; rawArgs_1_1 = rawArgs_1.next()) { + var arg = rawArgs_1_1.value; + if (arg.startsWith("\"") && groupedArg == null) { + groupedArg = []; + } + if (groupedArg) { + groupedArg.push(arg); + if (arg.endsWith("\"")) { + outputArgs.push(groupedArg.join(" ").slice(1, -1)); + groupedArg = null; + } + } + else { + outputArgs.push(arg); + } + } + } + catch (e_1_1) { e_1 = { error: e_1_1 }; } + finally { + try { + if (rawArgs_1_1 && !rawArgs_1_1.done && (_a = rawArgs_1.return)) _a.call(rawArgs_1); + } + finally { if (e_1) throw e_1.error; } + } + if (groupedArg != null) { + //return `Unterminated string literal.`; + outputArgs.push(groupedArg.join(" ")); + } + return outputArgs; +} +function disambiguateArgument(options_1, arg_1, _a, sender_1, outputArgs_1, optionStringifier_1) { + return __awaiter(this, arguments, void 0, function (options, arg, _b, sender, outputArgs, optionStringifier, columns) { + var word, a_an_word, _c, _d; + var name = _b.name, type = _b.type; + if (columns === void 0) { columns = 3; } + return __generator(this, function (_e) { + switch (_e.label) { + case 0: + if (!(options == null)) return [3 /*break*/, 1]; + (0, errors_1.fail)("".concat((0, funcs_1.capitalizeText)(types_1.commandArgNames[type]), " \"").concat(arg, "\" not found.")); + return [3 /*break*/, 4]; + case 1: + if (!(options instanceof Array)) return [3 /*break*/, 3]; + word = types_1.commandArgNames[type]; + if (!sender) + (0, errors_1.fail)("Name \"".concat(arg, "\" could refer to more than one ").concat(word, ".")); + a_an_word = (0, funcs_1.indefiniteArticle)(word); + _c = outputArgs; + _d = name; + return [4 /*yield*/, menus_1.Menu.menu("Select ".concat(a_an_word), "Select ".concat(a_an_word, " for the argument \"").concat(name, "\""), options, sender, { + includeCancel: true, + optionStringifier: optionStringifier, + columns: columns, + })]; + case 2: + _c[_d] = _e.sent(); + return [3 /*break*/, 4]; + case 3: + outputArgs[name] = options; + _e.label = 4; + case 4: return [2 /*return*/]; + } + }); + }); +} +var argsSupportingBlank = ["player", "offlinePlayer", "unittype", "map", "mapOrRandom", "rank", "roleflag", "item"]; +/** Takes a list of joined args passed to the command, and processes it, turning it into a kwargs style object. */ +function processArgs(args, processedCmdArgs, sender) { + return __awaiter(this, void 0, void 0, function () { + var outputArgs, _a, _b, _c, i, cmdArg, commonArgs, _d, player, team, number, milliseconds, block, e_2_1; + var e_2, _e; + return __generator(this, function (_f) { + switch (_f.label) { + case 0: + outputArgs = {}; + _f.label = 1; + case 1: + _f.trys.push([1, 32, 33, 34]); + _a = __values(processedCmdArgs.entries()), _b = _a.next(); + _f.label = 2; + case 2: + if (!!_b.done) return [3 /*break*/, 31]; + _c = __read(_b.value, 2), i = _c[0], cmdArg = _c[1]; + if (!(i in args) || args[i] === "") { + //if the arg was not provided or it was empty + if (cmdArg.isOptional) { + outputArgs[cmdArg.name] = undefined; + return [3 /*break*/, 30]; + } + else if (sender && argsSupportingBlank.includes(cmdArg.type)) { + //it will be resolved later + } + else { + (0, errors_1.fail)("No value specified for arg ".concat(cmdArg.name, ". Did you type two spaces instead of one?")); + } + } + commonArgs = [args[i], cmdArg, sender, outputArgs]; + _d = cmdArg.type; + switch (_d) { + case "player": return [3 /*break*/, 3]; + case "offlinePlayer": return [3 /*break*/, 5]; + case "team": return [3 /*break*/, 10]; + case "number": return [3 /*break*/, 11]; + case "time": return [3 /*break*/, 12]; + case "string": return [3 /*break*/, 13]; + case "boolean": return [3 /*break*/, 14]; + case "block": return [3 /*break*/, 15]; + case "unittype": return [3 /*break*/, 16]; + case "uuid": return [3 /*break*/, 18]; + case "map": return [3 /*break*/, 19]; + case "mapOrRandom": return [3 /*break*/, 21]; + case "rank": return [3 /*break*/, 23]; + case "roleflag": return [3 /*break*/, 25]; + case "item": return [3 /*break*/, 27]; + } + return [3 /*break*/, 29]; + case 3: return [4 /*yield*/, disambiguateArgument.apply(void 0, __spreadArray(__spreadArray([players_1.FishPlayer.search(players_1.FishPlayer.getAllOnline(), args[i])], __read(commonArgs), false), [function (player) { return (player.marked() ? config_1.prefixes.marked : player.autoflagged ? config_1.prefixes.flagged : "") + (Strings.stripColors(player.name).length >= 3 ? + player.name + : (0, funcs_1.escapeStringColorsClient)(player.name)); }, + 2], false))]; + case 4: + _f.sent(); + return [3 /*break*/, 30]; + case 5: + if (!globals_1.uuidPattern.test(args[i])) return [3 /*break*/, 6]; + player = players_1.FishPlayer.getById(args[i]); + if (player == null) + (0, errors_1.fail)("Player with uuid \"".concat(args[i], "\" not found. Specify \"create:").concat(args[i], "\" to create the player.")); + outputArgs[cmdArg.name] = player; + return [3 /*break*/, 9]; + case 6: + if (!(args[i].startsWith("create:") && globals_1.uuidPattern.test(args[i].split("create:")[1]))) return [3 /*break*/, 7]; + outputArgs[cmdArg.name] = players_1.FishPlayer.getFromInfo(Vars.netServer.admins.getInfo(args[i].split("create:")[1])); + return [3 /*break*/, 9]; + case 7: return [4 /*yield*/, disambiguateArgument.apply(void 0, __spreadArray(__spreadArray([players_1.FishPlayer.search(Object.values(players_1.FishPlayer.cachedPlayers), args[i])], __read(commonArgs), false), [function (player) { return Strings.stripColors(player.name).length >= 3 ? + player.name + : (0, funcs_1.escapeStringColorsClient)(player.name); }, + 2], false))]; + case 8: + _f.sent(); + _f.label = 9; + case 9: return [3 /*break*/, 30]; + case 10: + { + team = (0, utils_1.getTeam)(args[i]); + if (typeof team == "string") + (0, errors_1.fail)(team); + outputArgs[cmdArg.name] = team; + return [3 /*break*/, 30]; + } + _f.label = 11; + case 11: + { + number = Number(args[i]); + if (isNaN(number)) { + if (/\(\d+,/.test(args[i])) + number = Number(args[i].slice(1, -1)); + else if (/\d+\)/.test(args[i])) + number = Number(args[i].slice(0, -1)); + if (isNaN(number)) + (0, errors_1.fail)("Invalid number \"".concat(args[i], "\"")); + } + outputArgs[cmdArg.name] = number; + return [3 /*break*/, 30]; + } + _f.label = 12; + case 12: + { + milliseconds = (0, utils_1.parseTimeString)(args[i]); + if (milliseconds == null) + (0, errors_1.fail)("Invalid time string \"".concat(args[i], "\"")); + outputArgs[cmdArg.name] = milliseconds; + return [3 /*break*/, 30]; + } + _f.label = 13; + case 13: + outputArgs[cmdArg.name] = args[i]; + return [3 /*break*/, 30]; + case 14: + switch (args[i].toLowerCase()) { + case "true": + case "yes": + case "yeah": + case "ya": + case "ye": + case "t": + case "y": + case "1": + outputArgs[cmdArg.name] = true; + break; + case "false": + case "no": + case "nah": + case "nay": + case "nope": + case "f": + case "n": + case "0": + outputArgs[cmdArg.name] = false; + break; + default: (0, errors_1.fail)("Argument ".concat(args[i], " is not a boolean. Try \"true\" or \"false\".")); + } + return [3 /*break*/, 30]; + case 15: + { + block = (0, utils_1.getBlock)(args[i], "air"); + if (typeof block == "string") + (0, errors_1.fail)(block); + outputArgs[cmdArg.name] = block; + return [3 /*break*/, 30]; + } + _f.label = 16; + case 16: return [4 /*yield*/, disambiguateArgument.apply(void 0, __spreadArray(__spreadArray([(0, utils_1.getUnitType)(args[i])], __read(commonArgs), false), [function (u) { return u.emoji() + (0, funcs_1.capitalizeText)(u.name); }], false))]; + case 17: + _f.sent(); + return [3 /*break*/, 30]; + case 18: + if (!globals_1.uuidPattern.test(args[i])) + (0, errors_1.fail)("Invalid uuid string \"".concat(args[i], "\"")); + outputArgs[cmdArg.name] = args[i]; + return [3 /*break*/, 30]; + case 19: return [4 /*yield*/, disambiguateArgument.apply(void 0, __spreadArray(__spreadArray([(0, utils_1.getMap)(args[i])], __read(commonArgs), false), [function (r) { return r.name(); }, + 2], false))]; + case 20: + _f.sent(); + return [3 /*break*/, 30]; + case 21: + if (["rand", "random"].includes(args[i].toLowerCase())) { + outputArgs[cmdArg.name] = "random"; + return [3 /*break*/, 30]; + } + return [4 /*yield*/, disambiguateArgument.apply(void 0, __spreadArray(__spreadArray([(0, utils_1.getMap)(args[i])], __read(commonArgs), false), [function (r) { return r.name(); }, + 2], false))]; + case 22: + _f.sent(); + return [3 /*break*/, 30]; + case 23: return [4 /*yield*/, disambiguateArgument.apply(void 0, __spreadArray(__spreadArray([ranks_1.Rank.search(args[i])], __read(commonArgs), false), [function (r) { return r.coloredName(); }], false))]; + case 24: + _f.sent(); + return [3 /*break*/, 30]; + case 25: return [4 /*yield*/, disambiguateArgument.apply(void 0, __spreadArray(__spreadArray([ranks_1.RoleFlag.search(args[i])], __read(commonArgs), false), [function (f) { return f.coloredName(); }], false))]; + case 26: + _f.sent(); + return [3 /*break*/, 30]; + case 27: return [4 /*yield*/, disambiguateArgument.apply(void 0, __spreadArray(__spreadArray([(0, utils_1.getItem)(args[i])], __read(commonArgs), false), [function (i) { return i.emoji() + (0, funcs_1.capitalizeText)(i.name, "-"); }, + 2], false))]; + case 28: + _f.sent(); + return [3 /*break*/, 30]; + case 29: + cmdArg.type; + (0, funcs_1.crash)("impossible"); + _f.label = 30; + case 30: + _b = _a.next(); + return [3 /*break*/, 2]; + case 31: return [3 /*break*/, 34]; + case 32: + e_2_1 = _f.sent(); + e_2 = { error: e_2_1 }; + return [3 /*break*/, 34]; + case 33: + try { + if (_b && !_b.done && (_e = _a.return)) _e.call(_a); + } + finally { if (e_2) throw e_2.error; } + return [7 /*endfinally*/]; + case 34: return [2 /*return*/, outputArgs]; + } + }); + }); +} +var variadicArgumentTypes = ["player", "string", "map", "mapOrRandom"]; +function isArgOptional(arg, allowMenus) { + return arg.isOptional || (argsSupportingBlank.includes(arg.type) && allowMenus); +} +/** Converts the CommandArg[] to the format accepted by Arc CommandHandler */ +function convertArgs(processedCmdArgs, allowMenus) { + return processedCmdArgs.map(function (arg, index, array) { + var isOptional = isArgOptional(arg, allowMenus) && + !array.slice(index + 1).some(function (c) { return !isArgOptional(c, allowMenus); }); //this is enforced by the arc command handler + //TODO internalize command handler + var brackets = isOptional ? ["[", "]"] : ["<", ">"]; + //if the arg is a string and last argument, make it variadic (so if `/warn player a b c d` is run, the last arg is "a b c d" not "a") + return brackets[0] + arg.name + (variadicArgumentTypes.includes(arg.type) && index + 1 == array.length ? "..." : "") + brackets[1]; + }).join(" "); +} +function handleTapEvent(event) { + var _a; + var sender = players_1.FishPlayer.get(event.player); + if (sender.tapInfo.commandName == null) + return; + var command = exports.allCommands[sender.tapInfo.commandName]; + var usageData = sender.getUsageData(sender.tapInfo.commandName); + var handleTapsUpdated = false; + try { + var failed_1 = false; + (_a = command.tapped) === null || _a === void 0 ? void 0 : _a.call(command, { + args: sender.tapInfo.lastArgs, + data: command.data, + outputFail: function (message) { (0, utils_1.outputFail)(message, sender); failed_1 = true; }, + outputSuccess: function (message) { return (0, utils_1.outputSuccess)(message, sender); }, + output: function (message) { return (0, utils_1.outputMessage)(message, sender); }, + f: formatting_1.outputFormatter_client, + admins: Vars.netServer.admins, + commandLastUsed: usageData.lastUsed, + commandLastUsedSuccessfully: usageData.lastUsedSuccessfully, + lastUsed: usageData.tapLastUsed, + lastUsedSuccessfully: usageData.tapLastUsedSuccessfully, + sender: sender, + tile: event.tile, + x: event.tile.x, + y: event.tile.y, + currentTapMode: sender.tapInfo.commandName == null ? "off" : sender.tapInfo.mode, + handleTaps: function (mode) { + if (mode == "off") { + sender.tapInfo.commandName = null; + return; + } + sender.tapInfo.mode = mode; + handleTapsUpdated = true; + }, + }); + if (!failed_1) + usageData.tapLastUsedSuccessfully = Date.now(); + } + catch (err) { + (0, utils_1.handleError)(err, sender, utils_1.outputFail, "".concat(sender.cleanedName, " ran /").concat(sender.tapInfo.commandName, " and tapped")); + } + finally { + if (sender.tapInfo.mode == "once" && !handleTapsUpdated) { + sender.tapInfo.commandName = null; + } + usageData.tapLastUsed = Date.now(); + } +} +/** + * Registers all commands in a list to a client command handler. + **/ +function register(commands, clientHandler, serverHandler) { + var e_3, _a; + var _loop_1 = function (name, _data) { + //Invoke thunk if necessary + var data = typeof _data == "function" ? _data() : _data; + //Process the args + var processedCmdArgs = data.args.map(processArgString); + clientHandler.removeCommand(name); //The function silently fails if the argument doesn't exist so this is safe + clientHandler.register(name, convertArgs(processedCmdArgs, true), data.description, new CommandHandler.CommandRunner({ accept: function (unjoinedRawArgs, sender) { + return __awaiter(this, void 0, void 0, function () { + var fishSender, rawArgs, resolvedArgs, err_1, usageData, failed, args_1, requirements, err_2; + var _a; + return __generator(this, function (_b) { + switch (_b.label) { + case 0: + if (!initialized) + (0, funcs_1.crash)("Commands not initialized!"); + fishSender = players_1.FishPlayer.get(sender); + players_1.FishPlayer.onPlayerCommand(fishSender, name, unjoinedRawArgs); + //Verify authorization + //as a bonus, this crashes if data.perm is undefined + if (!data.perm.check(fishSender)) { + if (data.customUnauthorizedMessage) { + (0, utils_1.outputFail)(data.customUnauthorizedMessage, sender); + globals_1.FishEvents.fire("commandUnauthorized", [fishSender, name]); + } + else if (data.isHidden) + (0, utils_1.outputMessage)(hiddenUnauthorizedMessage, sender); + else + (0, utils_1.outputFail)(data.perm.unauthorizedMessage, sender); + return [2 /*return*/]; + } + rawArgs = joinArgs(unjoinedRawArgs); + _b.label = 1; + case 1: + _b.trys.push([1, 3, , 4]); + return [4 /*yield*/, processArgs(rawArgs, processedCmdArgs, fishSender)]; + case 2: + resolvedArgs = _b.sent(); + return [3 /*break*/, 4]; + case 3: + err_1 = _b.sent(); + (0, utils_1.handleError)(err_1, fishSender, utils_1.outputFail, "".concat(fishSender.cleanedName, " ran /").concat(name)); + return [2 /*return*/]; + case 4: + usageData = fishSender.getUsageData(name); + failed = false; + _b.label = 5; + case 5: + _b.trys.push([5, 7, 8, 9]); + args_1 = { + rawArgs: rawArgs, + args: resolvedArgs, + sender: fishSender, + data: data.data, + outputFail: function (message) { (0, utils_1.outputFail)(message, sender); failed = true; }, + outputSuccess: function (message) { return (0, utils_1.outputSuccess)(message, sender); }, + output: function (message) { return (0, utils_1.outputMessage)(message, sender); }, + f: formatting_1.f_client, + execServer: function (command) { return serverHandler.handleMessage(command); }, + admins: Vars.netServer.admins, + lastUsedSender: usageData.lastUsed, + lastUsedSuccessfullySender: usageData.lastUsedSuccessfully, + lastUsedSuccessfully: ((_a = globalUsageData[name]) !== null && _a !== void 0 ? _a : (globalUsageData[name] = { lastUsed: -1, lastUsedSuccessfully: -1 })).lastUsedSuccessfully, + allCommands: exports.allCommands, + currentTapMode: fishSender.tapInfo.commandName == null ? "off" : fishSender.tapInfo.mode, + handleTaps: function (mode) { + if (data.tapped == undefined) + (0, funcs_1.crash)("No tap handler to activate: command \"".concat(name, "\"")); + if (mode == "off") { + fishSender.tapInfo.commandName = null; + } + else { + fishSender.tapInfo.commandName = name; + fishSender.tapInfo.mode = mode; + } + fishSender.tapInfo.lastArgs = resolvedArgs; + }, + }; + requirements = typeof data.requirements == "function" ? data.requirements(args_1) : data.requirements; + requirements === null || requirements === void 0 ? void 0 : requirements.forEach(function (r) { return r(args_1); }); + return [4 /*yield*/, data.handler(args_1)]; + case 6: + _b.sent(); + //Update usage data + if (!failed) { + usageData.lastUsedSuccessfully = globalUsageData[name].lastUsedSuccessfully = Date.now(); + } + return [3 /*break*/, 9]; + case 7: + err_2 = _b.sent(); + (0, utils_1.handleError)(err_2, fishSender, utils_1.outputFail, "".concat(fishSender.cleanedName, " ran /").concat(name)); + return [3 /*break*/, 9]; + case 8: + usageData.lastUsed = globalUsageData[name].lastUsed = Date.now(); + return [7 /*endfinally*/]; + case 9: return [2 /*return*/]; + } + }); + }); + } })); + exports.allCommands[name] = data; + }; + try { + for (var _b = __values(Object.entries(commands)), _c = _b.next(); !_c.done; _c = _b.next()) { + var _d = __read(_c.value, 2), name = _d[0], _data = _d[1]; + _loop_1(name, _data); + } + } + catch (e_3_1) { e_3 = { error: e_3_1 }; } + finally { + try { + if (_c && !_c.done && (_a = _b.return)) _a.call(_b); + } + finally { if (e_3) throw e_3.error; } + } +} +function registerConsole(commands, serverHandler) { + var e_4, _a; + var _loop_2 = function (name, data) { + //Process the args + var processedCmdArgs = data.args.map(processArgString); + serverHandler.removeCommand(name); //The function silently fails if the argument doesn't exist so this is safe + serverHandler.register(name, convertArgs(processedCmdArgs, false), data.description, new CommandHandler.CommandRunner({ accept: function (rawArgs) { + return __awaiter(this, void 0, void 0, function () { + var resolvedArgs, err_3, usageData, failed_2; + var _a; + var _b; + return __generator(this, function (_c) { + switch (_c.label) { + case 0: + if (!initialized) + (0, funcs_1.crash)("Commands not initialized!"); + _c.label = 1; + case 1: + _c.trys.push([1, 3, , 4]); + return [4 /*yield*/, processArgs(rawArgs, processedCmdArgs, null)]; + case 2: + resolvedArgs = _c.sent(); + return [3 /*break*/, 4]; + case 3: + err_3 = _c.sent(); + //if args are invalid + Log.err(err_3); + return [2 /*return*/]; + case 4: + usageData = ((_a = globalUsageData[_b = "_console_" + name]) !== null && _a !== void 0 ? _a : (globalUsageData[_b] = { lastUsed: -1, lastUsedSuccessfully: -1 })); + try { + failed_2 = false; + data.handler(__assign({ rawArgs: rawArgs, args: resolvedArgs, data: data.data, outputFail: function (message) { (0, utils_1.outputConsole)(message, Log.err); failed_2 = true; }, outputSuccess: utils_1.outputConsole, output: utils_1.outputConsole, f: formatting_1.f_server, execServer: function (command) { return serverHandler.handleMessage(command); }, admins: Vars.netServer.admins }, usageData)); + usageData.lastUsed = Date.now(); + if (!failed_2) + usageData.lastUsedSuccessfully = Date.now(); + } + catch (err) { + usageData.lastUsed = Date.now(); + if (err instanceof errors_1.CommandError) { + Log.warn(typeof err.data == "function" ? err.data("&fr") : err.data); + } + else { + Log.err("&lrAn error occured while executing the command!&fr"); + Log.err((0, funcs_1.parseError)(err)); + } + } + return [2 /*return*/]; + } + }); + }); + } })); + exports.allConsoleCommands[name] = data; + }; + try { + for (var _b = __values(Object.entries(commands)), _c = _b.next(); !_c.done; _c = _b.next()) { + var _d = __read(_c.value, 2), name = _d[0], data = _d[1]; + _loop_2(name, data); + } + } + catch (e_4_1) { e_4 = { error: e_4_1 }; } + finally { + try { + if (_c && !_c.done && (_a = _b.return)) _a.call(_b); + } + finally { if (e_4) throw e_4.error; } + } +} +function initialize() { + var e_5, _a, e_6, _b; + if (initialized) { + (0, funcs_1.crash)("Already initialized commands."); + } + try { + for (var _c = __values(Object.entries(exports.allConsoleCommands)), _d = _c.next(); !_d.done; _d = _c.next()) { + var _e = __read(_d.value, 2), key = _e[0], command_1 = _e[1]; + if (command_1.init) + command_1.data = command_1.init(); + } + } + catch (e_5_1) { e_5 = { error: e_5_1 }; } + finally { + try { + if (_d && !_d.done && (_a = _c.return)) _a.call(_c); + } + finally { if (e_5) throw e_5.error; } + } + try { + for (var _f = __values(Object.entries(exports.allCommands)), _g = _f.next(); !_g.done; _g = _f.next()) { + var _h = __read(_g.value, 2), key = _h[0], command_2 = _h[1]; + if (command_2.init) + command_2.data = command_2.init(); + } + } + catch (e_6_1) { e_6 = { error: e_6_1 }; } + finally { + try { + if (_g && !_g.done && (_b = _f.return)) _b.call(_f); + } + finally { if (e_6) throw e_6.error; } + } + initialized = true; +} +function reset() { + var e_7, _a, e_8, _b; + initialized = false; + try { + for (var _c = __values(Object.entries(exports.allConsoleCommands)), _d = _c.next(); !_d.done; _d = _c.next()) { + var _e = __read(_d.value, 2), key = _e[0], command_3 = _e[1]; + if (command_3.init) + command_3.data = undefined; + } + } + catch (e_7_1) { e_7 = { error: e_7_1 }; } + finally { + try { + if (_d && !_d.done && (_a = _c.return)) _a.call(_c); + } + finally { if (e_7) throw e_7.error; } + } + try { + for (var _f = __values(Object.entries(exports.allCommands)), _g = _f.next(); !_g.done; _g = _f.next()) { + var _h = __read(_g.value, 2), key = _h[0], command_4 = _h[1]; + if (command_4.init) + command_4.data = undefined; + } + } + catch (e_8_1) { e_8 = { error: e_8_1 }; } + finally { + try { + if (_g && !_g.done && (_b = _f.return)) _b.call(_f); + } + finally { if (e_8) throw e_8.error; } + } +} diff --git a/build/scripts/frameworks/commands/errors.js b/build/scripts/frameworks/commands/errors.js index a7fa23f2..ba915110 100644 --- a/build/scripts/frameworks/commands/errors.js +++ b/build/scripts/frameworks/commands/errors.js @@ -1,20 +1,20 @@ -"use strict"; -/* -Copyright © BalaM314, 2026. All Rights Reserved. -This file contains the error handling framework. -For usage information, see docs/framework-usage-guide.md -For maintenance information, see docs/frameworks.md -*/ -//Behold, the power of typescript! -Object.defineProperty(exports, "__esModule", { value: true }); -exports.CommandError = void 0; -exports.fail = fail; -exports.CommandError = (function () { }); -Object.setPrototypeOf(exports.CommandError.prototype, Error.prototype); -function fail(message) { - var err = new Error(typeof message == "string" ? message : ""); - //oh no it's even worse now because i have to smuggle a function through here - err.data = message; - Object.setPrototypeOf(err, exports.CommandError.prototype); - throw err; -} +"use strict"; +/* +Copyright © BalaM314, 2026. All Rights Reserved. +This file contains the error handling framework. +For usage information, see docs/framework-usage-guide.md +For maintenance information, see docs/frameworks.md +*/ +//Behold, the power of typescript! +Object.defineProperty(exports, "__esModule", { value: true }); +exports.CommandError = void 0; +exports.fail = fail; +exports.CommandError = (function () { }); +Object.setPrototypeOf(exports.CommandError.prototype, Error.prototype); +function fail(message) { + var err = new Error(typeof message == "string" ? message : ""); + //oh no it's even worse now because i have to smuggle a function through here + err.data = message; + Object.setPrototypeOf(err, exports.CommandError.prototype); + throw err; +} diff --git a/build/scripts/frameworks/commands/formatting.js b/build/scripts/frameworks/commands/formatting.js index ecd73206..08e169af 100644 --- a/build/scripts/frameworks/commands/formatting.js +++ b/build/scripts/frameworks/commands/formatting.js @@ -1,183 +1,183 @@ -"use strict"; -var __read = (this && this.__read) || function (o, n) { - var m = typeof Symbol === "function" && o[Symbol.iterator]; - if (!m) return o; - var i = m.call(o), r, ar = [], e; - try { - while ((n === void 0 || n-- > 0) && !(r = i.next()).done) ar.push(r.value); - } - catch (error) { e = { error: error }; } - finally { - try { - if (r && !r.done && (m = i["return"])) m.call(i); - } - finally { if (e) throw e.error; } - } - return ar; -}; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.f_server = exports.f_client = exports.processedFFunctions = exports.fFunctions = exports.outputFormatter_client = exports.outputFormatter_server = void 0; -var funcs_1 = require("/funcs"); -var globals_1 = require("/globals"); -var players_1 = require("/players"); -var ranks_1 = require("/ranks"); -exports.outputFormatter_server = (0, funcs_1.tagProcessorPartial)(function (chunk) { - if (chunk instanceof players_1.FishPlayer) { - return "&c(".concat((0, funcs_1.escapeStringColorsServer)(chunk.cleanedName), ")&fr"); - } - else if (chunk instanceof ranks_1.Rank) { - return "&p".concat(chunk.name, "&fr"); - } - else if (chunk instanceof ranks_1.RoleFlag) { - return "&p".concat(chunk.name, "&fr"); - } - else if (chunk instanceof Error) { - return "&r".concat((0, funcs_1.escapeStringColorsServer)(chunk.toString()), "&fr"); - } - else if (chunk instanceof Player) { - var player = chunk; //not sure why this is necessary, typescript randomly converts any to unknown - return "&cPlayer#".concat(player.id, " (").concat((0, funcs_1.escapeStringColorsServer)(Strings.stripColors(player.name)), ")&fr"); - } - else if (typeof chunk == "string") { - if (globals_1.uuidPattern.test(chunk)) { - return "&b".concat(chunk, "&fr"); - } - else if (globals_1.ipPattern.test(chunk)) { - return "&b".concat(chunk, "&fr"); - } - else { - return "".concat(chunk); - } - } - else if (typeof chunk == "boolean") { - return "&b".concat(chunk.toString(), "&fr"); - } - else if (typeof chunk == "number") { - return "&b".concat(chunk.toString(), "&fr"); - } - else if (chunk instanceof Administration.PlayerInfo) { - return "&c".concat((0, funcs_1.escapeStringColorsServer)(chunk.plainLastName()), "&fr"); - } - else if (chunk instanceof UnitType) { - return "&c".concat(chunk.localizedName, "&fr"); - } - else if (chunk instanceof Block) { - return "&c".concat(chunk.localizedName, "&fr"); - } - else if (chunk instanceof Team) { - return "&c".concat(chunk.name, "&fr"); - } - else if (chunk instanceof Item) { - return "&c".concat((0, funcs_1.capitalizeText)(chunk.name, "-"), "&fr"); - } - else { - chunk; - Log.err("Invalid format object!"); - Log.info(chunk); - return chunk; //let it get stringified by the JS engine - } -}); -exports.outputFormatter_client = (0, funcs_1.tagProcessorPartial)(function (chunk, i, data, stringChunks) { - var _a, _b; - var reset = (_b = data !== null && data !== void 0 ? data : (_a = stringChunks[0].match(/^\[.+?\]/)) === null || _a === void 0 ? void 0 : _a[0]) !== null && _b !== void 0 ? _b : ""; - if (chunk instanceof players_1.FishPlayer) { - return "[cyan](".concat(chunk.name, "[cyan])") + reset; - } - else if (chunk instanceof ranks_1.Rank) { - return "".concat(chunk.color).concat(chunk.name, "[]") + reset; - } - else if (chunk instanceof ranks_1.RoleFlag) { - return "".concat(chunk.color).concat(chunk.name, "[]") + reset; - } - else if (chunk instanceof Error) { - return "[red]".concat(chunk.toString()) + reset; - } - else if (chunk instanceof Player) { - var fishP = players_1.FishPlayer.get(chunk); - return "[cyan](".concat(fishP.name, "[cyan])") + reset; - } - else if (typeof chunk == "string") { - if (globals_1.uuidPattern.test(chunk)) { - return "[blue]".concat(chunk, "[]"); - } - else if (globals_1.ipPattern.test(chunk)) { - return "[blue]".concat(chunk, "[]"); - } - else { - //TODO reset color? - return chunk; - } - } - else if (typeof chunk == "boolean") { - return "[blue]".concat(chunk.toString(), "[]"); - } - else if (typeof chunk == "number") { - return "[blue]".concat(chunk.toString(), "[]"); - } - else if (chunk instanceof Administration.PlayerInfo) { - return chunk.lastName + reset; - } - else if (chunk instanceof UnitType) { - return "[cyan]".concat(chunk.localizedName, "[]"); - } - else if (chunk instanceof Block) { - return "[cyan]".concat(chunk.localizedName, "[]"); - } - else if (chunk instanceof Team) { - return "[white]".concat(chunk.coloredName(), "[][]"); - } - else if (chunk instanceof Item) { - return "[cyan]".concat((0, funcs_1.capitalizeText)(chunk.name, "-"), "[]"); - } - else { - chunk; - Log.err("Invalid format object!"); - Log.info(chunk); - return chunk; //allow it to get stringified by the engine - } -}); -exports.fFunctions = { - boolGood: function (value) { - return [ - value ? "[green]true[]" : "[red]false[]", - value ? "&lgtrue&fr" : "&lrfalse&fr", - ]; - }, - boolBad: function (value) { - return [ - value ? "[red]true[]" : "[green]false[]", - value ? "&lrtrue&fr" : "&lgfalse&fr", - ]; - }, - percent: function (value, decimals) { - if (decimals === void 0) { decimals = 0; } - if (isNaN(value) || !isFinite(value)) - return ["[gray]N/A[]", "N/A"]; - var percent = (value * 100).toFixed(decimals) + "%"; - return ["".concat(percent), "".concat(percent)]; - }, - number: function (value, decimals) { - if (decimals === void 0) { decimals = null; } - if (isNaN(value) || !isFinite(value)) - return ["[gray]N/A[]", "N/A"]; - if (decimals !== null) - return [value.toFixed(decimals), value.toFixed(decimals)]; - return [value.toString(), value.toString()]; - } -}; -exports.processedFFunctions = [0, 1].map(function (i) { - return Object.fromEntries(Object.entries(exports.fFunctions).map(function (_a) { - var _b = __read(_a, 2), k = _b[0], v = _b[1]; - return [k, - function () { - var args = []; - for (var _i = 0; _i < arguments.length; _i++) { - args[_i] = arguments[_i]; - } - return v.apply(exports.processedFFunctions[i], args)[i]; - } - ]; - })); -}); -exports.f_client = Object.assign(exports.outputFormatter_client, exports.processedFFunctions[0]); -exports.f_server = Object.assign(exports.outputFormatter_server, exports.processedFFunctions[1]); +"use strict"; +var __read = (this && this.__read) || function (o, n) { + var m = typeof Symbol === "function" && o[Symbol.iterator]; + if (!m) return o; + var i = m.call(o), r, ar = [], e; + try { + while ((n === void 0 || n-- > 0) && !(r = i.next()).done) ar.push(r.value); + } + catch (error) { e = { error: error }; } + finally { + try { + if (r && !r.done && (m = i["return"])) m.call(i); + } + finally { if (e) throw e.error; } + } + return ar; +}; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.f_server = exports.f_client = exports.processedFFunctions = exports.fFunctions = exports.outputFormatter_client = exports.outputFormatter_server = void 0; +var funcs_1 = require("/funcs"); +var globals_1 = require("/globals"); +var players_1 = require("/players"); +var ranks_1 = require("/ranks"); +exports.outputFormatter_server = (0, funcs_1.tagProcessorPartial)(function (chunk) { + if (chunk instanceof players_1.FishPlayer) { + return "&c(".concat((0, funcs_1.escapeStringColorsServer)(chunk.cleanedName), ")&fr"); + } + else if (chunk instanceof ranks_1.Rank) { + return "&p".concat(chunk.name, "&fr"); + } + else if (chunk instanceof ranks_1.RoleFlag) { + return "&p".concat(chunk.name, "&fr"); + } + else if (chunk instanceof Error) { + return "&r".concat((0, funcs_1.escapeStringColorsServer)(chunk.toString()), "&fr"); + } + else if (chunk instanceof Player) { + var player = chunk; //not sure why this is necessary, typescript randomly converts any to unknown + return "&cPlayer#".concat(player.id, " (").concat((0, funcs_1.escapeStringColorsServer)(Strings.stripColors(player.name)), ")&fr"); + } + else if (typeof chunk == "string") { + if (globals_1.uuidPattern.test(chunk)) { + return "&b".concat(chunk, "&fr"); + } + else if (globals_1.ipPattern.test(chunk)) { + return "&b".concat(chunk, "&fr"); + } + else { + return "".concat(chunk); + } + } + else if (typeof chunk == "boolean") { + return "&b".concat(chunk.toString(), "&fr"); + } + else if (typeof chunk == "number") { + return "&b".concat(chunk.toString(), "&fr"); + } + else if (chunk instanceof Administration.PlayerInfo) { + return "&c".concat((0, funcs_1.escapeStringColorsServer)(chunk.plainLastName()), "&fr"); + } + else if (chunk instanceof UnitType) { + return "&c".concat(chunk.localizedName, "&fr"); + } + else if (chunk instanceof Block) { + return "&c".concat(chunk.localizedName, "&fr"); + } + else if (chunk instanceof Team) { + return "&c".concat(chunk.name, "&fr"); + } + else if (chunk instanceof Item) { + return "&c".concat((0, funcs_1.capitalizeText)(chunk.name, "-"), "&fr"); + } + else { + chunk; + Log.err("Invalid format object!"); + Log.info(chunk); + return chunk; //let it get stringified by the JS engine + } +}); +exports.outputFormatter_client = (0, funcs_1.tagProcessorPartial)(function (chunk, i, data, stringChunks) { + var _a, _b; + var reset = (_b = data !== null && data !== void 0 ? data : (_a = stringChunks[0].match(/^\[.+?\]/)) === null || _a === void 0 ? void 0 : _a[0]) !== null && _b !== void 0 ? _b : ""; + if (chunk instanceof players_1.FishPlayer) { + return "[cyan](".concat(chunk.name, "[cyan])") + reset; + } + else if (chunk instanceof ranks_1.Rank) { + return "".concat(chunk.color).concat(chunk.name, "[]") + reset; + } + else if (chunk instanceof ranks_1.RoleFlag) { + return "".concat(chunk.color).concat(chunk.name, "[]") + reset; + } + else if (chunk instanceof Error) { + return "[red]".concat(chunk.toString()) + reset; + } + else if (chunk instanceof Player) { + var fishP = players_1.FishPlayer.get(chunk); + return "[cyan](".concat(fishP.name, "[cyan])") + reset; + } + else if (typeof chunk == "string") { + if (globals_1.uuidPattern.test(chunk)) { + return "[blue]".concat(chunk, "[]"); + } + else if (globals_1.ipPattern.test(chunk)) { + return "[blue]".concat(chunk, "[]"); + } + else { + //TODO reset color? + return chunk; + } + } + else if (typeof chunk == "boolean") { + return "[blue]".concat(chunk.toString(), "[]"); + } + else if (typeof chunk == "number") { + return "[blue]".concat(chunk.toString(), "[]"); + } + else if (chunk instanceof Administration.PlayerInfo) { + return chunk.lastName + reset; + } + else if (chunk instanceof UnitType) { + return "[cyan]".concat(chunk.localizedName, "[]"); + } + else if (chunk instanceof Block) { + return "[cyan]".concat(chunk.localizedName, "[]"); + } + else if (chunk instanceof Team) { + return "[white]".concat(chunk.coloredName(), "[][]"); + } + else if (chunk instanceof Item) { + return "[cyan]".concat((0, funcs_1.capitalizeText)(chunk.name, "-"), "[]"); + } + else { + chunk; + Log.err("Invalid format object!"); + Log.info(chunk); + return chunk; //allow it to get stringified by the engine + } +}); +exports.fFunctions = { + boolGood: function (value) { + return [ + value ? "[green]true[]" : "[red]false[]", + value ? "&lgtrue&fr" : "&lrfalse&fr", + ]; + }, + boolBad: function (value) { + return [ + value ? "[red]true[]" : "[green]false[]", + value ? "&lrtrue&fr" : "&lgfalse&fr", + ]; + }, + percent: function (value, decimals) { + if (decimals === void 0) { decimals = 0; } + if (isNaN(value) || !isFinite(value)) + return ["[gray]N/A[]", "N/A"]; + var percent = (value * 100).toFixed(decimals) + "%"; + return ["".concat(percent), "".concat(percent)]; + }, + number: function (value, decimals) { + if (decimals === void 0) { decimals = null; } + if (isNaN(value) || !isFinite(value)) + return ["[gray]N/A[]", "N/A"]; + if (decimals !== null) + return [value.toFixed(decimals), value.toFixed(decimals)]; + return [value.toString(), value.toString()]; + } +}; +exports.processedFFunctions = [0, 1].map(function (i) { + return Object.fromEntries(Object.entries(exports.fFunctions).map(function (_a) { + var _b = __read(_a, 2), k = _b[0], v = _b[1]; + return [k, + function () { + var args = []; + for (var _i = 0; _i < arguments.length; _i++) { + args[_i] = arguments[_i]; + } + return v.apply(exports.processedFFunctions[i], args)[i]; + } + ]; + })); +}); +exports.f_client = Object.assign(exports.outputFormatter_client, exports.processedFFunctions[0]); +exports.f_server = Object.assign(exports.outputFormatter_server, exports.processedFFunctions[1]); diff --git a/build/scripts/frameworks/commands/perm.js b/build/scripts/frameworks/commands/perm.js index 33ef8dca..04b73ef1 100644 --- a/build/scripts/frameworks/commands/perm.js +++ b/build/scripts/frameworks/commands/perm.js @@ -1,95 +1,95 @@ -"use strict"; -var __makeTemplateObject = (this && this.__makeTemplateObject) || function (cooked, raw) { - if (Object.defineProperty) { Object.defineProperty(cooked, "raw", { value: raw }); } else { cooked.raw = raw; } - return cooked; -}; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.Perm = void 0; -/* -Copyright © BalaM314, 2026. All Rights Reserved. -This file contains the Perm class. -*/ -var config_1 = require("/config"); -var funcs_1 = require("/funcs"); -var ranks_1 = require("/ranks"); -/** Represents a permission that is required to do something. */ -var Perm = /** @class */ (function () { - function Perm(name, check, color, unauthorizedMessage) { - if (color === void 0) { color = ""; } - if (unauthorizedMessage === void 0) { unauthorizedMessage = "You do not have the required permission (".concat(name, ") to execute this command"); } - this.name = name; - this.color = color; - this.unauthorizedMessage = unauthorizedMessage; - if (typeof check == "string") { - if (ranks_1.Rank.getByName(check) == null) - (0, funcs_1.crash)("Invalid perm ".concat(name, ": invalid rank name ").concat(check)); - this.check = function (fishP) { return fishP.ranksAtLeast(check); }; - } - else { - this.check = check; - } - Perm.perms[name] = this; - } - /** Creates a new Perm with overrides for specified gamemodes. */ - Perm.prototype.exceptModes = function (modes, unauthorizedMessage) { - var _this = this; - if (unauthorizedMessage === void 0) { unauthorizedMessage = this.unauthorizedMessage; } - return new Perm(this.name, function (fishP) { - var _a; - var effectivePerm = (_a = modes[config_1.Gamemode.name()]) !== null && _a !== void 0 ? _a : _this; - return effectivePerm.check(fishP); - }, this.color, unauthorizedMessage); - }; - Perm.fromRank = function (rank) { - return new Perm(rank.name, function (fishP) { return fishP.ranksAtLeast(rank); }, rank.color); - }; - Perm.getByName = function (name) { - var _a; - return (_a = Perm.perms[name]) !== null && _a !== void 0 ? _a : (0, funcs_1.crash)("Invalid requiredPerm"); - }; - Perm.perms = {}; - Perm.none = new Perm("none", function (fishP) { return true; }, "[sky]"); - Perm.trusted = Perm.fromRank(ranks_1.Rank.trusted); - Perm.mod = Perm.fromRank(ranks_1.Rank.mod); - Perm.admin = Perm.fromRank(ranks_1.Rank.admin); - Perm.member = new Perm("member", function (fishP) { return fishP.hasFlag("member"); }, "[pink]", "You must have a ".concat(config_1.FColor.member(templateObject_1 || (templateObject_1 = __makeTemplateObject(["Fish Membership"], ["Fish Membership"]))), " to use this command. Get a Fish Membership at[sky] ").concat(config_1.text.membershipURL, " []")); - Perm.chat = new Perm("chat", function (fishP) { return (!fishP.muted && !fishP.autoflagged) || fishP.ranksAtLeast("mod"); }); - Perm.bypassChatFilter = new Perm("bypassChatFilter", "admin"); - Perm.seeMutedMessages = new Perm("seeMutedMessages", function (fishP) { return fishP.muted || fishP.autoflagged || fishP.ranksAtLeast("mod"); }); - Perm.play = new Perm("play", function (fishP) { return !fishP.stelled() || fishP.ranksAtLeast("mod"); }); - Perm.seeErrorMessages = new Perm("seeErrorMessages", "admin"); - Perm.viewUUIDs = new Perm("viewUUIDs", "admin"); - Perm.viewIPs = new Perm("viewIPs", "admin"); - Perm.blockTrolling = new Perm("blockTrolling", function (fishP) { return fishP.rank === ranks_1.Rank.pi; }); - Perm.visualEffects = new Perm("visualEffects", function (fishP) { return (!fishP.stelled() || fishP.ranksAtLeast("mod")) && !fishP.hasFlag("no_effects"); }); - Perm.bulkVisualEffects = new Perm("bulkVisualEffects", function (fishP) { return ((fishP.hasFlag("developer") || fishP.hasFlag("illusionist") || fishP.hasFlag("member")) && !fishP.stelled()) - || fishP.ranksAtLeast("mod"); }); - Perm.bypassVoteFreeze = new Perm("bypassVoteFreeze", "trusted"); - Perm.bypassVotekick = new Perm("bypassVotekick", "mod"); - Perm.warn = new Perm("warn", "mod"); - Perm.vanish = new Perm("vanish", "mod"); - Perm.changeTeam = new Perm("changeTeam", "admin").exceptModes({ - sandbox: Perm.trusted, - attack: Perm.admin, - hexed: Perm.mod, - pvp: Perm.trusted, - minigame: Perm.trusted, - testsrv: Perm.trusted, - }); - /** Whether players should be allowed to change the team of a unit or building. If not, they will be kicked out of their current unit or building before switching teams. */ - Perm.changeTeamExternal = new Perm("changeTeamExternal", "admin").exceptModes({ - sandbox: Perm.trusted, - }); - Perm.usidCheck = new Perm("usidCheck", "trusted"); - Perm.runJS = new Perm("runJS", "manager"); - Perm.bypassNameCheck = new Perm("bypassNameCheck", "fish"); - Perm.hardcore = new Perm("hardcore", "trusted"); - Perm.massKill = new Perm("massKill", "admin").exceptModes({ - sandbox: Perm.mod, - }); - Perm.voteOtherTeams = new Perm("voteOtherTeams", "trusted"); - Perm.immediatelyVotekickNewPlayers = new Perm("immediatelyVotekickNewPlayers", "trusted"); - return Perm; -}()); -exports.Perm = Perm; -var templateObject_1; +"use strict"; +var __makeTemplateObject = (this && this.__makeTemplateObject) || function (cooked, raw) { + if (Object.defineProperty) { Object.defineProperty(cooked, "raw", { value: raw }); } else { cooked.raw = raw; } + return cooked; +}; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.Perm = void 0; +/* +Copyright © BalaM314, 2026. All Rights Reserved. +This file contains the Perm class. +*/ +var config_1 = require("/config"); +var funcs_1 = require("/funcs"); +var ranks_1 = require("/ranks"); +/** Represents a permission that is required to do something. */ +var Perm = /** @class */ (function () { + function Perm(name, check, color, unauthorizedMessage) { + if (color === void 0) { color = ""; } + if (unauthorizedMessage === void 0) { unauthorizedMessage = "You do not have the required permission (".concat(name, ") to execute this command"); } + this.name = name; + this.color = color; + this.unauthorizedMessage = unauthorizedMessage; + if (typeof check == "string") { + if (ranks_1.Rank.getByName(check) == null) + (0, funcs_1.crash)("Invalid perm ".concat(name, ": invalid rank name ").concat(check)); + this.check = function (fishP) { return fishP.ranksAtLeast(check); }; + } + else { + this.check = check; + } + Perm.perms[name] = this; + } + /** Creates a new Perm with overrides for specified gamemodes. */ + Perm.prototype.exceptModes = function (modes, unauthorizedMessage) { + var _this = this; + if (unauthorizedMessage === void 0) { unauthorizedMessage = this.unauthorizedMessage; } + return new Perm(this.name, function (fishP) { + var _a; + var effectivePerm = (_a = modes[config_1.Gamemode.name()]) !== null && _a !== void 0 ? _a : _this; + return effectivePerm.check(fishP); + }, this.color, unauthorizedMessage); + }; + Perm.fromRank = function (rank) { + return new Perm(rank.name, function (fishP) { return fishP.ranksAtLeast(rank); }, rank.color); + }; + Perm.getByName = function (name) { + var _a; + return (_a = Perm.perms[name]) !== null && _a !== void 0 ? _a : (0, funcs_1.crash)("Invalid requiredPerm"); + }; + Perm.perms = {}; + Perm.none = new Perm("none", function (fishP) { return true; }, "[sky]"); + Perm.trusted = Perm.fromRank(ranks_1.Rank.trusted); + Perm.mod = Perm.fromRank(ranks_1.Rank.mod); + Perm.admin = Perm.fromRank(ranks_1.Rank.admin); + Perm.member = new Perm("member", function (fishP) { return fishP.hasFlag("member"); }, "[pink]", "You must have a ".concat(config_1.FColor.member(templateObject_1 || (templateObject_1 = __makeTemplateObject(["Fish Membership"], ["Fish Membership"]))), " to use this command. Get a Fish Membership at[sky] ").concat(config_1.text.membershipURL, " []")); + Perm.chat = new Perm("chat", function (fishP) { return (!fishP.muted && !fishP.autoflagged) || fishP.ranksAtLeast("mod"); }); + Perm.bypassChatFilter = new Perm("bypassChatFilter", "admin"); + Perm.seeMutedMessages = new Perm("seeMutedMessages", function (fishP) { return fishP.muted || fishP.autoflagged || fishP.ranksAtLeast("mod"); }); + Perm.play = new Perm("play", function (fishP) { return !fishP.stelled() || fishP.ranksAtLeast("mod"); }); + Perm.seeErrorMessages = new Perm("seeErrorMessages", "admin"); + Perm.viewUUIDs = new Perm("viewUUIDs", "admin"); + Perm.viewIPs = new Perm("viewIPs", "admin"); + Perm.blockTrolling = new Perm("blockTrolling", function (fishP) { return fishP.rank === ranks_1.Rank.pi; }); + Perm.visualEffects = new Perm("visualEffects", function (fishP) { return (!fishP.stelled() || fishP.ranksAtLeast("mod")) && !fishP.hasFlag("no_effects"); }); + Perm.bulkVisualEffects = new Perm("bulkVisualEffects", function (fishP) { return ((fishP.hasFlag("developer") || fishP.hasFlag("illusionist") || fishP.hasFlag("member")) && !fishP.stelled()) + || fishP.ranksAtLeast("mod"); }); + Perm.bypassVoteFreeze = new Perm("bypassVoteFreeze", "trusted"); + Perm.bypassVotekick = new Perm("bypassVotekick", "mod"); + Perm.warn = new Perm("warn", "mod"); + Perm.vanish = new Perm("vanish", "mod"); + Perm.changeTeam = new Perm("changeTeam", "admin").exceptModes({ + sandbox: Perm.trusted, + attack: Perm.admin, + hexed: Perm.mod, + pvp: Perm.trusted, + minigame: Perm.trusted, + testsrv: Perm.trusted, + }); + /** Whether players should be allowed to change the team of a unit or building. If not, they will be kicked out of their current unit or building before switching teams. */ + Perm.changeTeamExternal = new Perm("changeTeamExternal", "admin").exceptModes({ + sandbox: Perm.trusted, + }); + Perm.usidCheck = new Perm("usidCheck", "trusted"); + Perm.runJS = new Perm("runJS", "manager"); + Perm.bypassNameCheck = new Perm("bypassNameCheck", "fish"); + Perm.hardcore = new Perm("hardcore", "trusted"); + Perm.massKill = new Perm("massKill", "admin").exceptModes({ + sandbox: Perm.mod, + }); + Perm.voteOtherTeams = new Perm("voteOtherTeams", "trusted"); + Perm.immediatelyVotekickNewPlayers = new Perm("immediatelyVotekickNewPlayers", "trusted"); + return Perm; +}()); +exports.Perm = Perm; +var templateObject_1; diff --git a/build/scripts/frameworks/commands/requirements.js b/build/scripts/frameworks/commands/requirements.js index 6ecb4aab..444ca5d8 100644 --- a/build/scripts/frameworks/commands/requirements.js +++ b/build/scripts/frameworks/commands/requirements.js @@ -1,92 +1,92 @@ -"use strict"; -/* -Copyright © BalaM314, 2026. All Rights Reserved. -This file contains the requirements system, which is part of the commands framework. -*/ -Object.defineProperty(exports, "__esModule", { value: true }); -exports.Req = void 0; -var config_1 = require("/config"); -var errors_1 = require("/frameworks/commands/errors"); -var utils_1 = require("/utils"); -exports.Req = { - mode: function () { - var modes = []; - for (var _i = 0; _i < arguments.length; _i++) { - modes[_i] = arguments[_i]; - } - return function () { - return modes.map(function (mode) { return config_1.Gamemode[mode](); }).some(Boolean) - || (0, errors_1.fail)("This command is only available in ".concat(modes.map(utils_1.formatModeName).join(" or "))); - }; - }, - modeNot: function (mode) { return function () { - return !config_1.Gamemode[mode]() - || (0, errors_1.fail)("This command is disabled in ".concat((0, utils_1.formatModeName)(mode))); - }; }, - moderate: function (argName, allowSameRank, minimumLevel, allowSelfIfUnauthorized) { - if (allowSameRank === void 0) { allowSameRank = false; } - if (minimumLevel === void 0) { minimumLevel = "mod"; } - if (allowSelfIfUnauthorized === void 0) { allowSelfIfUnauthorized = false; } - return function (_a) { - var args = _a.args, sender = _a.sender; - return (args[argName] == undefined || sender.canModerate(args[argName], !allowSameRank, minimumLevel, allowSelfIfUnauthorized) - || (0, errors_1.fail)("You do not have permission to perform moderation actions on this player.")); - }; - }, - cooldown: function (durationMS) { return function (_a) { - var lastUsedSuccessfullySender = _a.lastUsedSuccessfullySender; - return Date.now() - lastUsedSuccessfullySender >= durationMS - || (0, errors_1.fail)("This command was run recently and is on cooldown."); - }; }, - cooldownGlobal: function (durationMS) { return function (_a) { - var lastUsedSuccessfully = _a.lastUsedSuccessfully; - return Date.now() - lastUsedSuccessfully >= durationMS - || (0, errors_1.fail)("This command was run recently and is on cooldown."); - }; }, - gameRunning: function () { - return !Vars.state.gameOver - || (0, errors_1.fail)("This game is over, please wait for the next map to load."); - }, - teamAlive: function (_a) { - var sender = _a.sender; - return sender.team().isAlive() - || (0, errors_1.fail)(Math.random() > 0.9 ? "You are already dead." : "Your team is dead."); - }, - unitExists: function (message) { - if (message === void 0) { message = "You must be in a unit to use this command."; } - return function (_a) { - var _b; - var sender = _a.sender; - return (sender.connected() && ((_b = sender.unit()) === null || _b === void 0 ? void 0 : _b.added) && !sender.unit().dead) - || (0, errors_1.fail)(message); - }; - }, - numberRange: function (argName, min, max) { - return function (_a) { - var args = _a.args; - return args[argName] == undefined || min <= args[argName] && args[argName] <= max - || (0, errors_1.fail)("".concat(argName, " must be between ").concat(min, " and ").concat(max)); - }; - }, - integer: function (argName) { - return function (_a) { - var args = _a.args; - return args[argName] == undefined || Number.isSafeInteger(args[argName]) - || (0, errors_1.fail)("".concat(argName, " must be an integer")); - }; - }, - integerRange: function (argName, min, max) { - return function (_a) { - var args = _a.args; - return exports.Req.integer(argName)({ args: args }) && exports.Req.numberRange(argName, min, max)({ args: args }); - }; - }, - positiveInteger: function (argName) { - return function (_a) { - var args = _a.args; - return exports.Req.integer(argName)({ args: args }) && - (args[argName] == undefined || args[argName] > 0 - || (0, errors_1.fail)("".concat(argName, " must be positive"))); - }; - }, -}; +"use strict"; +/* +Copyright © BalaM314, 2026. All Rights Reserved. +This file contains the requirements system, which is part of the commands framework. +*/ +Object.defineProperty(exports, "__esModule", { value: true }); +exports.Req = void 0; +var config_1 = require("/config"); +var errors_1 = require("/frameworks/commands/errors"); +var utils_1 = require("/utils"); +exports.Req = { + mode: function () { + var modes = []; + for (var _i = 0; _i < arguments.length; _i++) { + modes[_i] = arguments[_i]; + } + return function () { + return modes.map(function (mode) { return config_1.Gamemode[mode](); }).some(Boolean) + || (0, errors_1.fail)("This command is only available in ".concat(modes.map(utils_1.formatModeName).join(" or "))); + }; + }, + modeNot: function (mode) { return function () { + return !config_1.Gamemode[mode]() + || (0, errors_1.fail)("This command is disabled in ".concat((0, utils_1.formatModeName)(mode))); + }; }, + moderate: function (argName, allowSameRank, minimumLevel, allowSelfIfUnauthorized) { + if (allowSameRank === void 0) { allowSameRank = false; } + if (minimumLevel === void 0) { minimumLevel = "mod"; } + if (allowSelfIfUnauthorized === void 0) { allowSelfIfUnauthorized = false; } + return function (_a) { + var args = _a.args, sender = _a.sender; + return (args[argName] == undefined || sender.canModerate(args[argName], !allowSameRank, minimumLevel, allowSelfIfUnauthorized) + || (0, errors_1.fail)("You do not have permission to perform moderation actions on this player.")); + }; + }, + cooldown: function (durationMS) { return function (_a) { + var lastUsedSuccessfullySender = _a.lastUsedSuccessfullySender; + return Date.now() - lastUsedSuccessfullySender >= durationMS + || (0, errors_1.fail)("This command was run recently and is on cooldown."); + }; }, + cooldownGlobal: function (durationMS) { return function (_a) { + var lastUsedSuccessfully = _a.lastUsedSuccessfully; + return Date.now() - lastUsedSuccessfully >= durationMS + || (0, errors_1.fail)("This command was run recently and is on cooldown."); + }; }, + gameRunning: function () { + return !Vars.state.gameOver + || (0, errors_1.fail)("This game is over, please wait for the next map to load."); + }, + teamAlive: function (_a) { + var sender = _a.sender; + return sender.team().isAlive() + || (0, errors_1.fail)(Math.random() > 0.9 ? "You are already dead." : "Your team is dead."); + }, + unitExists: function (message) { + if (message === void 0) { message = "You must be in a unit to use this command."; } + return function (_a) { + var _b; + var sender = _a.sender; + return (sender.connected() && ((_b = sender.unit()) === null || _b === void 0 ? void 0 : _b.added) && !sender.unit().dead) + || (0, errors_1.fail)(message); + }; + }, + numberRange: function (argName, min, max) { + return function (_a) { + var args = _a.args; + return args[argName] == undefined || min <= args[argName] && args[argName] <= max + || (0, errors_1.fail)("".concat(argName, " must be between ").concat(min, " and ").concat(max)); + }; + }, + integer: function (argName) { + return function (_a) { + var args = _a.args; + return args[argName] == undefined || Number.isSafeInteger(args[argName]) + || (0, errors_1.fail)("".concat(argName, " must be an integer")); + }; + }, + integerRange: function (argName, min, max) { + return function (_a) { + var args = _a.args; + return exports.Req.integer(argName)({ args: args }) && exports.Req.numberRange(argName, min, max)({ args: args }); + }; + }, + positiveInteger: function (argName) { + return function (_a) { + var args = _a.args; + return exports.Req.integer(argName)({ args: args }) && + (args[argName] == undefined || args[argName] > 0 + || (0, errors_1.fail)("".concat(argName, " must be positive"))); + }; + }, +}; diff --git a/build/scripts/frameworks/commands/types.js b/build/scripts/frameworks/commands/types.js index d5e540cc..6f4b15c0 100644 --- a/build/scripts/frameworks/commands/types.js +++ b/build/scripts/frameworks/commands/types.js @@ -1,29 +1,29 @@ -"use strict"; -/* -Copyright © BalaM314, 2026. All Rights Reserved. -This file contains types for the commands framework. -*/ -Object.defineProperty(exports, "__esModule", { value: true }); -exports.commandArgNames = exports.commandArgTypes = void 0; -/** All valid command arg types. */ -exports.commandArgTypes = [ - "string", "number", "boolean", "player", /*"menuPlayer",*/ "team", "time", "unittype", "block", - "uuid", "offlinePlayer", "map", "mapOrRandom", "rank", "roleflag", "item" -]; -exports.commandArgNames = { - string: "text", - number: "number", - boolean: "boolean", - player: "player", - team: "team", - time: "duration", - unittype: "unit type", - block: "block", - uuid: "UUID", - offlinePlayer: "player", - map: "map", - mapOrRandom: "map", - rank: "rank", - roleflag: "role flag", - item: "item" -}; +"use strict"; +/* +Copyright © BalaM314, 2026. All Rights Reserved. +This file contains types for the commands framework. +*/ +Object.defineProperty(exports, "__esModule", { value: true }); +exports.commandArgNames = exports.commandArgTypes = void 0; +/** All valid command arg types. */ +exports.commandArgTypes = [ + "string", "number", "boolean", "player", /*"menuPlayer",*/ "team", "time", "unittype", "block", + "uuid", "offlinePlayer", "map", "mapOrRandom", "rank", "roleflag", "item" +]; +exports.commandArgNames = { + string: "text", + number: "number", + boolean: "boolean", + player: "player", + team: "team", + time: "duration", + unittype: "unit type", + block: "block", + uuid: "UUID", + offlinePlayer: "player", + map: "map", + mapOrRandom: "map", + rank: "rank", + roleflag: "role flag", + item: "item" +}; diff --git a/build/scripts/frameworks/io.js b/build/scripts/frameworks/io.js index f4ad2059..05f31124 100644 --- a/build/scripts/frameworks/io.js +++ b/build/scripts/frameworks/io.js @@ -1,388 +1,388 @@ -"use strict"; -var __extends = (this && this.__extends) || (function () { - var extendStatics = function (d, b) { - extendStatics = Object.setPrototypeOf || - ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || - function (d, b) { for (var p in b) if (Object.prototype.hasOwnProperty.call(b, p)) d[p] = b[p]; }; - return extendStatics(d, b); - }; - return function (d, b) { - if (typeof b !== "function" && b !== null) - throw new TypeError("Class extends value " + String(b) + " is not a constructor or null"); - extendStatics(d, b); - function __() { this.constructor = d; } - d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); - }; -})(); -var __values = (this && this.__values) || function(o) { - var s = typeof Symbol === "function" && Symbol.iterator, m = s && o[s], i = 0; - if (m) return m.call(o); - if (o && typeof o.length === "number") return { - next: function () { - if (o && i >= o.length) o = void 0; - return { value: o && o[i++], done: !o }; - } - }; - throw new TypeError(s ? "Object is not iterable." : "Symbol.iterator is not defined."); -}; -var __read = (this && this.__read) || function (o, n) { - var m = typeof Symbol === "function" && o[Symbol.iterator]; - if (!m) return o; - var i = m.call(o), r, ar = [], e; - try { - while ((n === void 0 || n-- > 0) && !(r = i.next()).done) ar.push(r.value); - } - catch (error) { e = { error: error }; } - finally { - try { - if (r && !r.done && (m = i["return"])) m.call(i); - } - finally { if (e) throw e.error; } - } - return ar; -}; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.SettingsSerializer = exports.Serializer = exports.DataClass = void 0; -exports.dataClass = dataClass; -exports.serialize = serialize; -/* -Copyright © BalaM314, 2026. All Rights Reserved. -This file contains the serialization framework. -For usage information, see docs/framework-usage-guide.md -For maintenance information, see docs/frameworks.md -*/ -var funcs_1 = require("/funcs"); -var globals_1 = require("/globals"); -var DataClass = /** @class */ (function () { - function DataClass(data) { - Object.assign(this, data); - } - return DataClass; -}()); -exports.DataClass = DataClass; -function dataClass() { - return DataClass; -} -function checkBounds(type, value, min, max) { - if (value < min) { - Log.warn("Integer underflow when serializing ".concat(type, ": value ").concat(value, " was less than ").concat(min)); - return min; - } - if (value >= max) { - Log.warn("Integer overflow when serializing ".concat(type, ": value ").concat(value, " was greater than ").concat(max)); - return max; - } - return value; -} -var Serializer = /** @class */ (function () { - function Serializer(schema, oldSchema) { - this.schema = schema; - this.oldSchema = oldSchema; - } - Serializer.prototype.write = function (object, output) { - Serializer.writeNode(this.schema, object, output); - }; - Serializer.prototype.read = function (input) { - input.mark(0xFFFF); //1MB - try { - return Serializer.readNode(this.schema, input); - } - catch (err) { - Log.warn("Using fallback schema: this message should go away after a restart"); - input.reset(); - if (this.oldSchema) - return Serializer.readNode(this.oldSchema, input); - else - throw err; - } - }; - Serializer.writeNode = function (schema, value, output) { - var e_1, _a, e_2, _b; - var checkNumbers = false; - switch (schema[0]) { - case 'string': - output.writeUTF(value); - break; - case 'number': - if (checkNumbers) { - switch (schema[1]) { - case 'u8': - output.writeByte(checkBounds(schema[1], value, 0, Math.pow(2, 8))); - break; - case 'u16': - output.writeShort(checkBounds(schema[1], value, 0, Math.pow(2, 16))); - break; - case 'u32': - output.writeInt(checkBounds(schema[1], value, 0, Math.pow(2, 32)) & 0xFFFFFFFF); - break; - case 'i8': - output.writeByte(checkBounds(schema[1], value, -(Math.pow(2, 7)), (Math.pow(2, 7)))); - break; - case 'i16': - output.writeShort(checkBounds(schema[1], value, -(Math.pow(2, 15)), (Math.pow(2, 15)))); - break; - case 'i32': - output.writeInt(checkBounds(schema[1], value, -(Math.pow(2, 31)), (Math.pow(2, 31)))); - break; - case 'i64': - output.writeLong(checkBounds(schema[1], value, -(Math.pow(2, 63)), (Math.pow(2, 63)))); - break; - case 'f32': - output.writeFloat(isNaN(value) || !isFinite(value) ? (Log.warn('Attempted to write a NaN floating-point value, defaulting to 0'), 0) : value); - break; - case 'f64': - output.writeDouble(isNaN(value) || !isFinite(value) ? (Log.warn('Attempted to write a NaN floating-point value, defaulting to 0'), 0) : value); - break; - } - } - else { - switch (schema[1]) { - case 'u8': - output.writeByte(value); - break; - case 'u16': - output.writeShort(value); - break; - case 'u32': - output.writeInt(value & 0xFFFFFFFF); - break; //If the value is greater than 0x7FFFFFFF, make it negative so Java writes it correctly - case 'i8': - output.writeByte(value); - break; - case 'i16': - output.writeShort(value); - break; - case 'i32': - output.writeInt(value); - break; - case 'i64': - output.writeLong(value); - break; - case 'f32': - output.writeFloat(value); - break; - case 'f64': - output.writeDouble(value); - break; - } - } - break; - case 'boolean': - output.writeBoolean(value); - break; - case 'team': - if (!value) - Log.err("attempting to serialize a Team, but it was null"); //temporary debug message - output.writeByte(value.id); - break; - case 'object': - try { - for (var _c = __values(schema[1]), _d = _c.next(); !_d.done; _d = _c.next()) { - var _e = __read(_d.value, 2), key = _e[0], childSchema = _e[1]; - //correspondence - this.writeNode(childSchema, value[key], output); - } - } - catch (e_1_1) { e_1 = { error: e_1_1 }; } - finally { - try { - if (_d && !_d.done && (_a = _c.return)) _a.call(_c); - } - finally { if (e_1) throw e_1.error; } - } - break; - case 'class': - try { - for (var _f = __values(schema[2]), _g = _f.next(); !_g.done; _g = _f.next()) { - var _h = __read(_g.value, 2), key = _h[0], childSchema = _h[1]; - //correspondence - this.writeNode(childSchema, value[key], output); - } - } - catch (e_2_1) { e_2 = { error: e_2_1 }; } - finally { - try { - if (_g && !_g.done && (_b = _f.return)) _b.call(_f); - } - finally { if (e_2) throw e_2.error; } - } - break; - case 'array': - if (typeof schema[1] == "string") { - this.writeNode(["number", schema[1]], value.length, output); - } - else { - if (schema[1] !== value.length) { - Log.err('SERIALIZATION WARNING: received invalid data: array with greater length than specified by schema'); - value.length = schema[1]; - } - } - // eslint-disable-next-line @typescript-eslint/prefer-for-of - for (var i = 0; i < value.length; i++) { - this.writeNode(schema[2], value[i], output); - } - break; - case 'version': - output.writeByte(schema[1]); - this.writeNode(schema[2], value, output); - break; - } - }; - Serializer.readNode = function (schema, input) { - var e_3, _a, e_4, _b; - switch (schema[0]) { - case 'string': - return input.readUTF(); - case 'number': - switch (schema[1]) { - case 'u8': return input.readUnsignedByte(); - case 'u16': return input.readUnsignedShort(); - case 'u32': { - var value = input.readInt(); //Java does not support unsigned ints - return value < 0 ? value + Math.pow(2, 32) : value; - } - case 'i8': return input.readByte(); - case 'i16': return input.readShort(); - case 'i32': return input.readInt(); - case 'i64': return input.readLong(); - case 'f32': return input.readFloat(); - case 'f64': return input.readDouble(); - } - schema[1]; - break; - case 'boolean': - return input.readBoolean(); - case 'team': - return Team.all[input.readByte()]; - case 'object': { - var output = {}; - try { - for (var _c = __values(schema[1]), _d = _c.next(); !_d.done; _d = _c.next()) { - var _e = __read(_d.value, 2), key = _e[0], childSchema = _e[1]; - output[key] = this.readNode(childSchema, input); - } - } - catch (e_3_1) { e_3 = { error: e_3_1 }; } - finally { - try { - if (_d && !_d.done && (_a = _c.return)) _a.call(_c); - } - finally { if (e_3) throw e_3.error; } - } - return output; - } - case 'class': { - var classData = {}; - try { - for (var _f = __values(schema[2]), _g = _f.next(); !_g.done; _g = _f.next()) { - var _h = __read(_g.value, 2), key = _h[0], childSchema = _h[1]; - classData[key] = this.readNode(childSchema, input); - } - } - catch (e_4_1) { e_4 = { error: e_4_1 }; } - finally { - try { - if (_g && !_g.done && (_b = _f.return)) _b.call(_f); - } - finally { if (e_4) throw e_4.error; } - } - return new schema[1](classData); - } - case 'array': { - var length = typeof schema[1] === "number" ? - schema[1] - : this.readNode(["number", schema[1]], input); - var array = new Array(length); - for (var i = 0; i < length; i++) { - array[i] = this.readNode(schema[2], input); - } - return array; - } - case 'version': { - var version = input.readByte(); - if (version !== schema[1]) - (0, funcs_1.crash)("Expected version ".concat(schema[1], ", but read ").concat(version)); - return this.readNode(schema[2], input); - } - } - }; - return Serializer; -}()); -exports.Serializer = Serializer; -var SettingsSerializer = /** @class */ (function (_super) { - __extends(SettingsSerializer, _super); - function SettingsSerializer(settingsKey, schema, oldSchema) { - var _this = _super.call(this, schema, oldSchema) || this; - _this.settingsKey = settingsKey; - _this.schema = schema; - _this.oldSchema = oldSchema; - return _this; - } - SettingsSerializer.prototype.writeSettings = function (object) { - var output = new ByteArrayOutputStream(); - this.write(object, new DataOutputStream(output)); - Core.settings.put(this.settingsKey, output.toByteArray()); - }; - SettingsSerializer.prototype.readSettings = function () { - var data = Core.settings.getBytes(this.settingsKey); - if (data) - return this.read(new DataInputStream(new ByteArrayInputStream(data))); - else - return null; - }; - return SettingsSerializer; -}(Serializer)); -exports.SettingsSerializer = SettingsSerializer; -if (!Symbol.metadata) - Object.defineProperty(Symbol, "metadata", { - writable: false, - enumerable: false, - configurable: false, - value: Symbol("Symbol.metadata") - }); -var valuesToSerialize = new AtomicInteger(); -function serialize(settingsKey, schema, oldSchema, fixer, saveEvent) { - if (saveEvent === void 0) { saveEvent = "saveData"; } - return function decorate(_, _a) { - var addInitializer = _a.addInitializer, access = _a.access, name = _a.name; - valuesToSerialize.getAndIncrement(); - addInitializer(function () { - var _this = this; - var serializer = (0, funcs_1.lazy)(function () { - return new SettingsSerializer(settingsKey, schema(), oldSchema === null || oldSchema === void 0 ? void 0 : oldSchema()); - }); - globals_1.FishEvents.on("loadData", function () { return Threads.daemon(function () { - //Load data multithreaded - var start = Time.nanos(); - var value = serializer().readSettings(); - if (value) { - if (fixer) - value = fixer(value); - access.set(_this, value); - } - if (valuesToSerialize.decrementAndGet() == 0) - globals_1.FishEvents.fire("dataLoaded", []); - Log.debug("serialize read @ @", settingsKey, (Time.nanos() - start) / 1e6); - }); }); - globals_1.FishEvents.on(saveEvent, function () { - try { - Time.mark(); - var value = access.get(_this); - if (value == null) - return; - serializer().writeSettings(value); - Log.debug("serialize save @ @", settingsKey, Time.elapsed()); - } - catch (err) { - Log.err("Error while saving field ".concat(String(name), " on ").concat(String(_this === null || _this === void 0 ? void 0 : _this.name), " using settings key ").concat(settingsKey)); - Log.info(JSON.stringify(access.get(_this))); - throw err; - } - }); - }); - }; -} -globals_1.FishEvents.on("loadData", function () { - if (valuesToSerialize.get() == 0) - globals_1.FishEvents.fire("dataLoaded", []); -}); +"use strict"; +var __extends = (this && this.__extends) || (function () { + var extendStatics = function (d, b) { + extendStatics = Object.setPrototypeOf || + ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || + function (d, b) { for (var p in b) if (Object.prototype.hasOwnProperty.call(b, p)) d[p] = b[p]; }; + return extendStatics(d, b); + }; + return function (d, b) { + if (typeof b !== "function" && b !== null) + throw new TypeError("Class extends value " + String(b) + " is not a constructor or null"); + extendStatics(d, b); + function __() { this.constructor = d; } + d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); + }; +})(); +var __values = (this && this.__values) || function(o) { + var s = typeof Symbol === "function" && Symbol.iterator, m = s && o[s], i = 0; + if (m) return m.call(o); + if (o && typeof o.length === "number") return { + next: function () { + if (o && i >= o.length) o = void 0; + return { value: o && o[i++], done: !o }; + } + }; + throw new TypeError(s ? "Object is not iterable." : "Symbol.iterator is not defined."); +}; +var __read = (this && this.__read) || function (o, n) { + var m = typeof Symbol === "function" && o[Symbol.iterator]; + if (!m) return o; + var i = m.call(o), r, ar = [], e; + try { + while ((n === void 0 || n-- > 0) && !(r = i.next()).done) ar.push(r.value); + } + catch (error) { e = { error: error }; } + finally { + try { + if (r && !r.done && (m = i["return"])) m.call(i); + } + finally { if (e) throw e.error; } + } + return ar; +}; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.SettingsSerializer = exports.Serializer = exports.DataClass = void 0; +exports.dataClass = dataClass; +exports.serialize = serialize; +/* +Copyright © BalaM314, 2026. All Rights Reserved. +This file contains the serialization framework. +For usage information, see docs/framework-usage-guide.md +For maintenance information, see docs/frameworks.md +*/ +var funcs_1 = require("/funcs"); +var globals_1 = require("/globals"); +var DataClass = /** @class */ (function () { + function DataClass(data) { + Object.assign(this, data); + } + return DataClass; +}()); +exports.DataClass = DataClass; +function dataClass() { + return DataClass; +} +function checkBounds(type, value, min, max) { + if (value < min) { + Log.warn("Integer underflow when serializing ".concat(type, ": value ").concat(value, " was less than ").concat(min)); + return min; + } + if (value >= max) { + Log.warn("Integer overflow when serializing ".concat(type, ": value ").concat(value, " was greater than ").concat(max)); + return max; + } + return value; +} +var Serializer = /** @class */ (function () { + function Serializer(schema, oldSchema) { + this.schema = schema; + this.oldSchema = oldSchema; + } + Serializer.prototype.write = function (object, output) { + Serializer.writeNode(this.schema, object, output); + }; + Serializer.prototype.read = function (input) { + input.mark(0xFFFF); //1MB + try { + return Serializer.readNode(this.schema, input); + } + catch (err) { + Log.warn("Using fallback schema: this message should go away after a restart"); + input.reset(); + if (this.oldSchema) + return Serializer.readNode(this.oldSchema, input); + else + throw err; + } + }; + Serializer.writeNode = function (schema, value, output) { + var e_1, _a, e_2, _b; + var checkNumbers = false; + switch (schema[0]) { + case 'string': + output.writeUTF(value); + break; + case 'number': + if (checkNumbers) { + switch (schema[1]) { + case 'u8': + output.writeByte(checkBounds(schema[1], value, 0, Math.pow(2, 8))); + break; + case 'u16': + output.writeShort(checkBounds(schema[1], value, 0, Math.pow(2, 16))); + break; + case 'u32': + output.writeInt(checkBounds(schema[1], value, 0, Math.pow(2, 32)) & 0xFFFFFFFF); + break; + case 'i8': + output.writeByte(checkBounds(schema[1], value, -(Math.pow(2, 7)), (Math.pow(2, 7)))); + break; + case 'i16': + output.writeShort(checkBounds(schema[1], value, -(Math.pow(2, 15)), (Math.pow(2, 15)))); + break; + case 'i32': + output.writeInt(checkBounds(schema[1], value, -(Math.pow(2, 31)), (Math.pow(2, 31)))); + break; + case 'i64': + output.writeLong(checkBounds(schema[1], value, -(Math.pow(2, 63)), (Math.pow(2, 63)))); + break; + case 'f32': + output.writeFloat(isNaN(value) || !isFinite(value) ? (Log.warn('Attempted to write a NaN floating-point value, defaulting to 0'), 0) : value); + break; + case 'f64': + output.writeDouble(isNaN(value) || !isFinite(value) ? (Log.warn('Attempted to write a NaN floating-point value, defaulting to 0'), 0) : value); + break; + } + } + else { + switch (schema[1]) { + case 'u8': + output.writeByte(value); + break; + case 'u16': + output.writeShort(value); + break; + case 'u32': + output.writeInt(value & 0xFFFFFFFF); + break; //If the value is greater than 0x7FFFFFFF, make it negative so Java writes it correctly + case 'i8': + output.writeByte(value); + break; + case 'i16': + output.writeShort(value); + break; + case 'i32': + output.writeInt(value); + break; + case 'i64': + output.writeLong(value); + break; + case 'f32': + output.writeFloat(value); + break; + case 'f64': + output.writeDouble(value); + break; + } + } + break; + case 'boolean': + output.writeBoolean(value); + break; + case 'team': + if (!value) + Log.err("attempting to serialize a Team, but it was null"); //temporary debug message + output.writeByte(value.id); + break; + case 'object': + try { + for (var _c = __values(schema[1]), _d = _c.next(); !_d.done; _d = _c.next()) { + var _e = __read(_d.value, 2), key = _e[0], childSchema = _e[1]; + //correspondence + this.writeNode(childSchema, value[key], output); + } + } + catch (e_1_1) { e_1 = { error: e_1_1 }; } + finally { + try { + if (_d && !_d.done && (_a = _c.return)) _a.call(_c); + } + finally { if (e_1) throw e_1.error; } + } + break; + case 'class': + try { + for (var _f = __values(schema[2]), _g = _f.next(); !_g.done; _g = _f.next()) { + var _h = __read(_g.value, 2), key = _h[0], childSchema = _h[1]; + //correspondence + this.writeNode(childSchema, value[key], output); + } + } + catch (e_2_1) { e_2 = { error: e_2_1 }; } + finally { + try { + if (_g && !_g.done && (_b = _f.return)) _b.call(_f); + } + finally { if (e_2) throw e_2.error; } + } + break; + case 'array': + if (typeof schema[1] == "string") { + this.writeNode(["number", schema[1]], value.length, output); + } + else { + if (schema[1] !== value.length) { + Log.err('SERIALIZATION WARNING: received invalid data: array with greater length than specified by schema'); + value.length = schema[1]; + } + } + // eslint-disable-next-line @typescript-eslint/prefer-for-of + for (var i = 0; i < value.length; i++) { + this.writeNode(schema[2], value[i], output); + } + break; + case 'version': + output.writeByte(schema[1]); + this.writeNode(schema[2], value, output); + break; + } + }; + Serializer.readNode = function (schema, input) { + var e_3, _a, e_4, _b; + switch (schema[0]) { + case 'string': + return input.readUTF(); + case 'number': + switch (schema[1]) { + case 'u8': return input.readUnsignedByte(); + case 'u16': return input.readUnsignedShort(); + case 'u32': { + var value = input.readInt(); //Java does not support unsigned ints + return value < 0 ? value + Math.pow(2, 32) : value; + } + case 'i8': return input.readByte(); + case 'i16': return input.readShort(); + case 'i32': return input.readInt(); + case 'i64': return input.readLong(); + case 'f32': return input.readFloat(); + case 'f64': return input.readDouble(); + } + schema[1]; + break; + case 'boolean': + return input.readBoolean(); + case 'team': + return Team.all[input.readByte()]; + case 'object': { + var output = {}; + try { + for (var _c = __values(schema[1]), _d = _c.next(); !_d.done; _d = _c.next()) { + var _e = __read(_d.value, 2), key = _e[0], childSchema = _e[1]; + output[key] = this.readNode(childSchema, input); + } + } + catch (e_3_1) { e_3 = { error: e_3_1 }; } + finally { + try { + if (_d && !_d.done && (_a = _c.return)) _a.call(_c); + } + finally { if (e_3) throw e_3.error; } + } + return output; + } + case 'class': { + var classData = {}; + try { + for (var _f = __values(schema[2]), _g = _f.next(); !_g.done; _g = _f.next()) { + var _h = __read(_g.value, 2), key = _h[0], childSchema = _h[1]; + classData[key] = this.readNode(childSchema, input); + } + } + catch (e_4_1) { e_4 = { error: e_4_1 }; } + finally { + try { + if (_g && !_g.done && (_b = _f.return)) _b.call(_f); + } + finally { if (e_4) throw e_4.error; } + } + return new schema[1](classData); + } + case 'array': { + var length = typeof schema[1] === "number" ? + schema[1] + : this.readNode(["number", schema[1]], input); + var array = new Array(length); + for (var i = 0; i < length; i++) { + array[i] = this.readNode(schema[2], input); + } + return array; + } + case 'version': { + var version = input.readByte(); + if (version !== schema[1]) + (0, funcs_1.crash)("Expected version ".concat(schema[1], ", but read ").concat(version)); + return this.readNode(schema[2], input); + } + } + }; + return Serializer; +}()); +exports.Serializer = Serializer; +var SettingsSerializer = /** @class */ (function (_super) { + __extends(SettingsSerializer, _super); + function SettingsSerializer(settingsKey, schema, oldSchema) { + var _this = _super.call(this, schema, oldSchema) || this; + _this.settingsKey = settingsKey; + _this.schema = schema; + _this.oldSchema = oldSchema; + return _this; + } + SettingsSerializer.prototype.writeSettings = function (object) { + var output = new ByteArrayOutputStream(); + this.write(object, new DataOutputStream(output)); + Core.settings.put(this.settingsKey, output.toByteArray()); + }; + SettingsSerializer.prototype.readSettings = function () { + var data = Core.settings.getBytes(this.settingsKey); + if (data) + return this.read(new DataInputStream(new ByteArrayInputStream(data))); + else + return null; + }; + return SettingsSerializer; +}(Serializer)); +exports.SettingsSerializer = SettingsSerializer; +if (!Symbol.metadata) + Object.defineProperty(Symbol, "metadata", { + writable: false, + enumerable: false, + configurable: false, + value: Symbol("Symbol.metadata") + }); +var valuesToSerialize = new AtomicInteger(); +function serialize(settingsKey, schema, oldSchema, fixer, saveEvent) { + if (saveEvent === void 0) { saveEvent = "saveData"; } + return function decorate(_, _a) { + var addInitializer = _a.addInitializer, access = _a.access, name = _a.name; + valuesToSerialize.getAndIncrement(); + addInitializer(function () { + var _this = this; + var serializer = (0, funcs_1.lazy)(function () { + return new SettingsSerializer(settingsKey, schema(), oldSchema === null || oldSchema === void 0 ? void 0 : oldSchema()); + }); + globals_1.FishEvents.on("loadData", function () { return Threads.daemon(function () { + //Load data multithreaded + var start = Time.nanos(); + var value = serializer().readSettings(); + if (value) { + if (fixer) + value = fixer(value); + access.set(_this, value); + } + if (valuesToSerialize.decrementAndGet() == 0) + globals_1.FishEvents.fire("dataLoaded", []); + Log.debug("serialize read @ @", settingsKey, (Time.nanos() - start) / 1e6); + }); }); + globals_1.FishEvents.on(saveEvent, function () { + try { + Time.mark(); + var value = access.get(_this); + if (value == null) + return; + serializer().writeSettings(value); + Log.debug("serialize save @ @", settingsKey, Time.elapsed()); + } + catch (err) { + Log.err("Error while saving field ".concat(String(name), " on ").concat(String(_this === null || _this === void 0 ? void 0 : _this.name), " using settings key ").concat(settingsKey)); + Log.info(JSON.stringify(access.get(_this))); + throw err; + } + }); + }); + }; +} +globals_1.FishEvents.on("loadData", function () { + if (valuesToSerialize.get() == 0) + globals_1.FishEvents.fire("dataLoaded", []); +}); diff --git a/build/scripts/frameworks/menus.js b/build/scripts/frameworks/menus.js index 8af7338f..1b403cb3 100644 --- a/build/scripts/frameworks/menus.js +++ b/build/scripts/frameworks/menus.js @@ -1,382 +1,382 @@ -"use strict"; -/* eslint-disable @typescript-eslint/array-type */ -/* -Copyright © BalaM314, 2026. All Rights Reserved. -This file contains the menu framework. -For usage information, see docs/framework-usage-guide.md -For maintenance information, see docs/frameworks.md -*/ -var __assign = (this && this.__assign) || function () { - __assign = Object.assign || function(t) { - for (var s, i = 1, n = arguments.length; i < n; i++) { - s = arguments[i]; - for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p)) - t[p] = s[p]; - } - return t; - }; - return __assign.apply(this, arguments); -}; -var __rest = (this && this.__rest) || function (s, e) { - var t = {}; - for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0) - t[p] = s[p]; - if (s != null && typeof Object.getOwnPropertySymbols === "function") - for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) { - if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i])) - t[p[i]] = s[p[i]]; - } - return t; -}; -var __values = (this && this.__values) || function(o) { - var s = typeof Symbol === "function" && Symbol.iterator, m = s && o[s], i = 0; - if (m) return m.call(o); - if (o && typeof o.length === "number") return { - next: function () { - if (o && i >= o.length) o = void 0; - return { value: o && o[i++], done: !o }; - } - }; - throw new TypeError(s ? "Object is not iterable." : "Symbol.iterator is not defined."); -}; -var __read = (this && this.__read) || function (o, n) { - var m = typeof Symbol === "function" && o[Symbol.iterator]; - if (!m) return o; - var i = m.call(o), r, ar = [], e; - try { - while ((n === void 0 || n-- > 0) && !(r = i.next()).done) ar.push(r.value); - } - catch (error) { e = { error: error }; } - finally { - try { - if (r && !r.done && (m = i["return"])) m.call(i); - } - finally { if (e) throw e.error; } - } - return ar; -}; -var __spreadArray = (this && this.__spreadArray) || function (to, from, pack) { - if (pack || arguments.length === 2) for (var i = 0, l = from.length, ar; i < l; i++) { - if (ar || !(i in from)) { - if (!ar) ar = Array.prototype.slice.call(from, 0, i); - ar[i] = from[i]; - } - } - return to.concat(ar || Array.prototype.slice.call(from)); -}; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.listeners = exports.Menu = exports.Cancel = void 0; -exports.registerListeners = registerListeners; -var commands_1 = require("/frameworks/commands"); -var funcs_1 = require("/funcs"); -var players_1 = require("/players"); -var promise_1 = require("/promise"); -var utils_1 = require("/utils"); -/** Used to change the behavior of adding another menu when being run in a menu callback. */ -var isInMenuCallback = false; -/** Stores a mapping from name to the numeric id of a listener that has been registered. */ -var registeredListeners = {}; -exports.listeners = registeredListeners; -/** Stores all listeners in use by fish-commands. */ -var listeners = { - generic: function (player, option) { - var fishSender = players_1.FishPlayer.get(player); - var prevCallback = fishSender.activeMenus.shift(); - if (!prevCallback) - return; //No menu to process, do nothing - isInMenuCallback = true; - prevCallback.callback(option); - isInMenuCallback = false; - }, - none: function (player, option) { - //do nothing - } -}; -exports.Cancel = Symbol("Cancel"); -/** Registers all listeners, should be called on server load. */ -function registerListeners() { - var e_1, _a; - var _b; - try { - for (var _c = __values(Object.entries(listeners)), _d = _c.next(); !_d.done; _d = _c.next()) { - var _e = __read(_d.value, 2), key = _e[0], listener = _e[1]; - (_b = registeredListeners[key]) !== null && _b !== void 0 ? _b : (registeredListeners[key] = Menus.registerMenu(listener)); - } - } - catch (e_1_1) { e_1 = { error: e_1_1 }; } - finally { - try { - if (_d && !_d.done && (_a = _c.return)) _a.call(_c); - } - finally { if (e_1) throw e_1.error; } - } -} -exports.Menu = { - /** Displays a menu to a player, returning a Promise. */ - raw: function (title, description, arrangedOptions, target, _a) { - var _b = _a === void 0 ? {} : _a, _c = _b.optionStringifier, optionStringifier = _c === void 0 ? String : _c, _d = _b.onCancel, onCancel = _d === void 0 ? "reject" : _d, _e = _b.cancelOptionId, cancelOptionId = _e === void 0 ? -1 : _e; - var _f = promise_1.Promise.withResolvers(), promise = _f.promise, reject = _f.reject, resolve = _f.resolve; - //The target fishPlayer has a property called activeMenu, which stores information about the last menu triggered. - //If menu() is being called from a menu calback, add it to the front of the queue so it is processed before any other menus. - //Otherwise, two multi-step menus queued together would alternate, which would confuse the player. - target.activeMenus[isInMenuCallback ? "unshift" : "push"]({ callback: function (option) { - //Additional permission validation could be done here, but the only way that callback() can be called is if the above statement executed, - //and on sensitive menus such as the stop menu, the only way to reach that is if menu() was called by the /stop command, - //which already checks permissions. - //Additionally, the callback is cleared by the generic menu listener after it is executed. - try { - var options = arrangedOptions.flat(); - //We do need to validate option though, as it can be any number. - if (option === -1 || option === cancelOptionId || !(option in options)) { - //Consider any invalid option to be a cancellation - if (onCancel == "null") - resolve(null); - else if (onCancel == "reject") - reject(exports.Cancel); - else - return; - } - else { - resolve(options[option]); - } - } - catch (err) { - (0, utils_1.handleError)(err, target, utils_1.outputFail, "".concat(target.cleanedName, " submitted menu \"").concat(title, "\" \"").concat(description, "\"")); - } - } }); - var i = 0; - var stringifiedOptions = arrangedOptions.map(function (r) { return r.map(function (item) { - if (i === cancelOptionId) - return item; - i++; - return optionStringifier(item); - }); }); - Call.menu(target.con, registeredListeners.generic, title, description, stringifiedOptions); - return promise; - }, - /** Displays a menu to a player, returning a Promise. Arranges provided options into a 2D array, and can add a Cancel option. */ - menu: function (title, description, options, target, _a) { - var _b = _a === void 0 ? {} : _a, _c = _b.includeCancel, includeCancel = _c === void 0 ? false : _c, _d = _b.optionStringifier, optionStringifier = _d === void 0 ? String : _d, _e = _b.columns, columns = _e === void 0 ? 3 : _e, _f = _b.onCancel, onCancel = _f === void 0 ? "reject" : _f, _g = _b.cancelOptionId, cancelOptionId = _g === void 0 ? -1 : _g; - //Set up the 2D array of options, and maybe add cancel - //Call.menu() with [[]] will cause a client crash, make sure to pass [] instead - var arrangedOptions = (options.length == 0 && !includeCancel) ? [] : (0, funcs_1.to2DArray)(options, columns); - if (includeCancel) { - arrangedOptions.push(["[red]Cancel[]"]); - //This is safe because cancelOptionId is set, - //so the handler will never get called with "Cancel". - cancelOptionId = options.length; - } - return exports.Menu.raw(title, description, arrangedOptions, target, { - cancelOptionId: cancelOptionId, - onCancel: onCancel, - optionStringifier: optionStringifier - }); - }, - /** Rejects with a CommandError if the user chooses to cancel. */ - confirm: function (target, description, _a) { - var _b = _a === void 0 ? {} : _a, _c = _b.cancelOutput, cancelOutput = _c === void 0 ? "Cancelled." : _c, _d = _b.title, title = _d === void 0 ? "Confirm" : _d, _e = _b.confirmText, confirmText = _e === void 0 ? "[green]Confirm" : _e, _f = _b.cancelText, cancelText = _f === void 0 ? "[red]Cancel" : _f; - return exports.Menu.menu(title, description, [confirmText, cancelText], target, { onCancel: "reject", cancelOptionId: 1 }).catch(function (e) { - if (e === exports.Cancel) - (0, commands_1.fail)(cancelOutput); - throw e; //some random error, rethrow it - }); - }, - /** Same as confirm(), but with inverted colors, for potentially dangerous actions. */ - confirmDangerous: function (target, description, _a) { - if (_a === void 0) { _a = {}; } - var _b = _a.confirmText, confirmText = _b === void 0 ? "[red]Confirm" : _b, _c = _a.cancelText, cancelText = _c === void 0 ? "[green]Cancel" : _c, rest = __rest(_a, ["confirmText", "cancelText"]); - return exports.Menu.confirm(target, description, __assign({ cancelText: cancelText, confirmText: confirmText }, rest)); - }, - /** - * Displays a menu to a player, returning a Promise. - * Accepts pre-generated data and text. Alternative to optionStringifier if the text is already generated. - */ - buttons: function (target, title, description, options, cfg) { - if (cfg === void 0) { cfg = {}; } - return exports.Menu.raw(title, description, options, target, __assign(__assign({}, cfg), { optionStringifier: function (o) { return o.text; } })).then(function (o) { return o === null || o === void 0 ? void 0 : o.data; }); - }, - /** - * Displays a menu to a player, returning a Promise. - * Adds left and right arrows to switch pages. - * Shows different options based on the page. - */ - pages: function (target, title, description, options, cfg) { - var _a = promise_1.Promise.withResolvers(), promise = _a.promise, reject = _a.reject, resolve = _a.resolve; - function showPage(index) { - var opts = __spreadArray(__spreadArray([], __read(options[index].map(function (r) { return r.map(function (d) { return ({ text: d.text, data: [d.data] }); }); })), false), [ - [ - { data: "left", text: "[".concat(index == 0 ? "gray" : "accent", "]<--") }, - { data: "numbers", text: "[accent]".concat(index + 1, "/").concat(options.length) }, - { data: "right", text: "[".concat(index == options.length - 1 ? "gray" : "accent", "]-->") } - ] - ], false); - void exports.Menu.buttons(target, title, description, opts, __assign(__assign({}, cfg), { onCancel: "null" })).then(function (response) { - if (response instanceof Array) - resolve(response[0]); - else if (response === "right") - showPage(Math.min(index + 1, options.length - 1)); - else if (response === "left") - showPage(Math.max(index - 1, 0)); - else { - //Treat numbers as cancel - if (cfg.onCancel == "null") - resolve(null); - else if (cfg.onCancel == "reject") - reject(exports.Cancel); - //otherwise, just let the promise hang - } - }); - } - showPage(0); - return promise; - }, - /** - * Displays a menu to a player, returning a Promise. - * Adds left and right arrows to switch pages. - * Shows different text based on the current page. - */ - textPages: function (target, pages, options, cfg) { - if (options === void 0) { options = []; } - if (cfg === void 0) { cfg = {}; } - var _a = promise_1.Promise.withResolvers(), promise = _a.promise, reject = _a.reject, resolve = _a.resolve; - var pageSkipSize = Math.max(Math.floor(pages.length / 8), 5); - function showPage(index) { - var opts = __spreadArray(__spreadArray([ - [ - { data: ["left", pageSkipSize], text: "[".concat(index == 0 ? "gray" : "accent", "]<<<") }, - { data: ["left", 1], text: "[".concat(index == 0 ? "gray" : "accent", "]<--") }, - { data: ["right", 1], text: "[".concat(index == pages.length - 1 ? "gray" : "accent", "]-->") }, - { data: ["right", pageSkipSize], text: "[".concat(index == pages.length - 1 ? "gray" : "accent", "]>>>") }, - ], [ - { data: ["numbers"], text: "[accent]Page ".concat(index + 1, "/").concat(pages.length) }, - ] - ], __read(options.map(function (d, i) { return [{ data: [i], text: d }]; })), false), [ - [ - { data: ["cancel"], text: "[lightgray]Close" }, - ], - ], false); - void exports.Menu.buttons(target, pages[index][0], pages[index][1](), opts, __assign(__assign({}, cfg), { onCancel: "null" })).then(function (response) { - if ((response === null || response === void 0 ? void 0 : response[0]) === "right") - showPage(Math.min(index + response[1], pages.length - 1)); - else if ((response === null || response === void 0 ? void 0 : response[0]) === "left") - showPage(Math.max(index - response[1], 0)); - else if (typeof (response === null || response === void 0 ? void 0 : response[0]) === "number") { - resolve([index, options[response[0]]]); - } - else { - //Treat numbers as cancel - if (cfg.onCancel == "null") - resolve(null); - else if (cfg.onCancel == "reject") - reject(exports.Cancel); - //otherwise, just let the promise hang - } - }); - } - var index = (function () { - if (cfg.startPage == undefined) - return 0; - if (typeof cfg.startPage === 'number') { - if (cfg.startPage < 0) - return 0; - return cfg.startPage; - } - var index = pages.findIndex(function (_a) { - var _b = __read(_a, 1), title = _b[0]; - return title === cfg.startPage; - }); - if (index === -1) - return 0; - return index; - })(); - showPage(index); - return promise; - }, - /** - * Displays a menu to a player, returning a Promise. - * Accepts a 2D array of options and shows a region of that 2D grid. - * Adds arrows to scroll left/right/up/down. - * Resolves to the selected option. - */ - scroll2D: function (target, title, description, options, cfg) { - var _a, _b; - if (cfg === void 0) { cfg = {}; } - var _c = promise_1.Promise.withResolvers(), promise = _c.promise, reject = _c.reject, resolve = _c.resolve; - var _d = cfg.rows, rows = _d === void 0 ? 5 : _d, _e = cfg.columns, cols = _e === void 0 ? 5 : _e; - var height = options.length; - var width = options[0].length; - function showPage(x, y) { - var _a, _b; - var opts = __spreadArray(__spreadArray([], __read(options.slice(y, y + rows).map(function (r) { return r.concat(Array(width - r.length).fill({ data: "blank", text: "" })); }).map(function (r) { - return r.slice(x, x + cols).map(function (d) { return ({ text: d.text, data: [d.data] }); }); - })), false), [ - [ - { data: "blank", text: "" }, - ], - [ - { data: "blank", text: "" }, - { data: "up", text: "[".concat(y == 0 ? "gray" : "accent", "]").concat(String.fromCharCode(Iconc.up)) }, - { data: "blank", text: "" }, - ], [ - { data: "left", text: "[".concat(x == 0 ? "gray" : "accent", "]").concat(String.fromCharCode(Iconc.left)) }, - { data: "blank", text: (_b = (_a = cfg.getCenterText) === null || _a === void 0 ? void 0 : _a.call(cfg, x, y)) !== null && _b !== void 0 ? _b : '' }, - { data: "right", text: "[".concat(x == width - cols ? "gray" : "accent", "]").concat(String.fromCharCode(Iconc.right)) }, - ], [ - { data: "blank", text: "" }, - { data: "down", text: "[".concat(y == height - rows ? "gray" : "accent", "]").concat(String.fromCharCode(Iconc.down)) }, - { data: "blank", text: "" }, - ] - ], false); - void exports.Menu.buttons(target, title, description, opts, cfg).then(function (response) { - if (response instanceof Array) - resolve([response[0], x, y]); - else if (response === "right") - showPage(Math.min(x + 1, width - cols), y); - else if (response === "left") - showPage(Math.max(x - 1, 0), y); - else if (response === "up") - showPage(x, Math.max(y - 1, 0)); - else if (response === "down") - showPage(x, Math.min(y + 1, height - rows)); - else { - //Treat numbers as cancel - if (cfg.onCancel == "null") - resolve(null); - else if (cfg.onCancel == "reject") - reject(exports.Cancel); - //otherwise, just let the promise hang - } - }); - } - showPage(Math.min((_a = cfg.x) !== null && _a !== void 0 ? _a : 0, width - cols), Math.min((_b = cfg.y) !== null && _b !== void 0 ? _b : 0, height - rows)); - return promise; - }, - /** - * Displays a menu to a player, returning a Promise. - * Adds left and right arrows to switch pages. Automatically paginates provided options. - * Accepts pre-generated data and text. Alternative to optionStringifier if the text is already generated. - */ - pagedListButtons: function (target, title, description, options, _a) { - var _b; - var _c = _a.rowsPerPage, rowsPerPage = _c === void 0 ? 10 : _c, _d = _a.columns, columns = _d === void 0 ? 3 : _d, cfg = __rest(_a, ["rowsPerPage", "columns"]); - //Generate pages - var pages = (0, funcs_1.to2DArray)((0, funcs_1.to2DArray)(options, columns), rowsPerPage); - if (pages.length <= 1) - return exports.Menu.buttons(target, title, description, (_b = pages[0]) !== null && _b !== void 0 ? _b : [], cfg); - return exports.Menu.pages(target, title, description, pages, cfg); - }, - /** - * Displays a menu to a player, returning a Promise. - * Adds left and right arrows to switch pages. Automatically paginates provided options. - */ - pagedList: function (target, title, description, options, _a) { - var _b; - if (_a === void 0) { _a = {}; } - var _c = _a.rowsPerPage, rowsPerPage = _c === void 0 ? 10 : _c, _d = _a.columns, columns = _d === void 0 ? 3 : _d, _e = _a.optionStringifier, optionStringifier = _e === void 0 ? String : _e, cfg = __rest(_a, ["rowsPerPage", "columns", "optionStringifier"]); - //Generate pages - var pages = (0, funcs_1.to2DArray)((0, funcs_1.to2DArray)(options.map(function (o) { return ({ data: o, get text() { return optionStringifier(o); } }); }), columns), rowsPerPage); - if (pages.length <= 1) - return exports.Menu.buttons(target, title, description, (_b = pages[0]) !== null && _b !== void 0 ? _b : [], cfg); - return exports.Menu.pages(target, title, description, pages, cfg); - } -}; +"use strict"; +/* eslint-disable @typescript-eslint/array-type */ +/* +Copyright © BalaM314, 2026. All Rights Reserved. +This file contains the menu framework. +For usage information, see docs/framework-usage-guide.md +For maintenance information, see docs/frameworks.md +*/ +var __assign = (this && this.__assign) || function () { + __assign = Object.assign || function(t) { + for (var s, i = 1, n = arguments.length; i < n; i++) { + s = arguments[i]; + for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p)) + t[p] = s[p]; + } + return t; + }; + return __assign.apply(this, arguments); +}; +var __rest = (this && this.__rest) || function (s, e) { + var t = {}; + for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0) + t[p] = s[p]; + if (s != null && typeof Object.getOwnPropertySymbols === "function") + for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) { + if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i])) + t[p[i]] = s[p[i]]; + } + return t; +}; +var __values = (this && this.__values) || function(o) { + var s = typeof Symbol === "function" && Symbol.iterator, m = s && o[s], i = 0; + if (m) return m.call(o); + if (o && typeof o.length === "number") return { + next: function () { + if (o && i >= o.length) o = void 0; + return { value: o && o[i++], done: !o }; + } + }; + throw new TypeError(s ? "Object is not iterable." : "Symbol.iterator is not defined."); +}; +var __read = (this && this.__read) || function (o, n) { + var m = typeof Symbol === "function" && o[Symbol.iterator]; + if (!m) return o; + var i = m.call(o), r, ar = [], e; + try { + while ((n === void 0 || n-- > 0) && !(r = i.next()).done) ar.push(r.value); + } + catch (error) { e = { error: error }; } + finally { + try { + if (r && !r.done && (m = i["return"])) m.call(i); + } + finally { if (e) throw e.error; } + } + return ar; +}; +var __spreadArray = (this && this.__spreadArray) || function (to, from, pack) { + if (pack || arguments.length === 2) for (var i = 0, l = from.length, ar; i < l; i++) { + if (ar || !(i in from)) { + if (!ar) ar = Array.prototype.slice.call(from, 0, i); + ar[i] = from[i]; + } + } + return to.concat(ar || Array.prototype.slice.call(from)); +}; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.listeners = exports.Menu = exports.Cancel = void 0; +exports.registerListeners = registerListeners; +var commands_1 = require("/frameworks/commands"); +var funcs_1 = require("/funcs"); +var players_1 = require("/players"); +var promise_1 = require("/promise"); +var utils_1 = require("/utils"); +/** Used to change the behavior of adding another menu when being run in a menu callback. */ +var isInMenuCallback = false; +/** Stores a mapping from name to the numeric id of a listener that has been registered. */ +var registeredListeners = {}; +exports.listeners = registeredListeners; +/** Stores all listeners in use by fish-commands. */ +var listeners = { + generic: function (player, option) { + var fishSender = players_1.FishPlayer.get(player); + var prevCallback = fishSender.activeMenus.shift(); + if (!prevCallback) + return; //No menu to process, do nothing + isInMenuCallback = true; + prevCallback.callback(option); + isInMenuCallback = false; + }, + none: function (player, option) { + //do nothing + } +}; +exports.Cancel = Symbol("Cancel"); +/** Registers all listeners, should be called on server load. */ +function registerListeners() { + var e_1, _a; + var _b; + try { + for (var _c = __values(Object.entries(listeners)), _d = _c.next(); !_d.done; _d = _c.next()) { + var _e = __read(_d.value, 2), key = _e[0], listener = _e[1]; + (_b = registeredListeners[key]) !== null && _b !== void 0 ? _b : (registeredListeners[key] = Menus.registerMenu(listener)); + } + } + catch (e_1_1) { e_1 = { error: e_1_1 }; } + finally { + try { + if (_d && !_d.done && (_a = _c.return)) _a.call(_c); + } + finally { if (e_1) throw e_1.error; } + } +} +exports.Menu = { + /** Displays a menu to a player, returning a Promise. */ + raw: function (title, description, arrangedOptions, target, _a) { + var _b = _a === void 0 ? {} : _a, _c = _b.optionStringifier, optionStringifier = _c === void 0 ? String : _c, _d = _b.onCancel, onCancel = _d === void 0 ? "reject" : _d, _e = _b.cancelOptionId, cancelOptionId = _e === void 0 ? -1 : _e; + var _f = promise_1.Promise.withResolvers(), promise = _f.promise, reject = _f.reject, resolve = _f.resolve; + //The target fishPlayer has a property called activeMenu, which stores information about the last menu triggered. + //If menu() is being called from a menu calback, add it to the front of the queue so it is processed before any other menus. + //Otherwise, two multi-step menus queued together would alternate, which would confuse the player. + target.activeMenus[isInMenuCallback ? "unshift" : "push"]({ callback: function (option) { + //Additional permission validation could be done here, but the only way that callback() can be called is if the above statement executed, + //and on sensitive menus such as the stop menu, the only way to reach that is if menu() was called by the /stop command, + //which already checks permissions. + //Additionally, the callback is cleared by the generic menu listener after it is executed. + try { + var options = arrangedOptions.flat(); + //We do need to validate option though, as it can be any number. + if (option === -1 || option === cancelOptionId || !(option in options)) { + //Consider any invalid option to be a cancellation + if (onCancel == "null") + resolve(null); + else if (onCancel == "reject") + reject(exports.Cancel); + else + return; + } + else { + resolve(options[option]); + } + } + catch (err) { + (0, utils_1.handleError)(err, target, utils_1.outputFail, "".concat(target.cleanedName, " submitted menu \"").concat(title, "\" \"").concat(description, "\"")); + } + } }); + var i = 0; + var stringifiedOptions = arrangedOptions.map(function (r) { return r.map(function (item) { + if (i === cancelOptionId) + return item; + i++; + return optionStringifier(item); + }); }); + Call.menu(target.con, registeredListeners.generic, title, description, stringifiedOptions); + return promise; + }, + /** Displays a menu to a player, returning a Promise. Arranges provided options into a 2D array, and can add a Cancel option. */ + menu: function (title, description, options, target, _a) { + var _b = _a === void 0 ? {} : _a, _c = _b.includeCancel, includeCancel = _c === void 0 ? false : _c, _d = _b.optionStringifier, optionStringifier = _d === void 0 ? String : _d, _e = _b.columns, columns = _e === void 0 ? 3 : _e, _f = _b.onCancel, onCancel = _f === void 0 ? "reject" : _f, _g = _b.cancelOptionId, cancelOptionId = _g === void 0 ? -1 : _g; + //Set up the 2D array of options, and maybe add cancel + //Call.menu() with [[]] will cause a client crash, make sure to pass [] instead + var arrangedOptions = (options.length == 0 && !includeCancel) ? [] : (0, funcs_1.to2DArray)(options, columns); + if (includeCancel) { + arrangedOptions.push([(typeof includeCancel == "string" ? includeCancel : "[red]Cancel[]")]); + //This is safe because cancelOptionId is set, + //so the handler will never get called with "Cancel". + cancelOptionId = options.length; + } + return exports.Menu.raw(title, description, arrangedOptions, target, { + cancelOptionId: cancelOptionId, + onCancel: onCancel, + optionStringifier: optionStringifier + }); + }, + /** Rejects with a CommandError if the user chooses to cancel. */ + confirm: function (target, description, _a) { + var _b = _a === void 0 ? {} : _a, _c = _b.cancelOutput, cancelOutput = _c === void 0 ? "Cancelled." : _c, _d = _b.title, title = _d === void 0 ? "Confirm" : _d, _e = _b.confirmText, confirmText = _e === void 0 ? "[green]Confirm" : _e, _f = _b.cancelText, cancelText = _f === void 0 ? "[red]Cancel" : _f; + return exports.Menu.menu(title, description, [confirmText, cancelText], target, { onCancel: "reject", cancelOptionId: 1 }).catch(function (e) { + if (e === exports.Cancel) + (0, commands_1.fail)(cancelOutput); + throw e; //some random error, rethrow it + }); + }, + /** Same as confirm(), but with inverted colors, for potentially dangerous actions. */ + confirmDangerous: function (target, description, _a) { + if (_a === void 0) { _a = {}; } + var _b = _a.confirmText, confirmText = _b === void 0 ? "[red]Confirm" : _b, _c = _a.cancelText, cancelText = _c === void 0 ? "[green]Cancel" : _c, rest = __rest(_a, ["confirmText", "cancelText"]); + return exports.Menu.confirm(target, description, __assign({ cancelText: cancelText, confirmText: confirmText }, rest)); + }, + /** + * Displays a menu to a player, returning a Promise. + * Accepts pre-generated data and text. Alternative to optionStringifier if the text is already generated. + */ + buttons: function (target, title, description, options, cfg) { + if (cfg === void 0) { cfg = {}; } + return exports.Menu.raw(title, description, options, target, __assign(__assign({}, cfg), { optionStringifier: function (o) { return o.text; } })).then(function (o) { return o === null || o === void 0 ? void 0 : o.data; }); + }, + /** + * Displays a menu to a player, returning a Promise. + * Adds left and right arrows to switch pages. + * Shows different options based on the page. + */ + pages: function (target, title, description, options, cfg) { + var _a = promise_1.Promise.withResolvers(), promise = _a.promise, reject = _a.reject, resolve = _a.resolve; + function showPage(index) { + var opts = __spreadArray(__spreadArray([], __read(options[index].map(function (r) { return r.map(function (d) { return ({ text: d.text, data: [d.data] }); }); })), false), [ + [ + { data: "left", text: "[".concat(index == 0 ? "gray" : "accent", "]<--") }, + { data: "numbers", text: "[accent]".concat(index + 1, "/").concat(options.length) }, + { data: "right", text: "[".concat(index == options.length - 1 ? "gray" : "accent", "]-->") } + ] + ], false); + void exports.Menu.buttons(target, title, description, opts, __assign(__assign({}, cfg), { onCancel: "null" })).then(function (response) { + if (response instanceof Array) + resolve(response[0]); + else if (response === "right") + showPage(Math.min(index + 1, options.length - 1)); + else if (response === "left") + showPage(Math.max(index - 1, 0)); + else { + //Treat numbers as cancel + if (cfg.onCancel == "null") + resolve(null); + else if (cfg.onCancel == "reject") + reject(exports.Cancel); + //otherwise, just let the promise hang + } + }); + } + showPage(0); + return promise; + }, + /** + * Displays a menu to a player, returning a Promise. + * Adds left and right arrows to switch pages. + * Shows different text based on the current page. + */ + textPages: function (target, pages, options, cfg) { + if (options === void 0) { options = []; } + if (cfg === void 0) { cfg = {}; } + var _a = promise_1.Promise.withResolvers(), promise = _a.promise, reject = _a.reject, resolve = _a.resolve; + var pageSkipSize = Math.max(Math.floor(pages.length / 8), 5); + function showPage(index) { + var opts = __spreadArray(__spreadArray([ + [ + { data: ["left", pageSkipSize], text: "[".concat(index == 0 ? "gray" : "accent", "]<<<") }, + { data: ["left", 1], text: "[".concat(index == 0 ? "gray" : "accent", "]<--") }, + { data: ["right", 1], text: "[".concat(index == pages.length - 1 ? "gray" : "accent", "]-->") }, + { data: ["right", pageSkipSize], text: "[".concat(index == pages.length - 1 ? "gray" : "accent", "]>>>") }, + ], [ + { data: ["numbers"], text: "[accent]Page ".concat(index + 1, "/").concat(pages.length) }, + ] + ], __read(options.map(function (d, i) { return [{ data: [i], text: d }]; })), false), [ + [ + { data: ["cancel"], text: "[lightgray]Close" }, + ], + ], false); + void exports.Menu.buttons(target, pages[index][0], pages[index][1](), opts, __assign(__assign({}, cfg), { onCancel: "null" })).then(function (response) { + if ((response === null || response === void 0 ? void 0 : response[0]) === "right") + showPage(Math.min(index + response[1], pages.length - 1)); + else if ((response === null || response === void 0 ? void 0 : response[0]) === "left") + showPage(Math.max(index - response[1], 0)); + else if (typeof (response === null || response === void 0 ? void 0 : response[0]) === "number") { + resolve([index, options[response[0]]]); + } + else { + //Treat numbers as cancel + if (cfg.onCancel == "null") + resolve(null); + else if (cfg.onCancel == "reject") + reject(exports.Cancel); + //otherwise, just let the promise hang + } + }); + } + var index = (function () { + if (cfg.startPage == undefined) + return 0; + if (typeof cfg.startPage === 'number') { + if (cfg.startPage < 0) + return 0; + return cfg.startPage; + } + var index = pages.findIndex(function (_a) { + var _b = __read(_a, 1), title = _b[0]; + return title === cfg.startPage; + }); + if (index === -1) + return 0; + return index; + })(); + showPage(index); + return promise; + }, + /** + * Displays a menu to a player, returning a Promise. + * Accepts a 2D array of options and shows a region of that 2D grid. + * Adds arrows to scroll left/right/up/down. + * Resolves to the selected option. + */ + scroll2D: function (target, title, description, options, cfg) { + var _a, _b; + if (cfg === void 0) { cfg = {}; } + var _c = promise_1.Promise.withResolvers(), promise = _c.promise, reject = _c.reject, resolve = _c.resolve; + var _d = cfg.rows, rows = _d === void 0 ? 5 : _d, _e = cfg.columns, cols = _e === void 0 ? 5 : _e; + var height = options.length; + var width = options[0].length; + function showPage(x, y) { + var _a, _b; + var opts = __spreadArray(__spreadArray([], __read(options.slice(y, y + rows).map(function (r) { return r.concat(Array(width - r.length).fill({ data: "blank", text: "" })); }).map(function (r) { + return r.slice(x, x + cols).map(function (d) { return ({ text: d.text, data: [d.data] }); }); + })), false), [ + [ + { data: "blank", text: "" }, + ], + [ + { data: "blank", text: "" }, + { data: "up", text: "[".concat(y == 0 ? "gray" : "accent", "]").concat(String.fromCharCode(Iconc.up)) }, + { data: "blank", text: "" }, + ], [ + { data: "left", text: "[".concat(x == 0 ? "gray" : "accent", "]").concat(String.fromCharCode(Iconc.left)) }, + { data: "blank", text: (_b = (_a = cfg.getCenterText) === null || _a === void 0 ? void 0 : _a.call(cfg, x, y)) !== null && _b !== void 0 ? _b : '' }, + { data: "right", text: "[".concat(x == width - cols ? "gray" : "accent", "]").concat(String.fromCharCode(Iconc.right)) }, + ], [ + { data: "blank", text: "" }, + { data: "down", text: "[".concat(y == height - rows ? "gray" : "accent", "]").concat(String.fromCharCode(Iconc.down)) }, + { data: "blank", text: "" }, + ] + ], false); + void exports.Menu.buttons(target, title, description, opts, cfg).then(function (response) { + if (response instanceof Array) + resolve([response[0], x, y]); + else if (response === "right") + showPage(Math.min(x + 1, width - cols), y); + else if (response === "left") + showPage(Math.max(x - 1, 0), y); + else if (response === "up") + showPage(x, Math.max(y - 1, 0)); + else if (response === "down") + showPage(x, Math.min(y + 1, height - rows)); + else { + //Treat numbers as cancel + if (cfg.onCancel == "null") + resolve(null); + else if (cfg.onCancel == "reject") + reject(exports.Cancel); + //otherwise, just let the promise hang + } + }); + } + showPage(Math.min((_a = cfg.x) !== null && _a !== void 0 ? _a : 0, width - cols), Math.min((_b = cfg.y) !== null && _b !== void 0 ? _b : 0, height - rows)); + return promise; + }, + /** + * Displays a menu to a player, returning a Promise. + * Adds left and right arrows to switch pages. Automatically paginates provided options. + * Accepts pre-generated data and text. Alternative to optionStringifier if the text is already generated. + */ + pagedListButtons: function (target, title, description, options, _a) { + var _b; + var _c = _a.rowsPerPage, rowsPerPage = _c === void 0 ? 10 : _c, _d = _a.columns, columns = _d === void 0 ? 3 : _d, cfg = __rest(_a, ["rowsPerPage", "columns"]); + //Generate pages + var pages = (0, funcs_1.to2DArray)((0, funcs_1.to2DArray)(options, columns), rowsPerPage); + if (pages.length <= 1) + return exports.Menu.buttons(target, title, description, (_b = pages[0]) !== null && _b !== void 0 ? _b : [], cfg); + return exports.Menu.pages(target, title, description, pages, cfg); + }, + /** + * Displays a menu to a player, returning a Promise. + * Adds left and right arrows to switch pages. Automatically paginates provided options. + */ + pagedList: function (target, title, description, options, _a) { + var _b; + if (_a === void 0) { _a = {}; } + var _c = _a.rowsPerPage, rowsPerPage = _c === void 0 ? 10 : _c, _d = _a.columns, columns = _d === void 0 ? 3 : _d, _e = _a.optionStringifier, optionStringifier = _e === void 0 ? String : _e, cfg = __rest(_a, ["rowsPerPage", "columns", "optionStringifier"]); + //Generate pages + var pages = (0, funcs_1.to2DArray)((0, funcs_1.to2DArray)(options.map(function (o) { return ({ data: o, get text() { return optionStringifier(o); } }); }), columns), rowsPerPage); + if (pages.length <= 1) + return exports.Menu.buttons(target, title, description, (_b = pages[0]) !== null && _b !== void 0 ? _b : [], cfg); + return exports.Menu.pages(target, title, description, pages, cfg); + } +}; diff --git a/build/scripts/funcs.js b/build/scripts/funcs.js index 4a811ba2..ee7f6bac 100644 --- a/build/scripts/funcs.js +++ b/build/scripts/funcs.js @@ -1,474 +1,474 @@ -"use strict"; -var __read = (this && this.__read) || function (o, n) { - var m = typeof Symbol === "function" && o[Symbol.iterator]; - if (!m) return o; - var i = m.call(o), r, ar = [], e; - try { - while ((n === void 0 || n-- > 0) && !(r = i.next()).done) ar.push(r.value); - } - catch (error) { e = { error: error }; } - finally { - try { - if (r && !r.done && (m = i["return"])) m.call(i); - } - finally { if (e) throw e.error; } - } - return ar; -}; -var __spreadArray = (this && this.__spreadArray) || function (to, from, pack) { - if (pack || arguments.length === 2) for (var i = 0, l = from.length, ar; i < l; i++) { - if (ar || !(i in from)) { - if (!ar) ar = Array.prototype.slice.call(from, 0, i); - ar[i] = from[i]; - } - } - return to.concat(ar || Array.prototype.slice.call(from)); -}; -var __values = (this && this.__values) || function(o) { - var s = typeof Symbol === "function" && Symbol.iterator, m = s && o[s], i = 0; - if (m) return m.call(o); - if (o && typeof o.length === "number") return { - next: function () { - if (o && i >= o.length) o = void 0; - return { value: o && o[i++], done: !o }; - } - }; - throw new TypeError(s ? "Object is not iterable." : "Symbol.iterator is not defined."); -}; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.DurationSecs = exports.Duration = exports.EventEmitter = exports.StringIO = exports.StringBuilder = void 0; -exports.memoize = memoize; -exports.to2DArray = to2DArray; -exports.setToArray = setToArray; -exports.crash = crash; -exports.capitalizeText = capitalizeText; -exports.indefiniteArticle = indefiniteArticle; -exports.escapeTextDiscord = escapeTextDiscord; -exports.escapeStringColorsClient = escapeStringColorsClient; -exports.escapeStringColorsServer = escapeStringColorsServer; -exports.parseError = parseError; -exports.tagProcessor = tagProcessor; -exports.tagProcessorPartial = tagProcessorPartial; -exports.random = random; -exports.getIPAddress = getIPAddress; -exports.lazy = lazy; -exports.invalidtoNull = invalidtoNull; -exports.cleanColors = cleanColors; -exports.computeStatistics = computeStatistics; -exports.search = search; -exports.searchFixed = searchFixed; -exports.delay = delay; -var storedValues = {}; -/** - * Stores the output of a function and returns that value - * instead of running the function again unless any - * dependencies have changed to improve performance with - * functions that have expensive computation. - * @param callback function to run if a dependancy has changed - * @param dep dependency array of values to monitor - * @param id arbitrary unique id of the function for storage purposes. - */ -function memoize(callback, dep, id) { - if (!storedValues[id]) { - storedValues[id] = { value: callback(), dep: dep }; - } - else if (dep.some(function (d, ind) { return d !== storedValues[id].dep[ind]; })) { - //If the value changed - storedValues[id].value = callback(); - storedValues[id].dep = dep; - } - return storedValues[id].value; -} /** - * Converts a 1D array into a 2D array. - * @param width the max length of each row. - * The last row may not be full. - */ -function to2DArray(array, width) { - if (array.length == 0) - return []; - var output = [[]]; - array.forEach(function (el) { - if (output.at(-1).length >= width) { - output.push([]); - } - output.at(-1).push(el); - }); - return output; -} -function setToArray(set) { - var array = []; - set.each(function (item) { return array.push(item); }); - return array; -} -var StringBuilder = /** @class */ (function () { - function StringBuilder(str) { - if (str === void 0) { str = ""; } - this.str = str; - } - StringBuilder.prototype.add = function (str) { - this.str += str; - return this; - }; - StringBuilder.prototype.chunk = function (str) { - if (Strings.stripColors(str).length > 0) { - this.str = this.str + " " + str; - } - return this; - }; - return StringBuilder; -}()); -exports.StringBuilder = StringBuilder; -/** - * Used for serialization to strings. - * @deprecated use Serializer instead, which serializes to byte[] - */ -var StringIO = /** @class */ (function () { - function StringIO(string) { - if (string === void 0) { string = ""; } - this.string = string; - this.offset = 0; - } - StringIO.prototype.read = function (length) { - if (length === void 0) { length = 1; } - if (this.offset + length > this.string.length) - crash("Unexpected EOF"); - return this.string.slice(this.offset, this.offset += length); - }; - StringIO.prototype.write = function (str) { - this.string += str; - }; - StringIO.prototype.readString = function (/** The length of the written length. */ lenlen) { - if (lenlen === void 0) { lenlen = 3; } - var length = parseInt(this.read(lenlen)); - if (length == 0) - return null; - return this.read(length); - }; - StringIO.prototype.writeString = function (str, lenlen, truncate) { - if (lenlen === void 0) { lenlen = 3; } - if (truncate === void 0) { truncate = false; } - if (str === null) { - this.string += "0".repeat(lenlen); - } - else if (typeof str !== "string") { - crash("Attempted to serialize string ".concat(String(str), ", but it was not a string")); - } - else if (str.length > (Math.pow(10, lenlen) - 1)) { - if (truncate) { - Log.err("Cannot write strings with length greater than ".concat((Math.pow(10, lenlen) - 1), " (was ").concat(str.length, "), truncating")); - this.string += (Math.pow(10, lenlen) - 1).toString().padStart(lenlen, "0"); - this.string += str.slice(0, (Math.pow(10, lenlen) - 1)); - } - else { - crash("Cannot write strings with length greater than ".concat((Math.pow(10, lenlen) - 1), " (was ").concat(str.length, ")\n String was: \"").concat(str, "\"")); - } - } - else { - this.string += str.length.toString().padStart(lenlen, "0"); - this.string += str; - } - }; - StringIO.prototype.readEnumString = function (options) { - var length = (options.length - 1).toString().length; - var option = this.readNumber(length); - return options[option]; - }; - StringIO.prototype.writeEnumString = function (value, options) { - var length = (options.length - 1).toString().length; - var option = options.indexOf(value); - if (option == -1) - crash("Attempted to write invalid value \"".concat(String(value), "\" for enum, valid values are (").concat(options.join(", "), ")")); - this.writeNumber(option, length); - }; - StringIO.prototype.readNumber = function (size) { - if (size === void 0) { size = 4; } - var data = this.read(size); - if (/^0*-\d+$/.test(data)) { - //negative numbers were incorrectly stored in previous versions - data = "-" + data.split("-")[1]; - } - if (isNaN(Number(data))) - crash("Attempted to read invalid number: ".concat(data)); - return Number(data); - }; - StringIO.prototype.writeNumber = function (num, size, clamp) { - if (size === void 0) { size = 4; } - if (clamp === void 0) { clamp = false; } - if (typeof num != "number") - crash("".concat(String(num), " was not a number!")); - if (num.toString().length > size) { - if (clamp) { - if (num > (Math.pow(10, size)) - 1) - this.string += (Math.pow(10, size)) - 1; - else - this.string += num.toString().slice(0, size); - } - else - crash("Cannot write number ".concat(num, " with length ").concat(size, ": too long")); - } - this.string += num.toString().padStart(size, "0"); - }; - StringIO.prototype.readBool = function () { - return this.read(1) == "T" ? true : false; - }; - StringIO.prototype.writeBool = function (val) { - this.write(val ? "T" : "F"); - }; - StringIO.prototype.writeArray = function (array, func, lenlen) { - var _this = this; - this.writeNumber(array.length, lenlen); - array.forEach(function (e) { return func(e, _this); }); - }; - StringIO.prototype.readArray = function (func, lenlen) { - var length = this.readNumber(lenlen); - var array = []; - for (var i = 0; i < length; i++) { - array[i] = func(this); - } - return array; - }; - StringIO.prototype.expectEOF = function () { - if (this.string.length > this.offset) - crash("Expected EOF, but found extra data: \"".concat(this.string.slice(this.offset), "\"")); - }; - StringIO.read = function (data, func) { - var str = new StringIO(data); - try { - return func(str); - } - catch (err) { - Log.err("Error while reading compressed data!"); - Log.err(data); - throw err; - } - }; - StringIO.write = function (data, func) { - var str = new StringIO(); - func(str, data); - return str.string; - }; - return StringIO; -}()); -exports.StringIO = StringIO; -/** Something that emits events. */ -var EventEmitter = /** @class */ (function () { - function EventEmitter() { - this.listeners = {}; - } - EventEmitter.prototype.on = function (event, callback) { - var _a; - var _b; - ((_a = (_b = this.listeners)[event]) !== null && _a !== void 0 ? _a : (_b[event] = [])).push(callback); - return this; - }; - EventEmitter.prototype.fire = function (event, args) { - var _a; - var listeners = (_a = this.listeners[event]) !== null && _a !== void 0 ? _a : []; - // eslint-disable-next-line @typescript-eslint/prefer-for-of - for (var i = 0; i < listeners.length; i++) { - listeners[i].apply(listeners, __spreadArray([this], __read(args), false)); - } - }; - return EventEmitter; -}()); -exports.EventEmitter = EventEmitter; -function crash(message) { - throw new Error(message); -} -/** Best effort title-capitalization of a word. */ -function capitalizeText(text, separator) { - if (separator === void 0) { separator = " "; } - return text - .split(separator) - .map(function (word, i, arr) { return (["a", "an", "the", "in", "and", "of", "it", "is"].includes(word) && - i !== 0 && i !== arr.length - 1) ? word - : word[0].toUpperCase() + word.substring(1).toLowerCase(); }).join(" "); -} -/** Best effort prepends an indefinite article (either "a" or "an") to provided text. */ -function indefiniteArticle(text) { - var cText = Strings.stripColors(text); - if (/^[aeiou]/.test(cText) || cText == "hour") - return "an " + text; - else - return "a " + text; -} -var pattern = Pattern.compile("([*\\_~`|:])"); -function escapeTextDiscord(text) { - return pattern.matcher(text).replaceAll("\\\\$1\u200B"); -} -/** Prevents Mindustry from displaying color tags in a string by escaping them. Example: turns [scarlet]red to [[scarlet]red. */ -function escapeStringColorsClient(str) { - return str.replace(/\[/g, "[["); -} -// export function highlightStringColorsClient(str:string):string { -// return str.replace(/(? 1) - return { value: result }; - }; - try { - for (var filters_1 = __values(filters), filters_1_1 = filters_1.next(); !filters_1_1.done; filters_1_1 = filters_1.next()) { - var filter = filters_1_1.value; - var state_1 = _loop_1(filter); - if (typeof state_1 === "object") - return state_1.value; - } - } - catch (e_1_1) { e_1 = { error: e_1_1 }; } - finally { - try { - if (filters_1_1 && !filters_1_1.done && (_a = filters_1.return)) _a.call(filters_1); - } - finally { if (e_1) throw e_1.error; } - } - return null; - }; -} -function searchFixed(options, filters, recomputeOptions) { - var func = search.apply(void 0, __spreadArray([], __read(filters), false)); - var _options = options; - return function (query) { - if (typeof _options == "function") { - if (recomputeOptions) - return func(_options(), query); - else - return func(_options = _options(), query); - } - else - return func(_options, query); - }; -} -function delay(millis) { - return new Promise(function (res) { return Timer.schedule(res, millis / 1000); }); -} -exports.Duration = { - seconds: function (x) { return x * 1000; }, - minutes: function (x) { return x * 60000; }, - hours: function (x) { return x * 3600000; }, - days: function (x) { return x * 86400000; }, - months: function (x) { return x * 2592000000; }, -}; -exports.DurationSecs = { - minutes: function (x) { return x * 60; }, - hours: function (x) { return x * 3600; }, - days: function (x) { return x * 86400; }, - months: function (x) { return x * 2592000; }, -}; +"use strict"; +var __read = (this && this.__read) || function (o, n) { + var m = typeof Symbol === "function" && o[Symbol.iterator]; + if (!m) return o; + var i = m.call(o), r, ar = [], e; + try { + while ((n === void 0 || n-- > 0) && !(r = i.next()).done) ar.push(r.value); + } + catch (error) { e = { error: error }; } + finally { + try { + if (r && !r.done && (m = i["return"])) m.call(i); + } + finally { if (e) throw e.error; } + } + return ar; +}; +var __spreadArray = (this && this.__spreadArray) || function (to, from, pack) { + if (pack || arguments.length === 2) for (var i = 0, l = from.length, ar; i < l; i++) { + if (ar || !(i in from)) { + if (!ar) ar = Array.prototype.slice.call(from, 0, i); + ar[i] = from[i]; + } + } + return to.concat(ar || Array.prototype.slice.call(from)); +}; +var __values = (this && this.__values) || function(o) { + var s = typeof Symbol === "function" && Symbol.iterator, m = s && o[s], i = 0; + if (m) return m.call(o); + if (o && typeof o.length === "number") return { + next: function () { + if (o && i >= o.length) o = void 0; + return { value: o && o[i++], done: !o }; + } + }; + throw new TypeError(s ? "Object is not iterable." : "Symbol.iterator is not defined."); +}; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.DurationSecs = exports.Duration = exports.EventEmitter = exports.StringIO = exports.StringBuilder = void 0; +exports.memoize = memoize; +exports.to2DArray = to2DArray; +exports.setToArray = setToArray; +exports.crash = crash; +exports.capitalizeText = capitalizeText; +exports.indefiniteArticle = indefiniteArticle; +exports.escapeTextDiscord = escapeTextDiscord; +exports.escapeStringColorsClient = escapeStringColorsClient; +exports.escapeStringColorsServer = escapeStringColorsServer; +exports.parseError = parseError; +exports.tagProcessor = tagProcessor; +exports.tagProcessorPartial = tagProcessorPartial; +exports.random = random; +exports.getIPAddress = getIPAddress; +exports.lazy = lazy; +exports.invalidtoNull = invalidtoNull; +exports.cleanColors = cleanColors; +exports.computeStatistics = computeStatistics; +exports.search = search; +exports.searchFixed = searchFixed; +exports.delay = delay; +var storedValues = {}; +/** + * Stores the output of a function and returns that value + * instead of running the function again unless any + * dependencies have changed to improve performance with + * functions that have expensive computation. + * @param callback function to run if a dependancy has changed + * @param dep dependency array of values to monitor + * @param id arbitrary unique id of the function for storage purposes. + */ +function memoize(callback, dep, id) { + if (!storedValues[id]) { + storedValues[id] = { value: callback(), dep: dep }; + } + else if (dep.some(function (d, ind) { return d !== storedValues[id].dep[ind]; })) { + //If the value changed + storedValues[id].value = callback(); + storedValues[id].dep = dep; + } + return storedValues[id].value; +} /** + * Converts a 1D array into a 2D array. + * @param width the max length of each row. + * The last row may not be full. + */ +function to2DArray(array, width) { + if (array.length == 0) + return []; + var output = [[]]; + array.forEach(function (el) { + if (output.at(-1).length >= width) { + output.push([]); + } + output.at(-1).push(el); + }); + return output; +} +function setToArray(set) { + var array = []; + set.each(function (item) { return array.push(item); }); + return array; +} +var StringBuilder = /** @class */ (function () { + function StringBuilder(str) { + if (str === void 0) { str = ""; } + this.str = str; + } + StringBuilder.prototype.add = function (str) { + this.str += str; + return this; + }; + StringBuilder.prototype.chunk = function (str) { + if (Strings.stripColors(str).length > 0) { + this.str = this.str + " " + str; + } + return this; + }; + return StringBuilder; +}()); +exports.StringBuilder = StringBuilder; +/** + * Used for serialization to strings. + * @deprecated use Serializer instead, which serializes to byte[] + */ +var StringIO = /** @class */ (function () { + function StringIO(string) { + if (string === void 0) { string = ""; } + this.string = string; + this.offset = 0; + } + StringIO.prototype.read = function (length) { + if (length === void 0) { length = 1; } + if (this.offset + length > this.string.length) + crash("Unexpected EOF"); + return this.string.slice(this.offset, this.offset += length); + }; + StringIO.prototype.write = function (str) { + this.string += str; + }; + StringIO.prototype.readString = function (/** The length of the written length. */ lenlen) { + if (lenlen === void 0) { lenlen = 3; } + var length = parseInt(this.read(lenlen)); + if (length == 0) + return null; + return this.read(length); + }; + StringIO.prototype.writeString = function (str, lenlen, truncate) { + if (lenlen === void 0) { lenlen = 3; } + if (truncate === void 0) { truncate = false; } + if (str === null) { + this.string += "0".repeat(lenlen); + } + else if (typeof str !== "string") { + crash("Attempted to serialize string ".concat(String(str), ", but it was not a string")); + } + else if (str.length > (Math.pow(10, lenlen) - 1)) { + if (truncate) { + Log.err("Cannot write strings with length greater than ".concat((Math.pow(10, lenlen) - 1), " (was ").concat(str.length, "), truncating")); + this.string += (Math.pow(10, lenlen) - 1).toString().padStart(lenlen, "0"); + this.string += str.slice(0, (Math.pow(10, lenlen) - 1)); + } + else { + crash("Cannot write strings with length greater than ".concat((Math.pow(10, lenlen) - 1), " (was ").concat(str.length, ")\n String was: \"").concat(str, "\"")); + } + } + else { + this.string += str.length.toString().padStart(lenlen, "0"); + this.string += str; + } + }; + StringIO.prototype.readEnumString = function (options) { + var length = (options.length - 1).toString().length; + var option = this.readNumber(length); + return options[option]; + }; + StringIO.prototype.writeEnumString = function (value, options) { + var length = (options.length - 1).toString().length; + var option = options.indexOf(value); + if (option == -1) + crash("Attempted to write invalid value \"".concat(String(value), "\" for enum, valid values are (").concat(options.join(", "), ")")); + this.writeNumber(option, length); + }; + StringIO.prototype.readNumber = function (size) { + if (size === void 0) { size = 4; } + var data = this.read(size); + if (/^0*-\d+$/.test(data)) { + //negative numbers were incorrectly stored in previous versions + data = "-" + data.split("-")[1]; + } + if (isNaN(Number(data))) + crash("Attempted to read invalid number: ".concat(data)); + return Number(data); + }; + StringIO.prototype.writeNumber = function (num, size, clamp) { + if (size === void 0) { size = 4; } + if (clamp === void 0) { clamp = false; } + if (typeof num != "number") + crash("".concat(String(num), " was not a number!")); + if (num.toString().length > size) { + if (clamp) { + if (num > (Math.pow(10, size)) - 1) + this.string += (Math.pow(10, size)) - 1; + else + this.string += num.toString().slice(0, size); + } + else + crash("Cannot write number ".concat(num, " with length ").concat(size, ": too long")); + } + this.string += num.toString().padStart(size, "0"); + }; + StringIO.prototype.readBool = function () { + return this.read(1) == "T" ? true : false; + }; + StringIO.prototype.writeBool = function (val) { + this.write(val ? "T" : "F"); + }; + StringIO.prototype.writeArray = function (array, func, lenlen) { + var _this = this; + this.writeNumber(array.length, lenlen); + array.forEach(function (e) { return func(e, _this); }); + }; + StringIO.prototype.readArray = function (func, lenlen) { + var length = this.readNumber(lenlen); + var array = []; + for (var i = 0; i < length; i++) { + array[i] = func(this); + } + return array; + }; + StringIO.prototype.expectEOF = function () { + if (this.string.length > this.offset) + crash("Expected EOF, but found extra data: \"".concat(this.string.slice(this.offset), "\"")); + }; + StringIO.read = function (data, func) { + var str = new StringIO(data); + try { + return func(str); + } + catch (err) { + Log.err("Error while reading compressed data!"); + Log.err(data); + throw err; + } + }; + StringIO.write = function (data, func) { + var str = new StringIO(); + func(str, data); + return str.string; + }; + return StringIO; +}()); +exports.StringIO = StringIO; +/** Something that emits events. */ +var EventEmitter = /** @class */ (function () { + function EventEmitter() { + this.listeners = {}; + } + EventEmitter.prototype.on = function (event, callback) { + var _a; + var _b; + ((_a = (_b = this.listeners)[event]) !== null && _a !== void 0 ? _a : (_b[event] = [])).push(callback); + return this; + }; + EventEmitter.prototype.fire = function (event, args) { + var _a; + var listeners = (_a = this.listeners[event]) !== null && _a !== void 0 ? _a : []; + // eslint-disable-next-line @typescript-eslint/prefer-for-of + for (var i = 0; i < listeners.length; i++) { + listeners[i].apply(listeners, __spreadArray([this], __read(args), false)); + } + }; + return EventEmitter; +}()); +exports.EventEmitter = EventEmitter; +function crash(message) { + throw new Error(message); +} +/** Best effort title-capitalization of a word. */ +function capitalizeText(text, separator) { + if (separator === void 0) { separator = " "; } + return text + .split(separator) + .map(function (word, i, arr) { return (["a", "an", "the", "in", "and", "of", "it", "is"].includes(word) && + i !== 0 && i !== arr.length - 1) ? word + : word[0].toUpperCase() + word.substring(1).toLowerCase(); }).join(" "); +} +/** Best effort prepends an indefinite article (either "a" or "an") to provided text. */ +function indefiniteArticle(text) { + var cText = Strings.stripColors(text); + if (/^[aeiou]/.test(cText) || cText == "hour") + return "an " + text; + else + return "a " + text; +} +var pattern = Pattern.compile("([*\\_~`|:])"); +function escapeTextDiscord(text) { + return pattern.matcher(text).replaceAll("\\\\$1\u200B"); +} +/** Prevents Mindustry from displaying color tags in a string by escaping them. Example: turns [scarlet]red to [[scarlet]red. */ +function escapeStringColorsClient(str) { + return str.replace(/\[/g, "[["); +} +// export function highlightStringColorsClient(str:string):string { +// return str.replace(/(? 1) + return { value: result }; + }; + try { + for (var filters_1 = __values(filters), filters_1_1 = filters_1.next(); !filters_1_1.done; filters_1_1 = filters_1.next()) { + var filter = filters_1_1.value; + var state_1 = _loop_1(filter); + if (typeof state_1 === "object") + return state_1.value; + } + } + catch (e_1_1) { e_1 = { error: e_1_1 }; } + finally { + try { + if (filters_1_1 && !filters_1_1.done && (_a = filters_1.return)) _a.call(filters_1); + } + finally { if (e_1) throw e_1.error; } + } + return null; + }; +} +function searchFixed(options, filters, recomputeOptions) { + var func = search.apply(void 0, __spreadArray([], __read(filters), false)); + var _options = options; + return function (query) { + if (typeof _options == "function") { + if (recomputeOptions) + return func(_options(), query); + else + return func(_options = _options(), query); + } + else + return func(_options, query); + }; +} +function delay(millis) { + return new Promise(function (res) { return Timer.schedule(res, millis / 1000); }); +} +exports.Duration = { + seconds: function (x) { return x * 1000; }, + minutes: function (x) { return x * 60000; }, + hours: function (x) { return x * 3600000; }, + days: function (x) { return x * 86400000; }, + months: function (x) { return x * 2592000000; }, +}; +exports.DurationSecs = { + minutes: function (x) { return x * 60; }, + hours: function (x) { return x * 3600; }, + days: function (x) { return x * 86400; }, + months: function (x) { return x * 2592000; }, +}; diff --git a/build/scripts/globals.js b/build/scripts/globals.js index d606d314..10e7e366 100644 --- a/build/scripts/globals.js +++ b/build/scripts/globals.js @@ -1,34 +1,34 @@ -"use strict"; -/* -Copyright © BalaM314, 2026. All Rights Reserved. -This file contains mutable global variables, and global constants. -*/ -Object.defineProperty(exports, "__esModule", { value: true }); -exports.FishEvents = exports.unitsT5 = exports.maxTime = exports.ipRangeWildcardPattern = exports.ipRangeCIDRPattern = exports.ipPortPattern = exports.ipPattern = exports.uuidPattern = exports.ipJoins = exports.fishPlugin = exports.fishState = exports.recentWhispers = exports.tileHistory = void 0; -var funcs_1 = require("/funcs"); -exports.tileHistory = {}; -exports.recentWhispers = {}; -exports.fishState = { - restartQueued: false, - restartLoopTask: null, - corruption_t1: null, - corruption_t2: null, - lastPranked: Date.now(), - labels: [], - peacefulMode: false, - joinBell: false, - startTime: Date.now(), -}; -exports.fishPlugin = { - directory: null, - version: null, -}; -exports.ipJoins = new ObjectIntMap(); -exports.uuidPattern = /^[a-zA-Z0-9+/]{22}==$/; -exports.ipPattern = /^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}$/; -exports.ipPortPattern = /^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}:\d{1,5}$/; -exports.ipRangeCIDRPattern = /^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}\/(1[2-9]|2[0-4])$/; //Disallow anything bigger than a /12 -exports.ipRangeWildcardPattern = /^(\d{1,3}\.\d{1,3})\.(?:(\d{1,3}\.\*)|\*)$/; //Disallow anything bigger than a /16 -exports.maxTime = 9999999999999; -exports.unitsT5 = [UnitTypes.reign, UnitTypes.toxopid, UnitTypes.corvus, UnitTypes.eclipse, UnitTypes.oct, UnitTypes.omura, UnitTypes.navanax, UnitTypes.conquer, UnitTypes.collaris, UnitTypes.disrupt]; -exports.FishEvents = new funcs_1.EventEmitter(); +"use strict"; +/* +Copyright © BalaM314, 2026. All Rights Reserved. +This file contains mutable global variables, and global constants. +*/ +Object.defineProperty(exports, "__esModule", { value: true }); +exports.FishEvents = exports.unitsT5 = exports.maxTime = exports.ipRangeWildcardPattern = exports.ipRangeCIDRPattern = exports.ipPortPattern = exports.ipPattern = exports.uuidPattern = exports.ipJoins = exports.fishPlugin = exports.fishState = exports.recentWhispers = exports.tileHistory = void 0; +var funcs_1 = require("/funcs"); +exports.tileHistory = {}; +exports.recentWhispers = {}; +exports.fishState = { + restartQueued: false, + restartLoopTask: null, + corruption_t1: null, + corruption_t2: null, + lastPranked: Date.now(), + labels: [], + peacefulMode: false, + joinBell: false, + startTime: Date.now(), +}; +exports.fishPlugin = { + directory: null, + version: null, +}; +exports.ipJoins = new ObjectIntMap(); +exports.uuidPattern = /^[a-zA-Z0-9+/]{22}==$/; +exports.ipPattern = /^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}$/; +exports.ipPortPattern = /^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}:\d{1,5}$/; +exports.ipRangeCIDRPattern = /^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}\/(1[2-9]|2[0-4])$/; //Disallow anything bigger than a /12 +exports.ipRangeWildcardPattern = /^(\d{1,3}\.\d{1,3})\.(?:(\d{1,3}\.\*)|\*)$/; //Disallow anything bigger than a /16 +exports.maxTime = 9999999999999; +exports.unitsT5 = [UnitTypes.reign, UnitTypes.toxopid, UnitTypes.corvus, UnitTypes.eclipse, UnitTypes.oct, UnitTypes.omura, UnitTypes.navanax, UnitTypes.conquer, UnitTypes.collaris, UnitTypes.disrupt]; +exports.FishEvents = new funcs_1.EventEmitter(); diff --git a/build/scripts/index.js b/build/scripts/index.js index 02acd68d..07c2e51e 100644 --- a/build/scripts/index.js +++ b/build/scripts/index.js @@ -1,341 +1,341 @@ -"use strict"; -/* -Copyright © BalaM314, 2026. All Rights Reserved. -This file contains the main code, which calls other functions and initializes the plugin. -*/ -var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { - if (k2 === undefined) k2 = k; - var desc = Object.getOwnPropertyDescriptor(m, k); - if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { - desc = { enumerable: true, get: function() { return m[k]; } }; - } - Object.defineProperty(o, k2, desc); -}) : (function(o, m, k, k2) { - if (k2 === undefined) k2 = k; - o[k2] = m[k]; -})); -var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { - Object.defineProperty(o, "default", { enumerable: true, value: v }); -}) : function(o, v) { - o["default"] = v; -}); -var __importStar = (this && this.__importStar) || (function () { - var ownKeys = function(o) { - ownKeys = Object.getOwnPropertyNames || function (o) { - var ar = []; - for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k; - return ar; - }; - return ownKeys(o); - }; - return function (mod) { - if (mod && mod.__esModule) return mod; - var result = {}; - if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]); - __setModuleDefault(result, mod); - return result; - }; -})(); -var __values = (this && this.__values) || function(o) { - var s = typeof Symbol === "function" && Symbol.iterator, m = s && o[s], i = 0; - if (m) return m.call(o); - if (o && typeof o.length === "number") return { - next: function () { - if (o && i >= o.length) o = void 0; - return { value: o && o[i++], done: !o }; - } - }; - throw new TypeError(s ? "Object is not iterable." : "Symbol.iterator is not defined."); -}; -Object.defineProperty(exports, "__esModule", { value: true }); -var api = __importStar(require("/api")); -var aggregate_1 = require("/commands/aggregate"); -var config_1 = require("/config"); -var commands_1 = require("/frameworks/commands"); -var menus = __importStar(require("/frameworks/menus")); -var funcs_1 = require("/funcs"); -var globals_1 = require("/globals"); -var maps_1 = require("/maps"); -var packetHandlers_1 = require("/packetHandlers"); -var players_1 = require("/players"); -var timers = __importStar(require("/timers")); -var utils_1 = require("/utils"); -Events.on(EventType.ConnectionEvent, function (e) { - if (Vars.netServer.admins.bannedIPs.contains(e.connection.address)) { - api.getBanned({ - ip: e.connection.address, - }, function (banned) { - if (!banned) { - //If they were previously banned locally, but the API says they aren't banned, then unban them and clear the kick that the outer function already did - Vars.netServer.admins.unbanPlayerIP(e.connection.address); - Vars.netServer.admins.kickedIPs.remove(e.connection.address); - } - }); - } - else if (api.isVpnCached(e.connection.address) && players_1.FishPlayer.shouldWhackFlaggedPlayers()) { - Vars.netServer.admins.blacklistDos(e.connection.address); - e.connection.kick("You have been DOSblacklisted. Please join our discord for help: " + config_1.text.discordURL + "\nYou won't see this message again."); - Log.info("&yAntibot killed connection ".concat(e.connection.address, " due to flagged while under attack")); - } -}); -Events.on(EventType.PlayerConnect, function (e) { - if (players_1.FishPlayer.shouldKickNewPlayers() && e.player.info.timesJoined == 1) { - //do not use the helper function, for maximum performance - e.player.kick(Packets.KickReason.kick, 3600000); - } - players_1.FishPlayer.onPlayerConnect(e.player); -}); -Events.on(EventType.PlayerJoin, function (e) { - players_1.FishPlayer.onPlayerJoin(e.player); -}); -Events.on(EventType.PlayerLeave, function (e) { - players_1.FishPlayer.onPlayerLeave(e.player); -}); -Events.on(EventType.ConnectPacketEvent, function (e) { - if (!players_1.FishPlayer.connectRate.allow(5000, 35)) { - players_1.FishPlayer.triggerAntibot(300000, "Rate of player connections exceeded 35 / 5s", "automatic"); - } - globals_1.ipJoins.increment(e.connection.address); - var info = Vars.netServer.admins.getInfoOptional(e.packet.uuid); - var underAttack = players_1.FishPlayer.antiBotMode(); - var newPlayer = !info || info.timesJoined < 10; - var longModName = e.packet.mods.contains(function (str) { return str.length > 50; }); - var veryLongModName = e.packet.mods.contains(function (str) { return str.length > 100; }); - if ((underAttack && e.packet.mods.size > 2) || - (underAttack && longModName) || - (veryLongModName && (underAttack || newPlayer))) { - Vars.netServer.admins.blacklistDos(e.connection.address); - e.connection.kicked = true; - players_1.FishPlayer.triggerAntibot(60000, (veryLongModName ? "very long mod name" : longModName ? "long mod name" : "it had mods while under attack"), "automatic"); - return; - } - var suspiciousModName = e.packet.mods.contains(function (str) { return str.includes('\x1B'); }); - if (suspiciousModName || e.packet.name.includes('\x1B')) { - Vars.netServer.admins.blacklistDos(e.connection.address); - e.connection.kicked = true; - players_1.FishPlayer.triggerAntibot(5000, "illegal characters in name or mods", "automatic"); - return; - } - if (globals_1.ipJoins.get(e.connection.address) >= ((underAttack || veryLongModName) ? 3 : (newPlayer || longModName) ? 7 : 15)) { - Vars.netServer.admins.blacklistDos(e.connection.address); - e.connection.kicked = true; - players_1.FishPlayer.triggerAntibot(5000, "too many connections", "automatic"); - return; - } - /*if(e.packet.name.includes("discord.gg/GnEdS9TdV6")){ - Vars.netServer.admins.blacklistDos(e.connection.address); - e.connection.kicked = true; - FishPlayer.onBotWhack(); - Log.info(`&yAntibot killed connection ${e.connection.address} due to omni discord link`); - return; - }*/ - if (e.packet.name.includes("1`1@everyone")) { - Vars.netServer.admins.blacklistDos(e.connection.address); - e.connection.kicked = true; - players_1.FishPlayer.triggerAntibot(-1, "known bad name", "automatic"); - return; - } - if (Vars.netServer.admins.isDosBlacklisted(e.connection.address)) { - //threading moment, i think - e.connection.kicked = true; - return; - } - api.getBanned({ - ip: e.connection.address, - uuid: e.packet.uuid - }, function (banned) { - if (banned) { - Log.info("&lrSynced ban of ".concat(e.packet.uuid, "/").concat(e.connection.address, ".")); - e.connection.kick(Packets.KickReason.banned, 1); - Vars.netServer.admins.banPlayerIP(e.connection.address); - Vars.netServer.admins.banPlayerID(e.packet.uuid); - } - else { - Vars.netServer.admins.unbanPlayerIP(e.connection.address); - Vars.netServer.admins.unbanPlayerID(e.packet.uuid); - } - }); - players_1.FishPlayer.onConnectPacket(e.packet); -}); -Events.on(EventType.UnitChangeEvent, function (e) { - players_1.FishPlayer.onUnitChange(e.player, e.unit); -}); -Events.on(EventType.ContentInitEvent, function () { - //Unhide latum and renale - UnitTypes.latum.hidden = false; - UnitTypes.renale.hidden = false; -}); -Events.on(EventType.PlayerChatEvent, function (e) { return (0, utils_1.processChat)(e.player, e.message, true); }); -Events.on(EventType.ServerLoadEvent, function (e) { - Time.mark(); - var clientHandler = Vars.netServer.clientCommands; - var serverHandler = ServerControl.instance.handler; - players_1.FishPlayer.loadAll(); - globals_1.FishEvents.fire("loadData", []); - timers.initializeTimers(); - menus.registerListeners(); - //Cap delta - Time.setDeltaProvider(function () { return Math.min(Core.graphics.getDeltaTime() * 60, 10); }); - // Mute muted players - Vars.netServer.admins.addChatFilter(function (player, message) { return (0, utils_1.processChat)(player, message); }); - // Vars.netServer.admins.addChatFilter((p, message) => FishPlayer.get(p).hasPerm("member") ? message : foolifyChat(message)); - // Action filters - Vars.netServer.admins.addActionFilter(function (action) { - var _a, _b, _c; - var player = action.player; - var fishP = players_1.FishPlayer.get(player); - //prevent stopped players from doing anything other than deposit items. - if (!fishP.hasPerm("play")) { - action.player.sendMessage('[scarlet]\u26A0 [yellow]You are stopped, you cant perfom this action.'); - return false; - } - else { - if (action.type === Administration.ActionType.pickupBlock) { - (0, utils_1.addToTileHistory)({ - pos: "".concat(action.tile.x, ",").concat(action.tile.y), - uuid: action.player.uuid(), - action: "picked up", - type: (_b = (_a = action.tile.block()) === null || _a === void 0 ? void 0 : _a.name) !== null && _b !== void 0 ? _b : "nothing", - }); - } - else if (action.type === Administration.ActionType.control && !((_c = action.unit) === null || _c === void 0 ? void 0 : _c.spawnedByCore) && Date.now() < fishP.blockedFromPossessingUnitsUntil) { - action.player.sendMessage("[scarlet]\u26A0 [yellow]You are blocked from controlling units for ".concat((0, utils_1.formatTimeRelative)(fishP.blockedFromPossessingUnitsUntil, true))); - return false; - } - else if (action.type === Administration.ActionType.commandUnits && Date.now() < fishP.blockedFromCommandingUnitsUntil) { - action.player.sendMessage("[scarlet]\u26A0 [yellow]You are blocked from commanding units for ".concat((0, utils_1.formatTimeRelative)(fishP.blockedFromCommandingUnitsUntil, true))); - return false; - } - else if (action.type === Administration.ActionType.pingLocation && action.pingText && action.pingText.length < Vars.maxPingTextLength) { - var fishP_1 = players_1.FishPlayer.get(action.player); - if (fishP_1.muted) { - action.player.sendMessage("[scarlet]\u26A0 [yellow]You are muted, you cannot send text through location pings."); - return false; - } - else if ((0, utils_1.matchFilter)(action.pingText, "chat", false)) { - //Allow it, but replace - player.pingX = action.pingX; - player.pingY = action.pingY; - player.pingTime = 1; - player.pingText = config_1.text.chatFilterReplacement.messageShort(); - return false; - } - } - return true; - } - }); - (0, aggregate_1.registerAll)(clientHandler, serverHandler); - (0, packetHandlers_1.loadPacketHandlers)(); - //Load plugin data - try { - var path = (0, utils_1.fishCommandsRootDirPath)(); - globals_1.fishPlugin.directory = path.toString(); - Threads.daemon(function () { - try { - globals_1.fishPlugin.version = OS.exec("git", "-C", globals_1.fishPlugin.directory, "rev-parse", "HEAD"); - } - catch (_a) { } - }); - } - catch (err) { - Log.err("Failed to get fish plugin information."); - Log.err(err); - } - Runtime.getRuntime().addShutdownHook(new Thread(function () { - try { - players_1.FishPlayer.uploadAll(); - } - catch (_a) { - Log.err("failed to upload"); - } - try { - globals_1.FishEvents.fire("saveData", []); - } - catch (_b) { - Log.err("failed to save misc data"); - } - try { - players_1.FishPlayer.saveAll(false); - } - catch (_c) { - Log.err("failed to save player data"); - } - Log.info("Saved on exit."); - })); - Vars.netServer.assigner = function (player, players) { - var _a; - if (Vars.state.rules.pvp) { - //find team with minimum amount of players and auto-assign player to that. - var fishP = players_1.FishPlayer.get(player); - var preferredTeam_1 = null; - if (fishP.restoreTeam && (Date.now() - fishP.restoreTeam[1] < funcs_1.Duration.minutes(5)) && fishP.restoreTeam[2] == ((_a = maps_1.PartialMapRun.current) === null || _a === void 0 ? void 0 : _a.startTime)) - preferredTeam_1 = fishP.restoreTeam[0]; - var re = Vars.state.teams.getActive().select(function (data) { return !((Vars.state.rules.waveTeam == data.team && Vars.state.rules.waves) || - !data.hasCore() || - data.team == Team.derelict || - !data.team.rules().protectCores); }).min(floatf(function (data) { - //Only if the team is valid - if (data.team == preferredTeam_1) - return -1; - var count = 0; - players.forEach(function (other) { - if (other.team() == data.team && other != player) { - count++; - } - }); - return count + Mathf.random(-0.1, 0.1); - })); - return re == null ? Vars.state.rules.defaultTeam : re.team; - } - else { - return Vars.state.rules.defaultTeam; - } - }; - Log.info("fish-commands: initialized in @ms (incl previous)", Time.elapsed()); -}); -// Keeps track of any action performed on a tile for use in tilelog. -Events.on(EventType.BlockBuildBeginEvent, utils_1.addToTileHistory); -Events.on(EventType.BuildRotateEvent, utils_1.addToTileHistory); -Events.on(EventType.ConfigEvent, utils_1.addToTileHistory); -Events.on(EventType.PickupEvent, utils_1.addToTileHistory); -Events.on(EventType.PayloadDropEvent, utils_1.addToTileHistory); -Events.on(EventType.UnitDestroyEvent, utils_1.addToTileHistory); -Events.on(EventType.BlockDestroyEvent, utils_1.addToTileHistory); -Events.on(EventType.UnitControlEvent, utils_1.addToTileHistory); -Events.on(EventType.TapEvent, commands_1.handleTapEvent); -Events.on(EventType.GameOverEvent, function (e) { - var e_1, _a; - try { - for (var _b = __values(Object.keys(globals_1.tileHistory)), _c = _b.next(); !_c.done; _c = _b.next()) { - var key = _c.value; - //clear tilelog - globals_1.tileHistory[key] = null; - delete globals_1.tileHistory[key]; - } - } - catch (e_1_1) { e_1 = { error: e_1_1 }; } - finally { - try { - if (_c && !_c.done && (_a = _b.return)) _a.call(_b); - } - finally { if (e_1) throw e_1.error; } - } - if (globals_1.fishState.restartQueued) { - //restart - Call.sendMessage("[accent]---[[[coral]+++[]]---\n[accent]Server restart imminent. [green]We'll be back after 15 seconds.[]\n[accent]---[[[coral]+++[]]---"); - (0, utils_1.serverRestartLoop)(12, true); - Events.on(EventType.WorldLoadBeginEvent, function () { - //Remove save - (0, utils_1.restartNow)(true); - }); - } - players_1.FishPlayer.onGameOver(e.winner); -}); -Events.on(EventType.WorldLoadEvent, function () { return players_1.FishPlayer.onGameBegin(); }); -Events.on(EventType.PlayerChatEvent, function (e) { - players_1.FishPlayer.onPlayerChat(e.player, e.message); -}); -Events.on(EventType.PlayEvent, function () { - globals_1.fishState.startTime = Date.now(); -}); -Log.info("fish-commands: parsing done in @ms", Date.now() - this._startTime); +"use strict"; +/* +Copyright © BalaM314, 2026. All Rights Reserved. +This file contains the main code, which calls other functions and initializes the plugin. +*/ +var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + var desc = Object.getOwnPropertyDescriptor(m, k); + if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { + desc = { enumerable: true, get: function() { return m[k]; } }; + } + Object.defineProperty(o, k2, desc); +}) : (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + o[k2] = m[k]; +})); +var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { + Object.defineProperty(o, "default", { enumerable: true, value: v }); +}) : function(o, v) { + o["default"] = v; +}); +var __importStar = (this && this.__importStar) || (function () { + var ownKeys = function(o) { + ownKeys = Object.getOwnPropertyNames || function (o) { + var ar = []; + for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k; + return ar; + }; + return ownKeys(o); + }; + return function (mod) { + if (mod && mod.__esModule) return mod; + var result = {}; + if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]); + __setModuleDefault(result, mod); + return result; + }; +})(); +var __values = (this && this.__values) || function(o) { + var s = typeof Symbol === "function" && Symbol.iterator, m = s && o[s], i = 0; + if (m) return m.call(o); + if (o && typeof o.length === "number") return { + next: function () { + if (o && i >= o.length) o = void 0; + return { value: o && o[i++], done: !o }; + } + }; + throw new TypeError(s ? "Object is not iterable." : "Symbol.iterator is not defined."); +}; +Object.defineProperty(exports, "__esModule", { value: true }); +var api = __importStar(require("/api")); +var aggregate_1 = require("/commands/aggregate"); +var config_1 = require("/config"); +var commands_1 = require("/frameworks/commands"); +var menus = __importStar(require("/frameworks/menus")); +var funcs_1 = require("/funcs"); +var globals_1 = require("/globals"); +var maps_1 = require("/maps"); +var packetHandlers_1 = require("/packetHandlers"); +var players_1 = require("/players"); +var timers = __importStar(require("/timers")); +var utils_1 = require("/utils"); +Events.on(EventType.ConnectionEvent, function (e) { + if (Vars.netServer.admins.bannedIPs.contains(e.connection.address)) { + api.getBanned({ + ip: e.connection.address, + }, function (banned) { + if (!banned) { + //If they were previously banned locally, but the API says they aren't banned, then unban them and clear the kick that the outer function already did + Vars.netServer.admins.unbanPlayerIP(e.connection.address); + Vars.netServer.admins.kickedIPs.remove(e.connection.address); + } + }); + } + else if (api.isVpnCached(e.connection.address) && players_1.FishPlayer.shouldWhackFlaggedPlayers()) { + Vars.netServer.admins.blacklistDos(e.connection.address); + e.connection.kick("You have been DOSblacklisted. Please join our discord for help: " + config_1.text.discordURL + "\nYou won't see this message again."); + Log.info("&yAntibot killed connection ".concat(e.connection.address, " due to flagged while under attack")); + } +}); +Events.on(EventType.PlayerConnect, function (e) { + if (players_1.FishPlayer.shouldKickNewPlayers() && e.player.info.timesJoined == 1) { + //do not use the helper function, for maximum performance + e.player.kick(Packets.KickReason.kick, 3600000); + } + players_1.FishPlayer.onPlayerConnect(e.player); +}); +Events.on(EventType.PlayerJoin, function (e) { + players_1.FishPlayer.onPlayerJoin(e.player); +}); +Events.on(EventType.PlayerLeave, function (e) { + players_1.FishPlayer.onPlayerLeave(e.player); +}); +Events.on(EventType.ConnectPacketEvent, function (e) { + if (!players_1.FishPlayer.connectRate.allow(5000, 35)) { + players_1.FishPlayer.triggerAntibot(300000, "Rate of player connections exceeded 35 / 5s", "automatic"); + } + globals_1.ipJoins.increment(e.connection.address); + var info = Vars.netServer.admins.getInfoOptional(e.packet.uuid); + var underAttack = players_1.FishPlayer.antiBotMode(); + var newPlayer = !info || info.timesJoined < 10; + var longModName = e.packet.mods.contains(function (str) { return str.length > 50; }); + var veryLongModName = e.packet.mods.contains(function (str) { return str.length > 100; }); + if ((underAttack && e.packet.mods.size > 2) || + (underAttack && longModName) || + (veryLongModName && (underAttack || newPlayer))) { + Vars.netServer.admins.blacklistDos(e.connection.address); + e.connection.kicked = true; + players_1.FishPlayer.triggerAntibot(60000, (veryLongModName ? "very long mod name" : longModName ? "long mod name" : "it had mods while under attack"), "automatic"); + return; + } + var suspiciousModName = e.packet.mods.contains(function (str) { return str.includes('\x1B'); }); + if (suspiciousModName || e.packet.name.includes('\x1B')) { + Vars.netServer.admins.blacklistDos(e.connection.address); + e.connection.kicked = true; + players_1.FishPlayer.triggerAntibot(5000, "illegal characters in name or mods", "automatic"); + return; + } + if (globals_1.ipJoins.get(e.connection.address) >= ((underAttack || veryLongModName) ? 3 : (newPlayer || longModName) ? 7 : 15)) { + Vars.netServer.admins.blacklistDos(e.connection.address); + e.connection.kicked = true; + players_1.FishPlayer.triggerAntibot(5000, "too many connections", "automatic"); + return; + } + /*if(e.packet.name.includes("discord.gg/GnEdS9TdV6")){ + Vars.netServer.admins.blacklistDos(e.connection.address); + e.connection.kicked = true; + FishPlayer.onBotWhack(); + Log.info(`&yAntibot killed connection ${e.connection.address} due to omni discord link`); + return; + }*/ + if (e.packet.name.includes("1`1@everyone")) { + Vars.netServer.admins.blacklistDos(e.connection.address); + e.connection.kicked = true; + players_1.FishPlayer.triggerAntibot(-1, "known bad name", "automatic"); + return; + } + if (Vars.netServer.admins.isDosBlacklisted(e.connection.address)) { + //threading moment, i think + e.connection.kicked = true; + return; + } + api.getBanned({ + ip: e.connection.address, + uuid: e.packet.uuid + }, function (banned) { + if (banned) { + Log.info("&lrSynced ban of ".concat(e.packet.uuid, "/").concat(e.connection.address, ".")); + e.connection.kick(Packets.KickReason.banned, 1); + Vars.netServer.admins.banPlayerIP(e.connection.address); + Vars.netServer.admins.banPlayerID(e.packet.uuid); + } + else { + Vars.netServer.admins.unbanPlayerIP(e.connection.address); + Vars.netServer.admins.unbanPlayerID(e.packet.uuid); + } + }); + players_1.FishPlayer.onConnectPacket(e.packet); +}); +Events.on(EventType.UnitChangeEvent, function (e) { + players_1.FishPlayer.onUnitChange(e.player, e.unit); +}); +Events.on(EventType.ContentInitEvent, function () { + //Unhide latum and renale + UnitTypes.latum.hidden = false; + UnitTypes.renale.hidden = false; +}); +Events.on(EventType.PlayerChatEvent, function (e) { return (0, utils_1.processChat)(e.player, e.message, true); }); +Events.on(EventType.ServerLoadEvent, function (e) { + Time.mark(); + var clientHandler = Vars.netServer.clientCommands; + var serverHandler = ServerControl.instance.handler; + players_1.FishPlayer.loadAll(); + globals_1.FishEvents.fire("loadData", []); + timers.initializeTimers(); + menus.registerListeners(); + //Cap delta + Time.setDeltaProvider(function () { return Math.min(Core.graphics.getDeltaTime() * 60, 10); }); + // Mute muted players + Vars.netServer.admins.addChatFilter(function (player, message) { return (0, utils_1.processChat)(player, message); }); + // Vars.netServer.admins.addChatFilter((p, message) => FishPlayer.get(p).hasPerm("member") ? message : foolifyChat(message)); + // Action filters + Vars.netServer.admins.addActionFilter(function (action) { + var _a, _b, _c; + var player = action.player; + var fishP = players_1.FishPlayer.get(player); + //prevent stopped players from doing anything other than deposit items. + if (!fishP.hasPerm("play")) { + action.player.sendMessage('[scarlet]\u26A0 [yellow]You are stopped, you cant perfom this action.'); + return false; + } + else { + if (action.type === Administration.ActionType.pickupBlock) { + (0, utils_1.addToTileHistory)({ + pos: "".concat(action.tile.x, ",").concat(action.tile.y), + uuid: action.player.uuid(), + action: "picked up", + type: (_b = (_a = action.tile.block()) === null || _a === void 0 ? void 0 : _a.name) !== null && _b !== void 0 ? _b : "nothing", + }); + } + else if (action.type === Administration.ActionType.control && !((_c = action.unit) === null || _c === void 0 ? void 0 : _c.spawnedByCore) && Date.now() < fishP.blockedFromPossessingUnitsUntil) { + action.player.sendMessage("[scarlet]\u26A0 [yellow]You are blocked from controlling units for ".concat((0, utils_1.formatTimeRelative)(fishP.blockedFromPossessingUnitsUntil, true))); + return false; + } + else if (action.type === Administration.ActionType.commandUnits && Date.now() < fishP.blockedFromCommandingUnitsUntil) { + action.player.sendMessage("[scarlet]\u26A0 [yellow]You are blocked from commanding units for ".concat((0, utils_1.formatTimeRelative)(fishP.blockedFromCommandingUnitsUntil, true))); + return false; + } + else if (action.type === Administration.ActionType.pingLocation && action.pingText && action.pingText.length < Vars.maxPingTextLength) { + var fishP_1 = players_1.FishPlayer.get(action.player); + if (fishP_1.muted) { + action.player.sendMessage("[scarlet]\u26A0 [yellow]You are muted, you cannot send text through location pings."); + return false; + } + else if ((0, utils_1.matchFilter)(action.pingText, "chat", false)) { + //Allow it, but replace + player.pingX = action.pingX; + player.pingY = action.pingY; + player.pingTime = 1; + player.pingText = config_1.text.chatFilterReplacement.messageShort(); + return false; + } + } + return true; + } + }); + (0, aggregate_1.registerAll)(clientHandler, serverHandler); + (0, packetHandlers_1.loadPacketHandlers)(); + //Load plugin data + try { + var path = (0, utils_1.fishCommandsRootDirPath)(); + globals_1.fishPlugin.directory = path.toString(); + Threads.daemon(function () { + try { + globals_1.fishPlugin.version = OS.exec("git", "-C", globals_1.fishPlugin.directory, "rev-parse", "HEAD"); + } + catch (_a) { } + }); + } + catch (err) { + Log.err("Failed to get fish plugin information."); + Log.err(err); + } + Runtime.getRuntime().addShutdownHook(new Thread(function () { + try { + players_1.FishPlayer.uploadAll(); + } + catch (_a) { + Log.err("failed to upload"); + } + try { + globals_1.FishEvents.fire("saveData", []); + } + catch (_b) { + Log.err("failed to save misc data"); + } + try { + players_1.FishPlayer.saveAll(false); + } + catch (_c) { + Log.err("failed to save player data"); + } + Log.info("Saved on exit."); + })); + Vars.netServer.assigner = function (player, players) { + var _a; + if (Vars.state.rules.pvp) { + //find team with minimum amount of players and auto-assign player to that. + var fishP = players_1.FishPlayer.get(player); + var preferredTeam_1 = null; + if (fishP.restoreTeam && (Date.now() - fishP.restoreTeam[1] < funcs_1.Duration.minutes(5)) && fishP.restoreTeam[2] == ((_a = maps_1.PartialMapRun.current) === null || _a === void 0 ? void 0 : _a.startTime)) + preferredTeam_1 = fishP.restoreTeam[0]; + var re = Vars.state.teams.getActive().select(function (data) { return !((Vars.state.rules.waveTeam == data.team && Vars.state.rules.waves) || + !data.hasCore() || + data.team == Team.derelict || + !data.team.rules().protectCores); }).min(floatf(function (data) { + //Only if the team is valid + if (data.team == preferredTeam_1) + return -1; + var count = 0; + players.forEach(function (other) { + if (other.team() == data.team && other != player) { + count++; + } + }); + return count + Mathf.random(-0.1, 0.1); + })); + return re == null ? Vars.state.rules.defaultTeam : re.team; + } + else { + return Vars.state.rules.defaultTeam; + } + }; + Log.info("fish-commands: initialized in @ms (incl previous)", Time.elapsed()); +}); +// Keeps track of any action performed on a tile for use in tilelog. +Events.on(EventType.BlockBuildBeginEvent, utils_1.addToTileHistory); +Events.on(EventType.BuildRotateEvent, utils_1.addToTileHistory); +Events.on(EventType.ConfigEvent, utils_1.addToTileHistory); +Events.on(EventType.PickupEvent, utils_1.addToTileHistory); +Events.on(EventType.PayloadDropEvent, utils_1.addToTileHistory); +Events.on(EventType.UnitDestroyEvent, utils_1.addToTileHistory); +Events.on(EventType.BlockDestroyEvent, utils_1.addToTileHistory); +Events.on(EventType.UnitControlEvent, utils_1.addToTileHistory); +Events.on(EventType.TapEvent, commands_1.handleTapEvent); +Events.on(EventType.GameOverEvent, function (e) { + var e_1, _a; + try { + for (var _b = __values(Object.keys(globals_1.tileHistory)), _c = _b.next(); !_c.done; _c = _b.next()) { + var key = _c.value; + //clear tilelog + globals_1.tileHistory[key] = null; + delete globals_1.tileHistory[key]; + } + } + catch (e_1_1) { e_1 = { error: e_1_1 }; } + finally { + try { + if (_c && !_c.done && (_a = _b.return)) _a.call(_b); + } + finally { if (e_1) throw e_1.error; } + } + if (globals_1.fishState.restartQueued) { + //restart + Call.sendMessage("[accent]---[[[coral]+++[]]---\n[accent]Server restart imminent. [green]We'll be back after 15 seconds.[]\n[accent]---[[[coral]+++[]]---"); + (0, utils_1.serverRestartLoop)(12, true); + Events.on(EventType.WorldLoadBeginEvent, function () { + //Remove save + (0, utils_1.restartNow)(true); + }); + } + players_1.FishPlayer.onGameOver(e.winner); +}); +Events.on(EventType.WorldLoadEvent, function () { return players_1.FishPlayer.onGameBegin(); }); +Events.on(EventType.PlayerChatEvent, function (e) { + players_1.FishPlayer.onPlayerChat(e.player, e.message); +}); +Events.on(EventType.PlayEvent, function () { + globals_1.fishState.startTime = Date.now(); +}); +Log.info("fish-commands: parsing done in @ms", Date.now() - this._startTime); diff --git a/build/scripts/maps.js b/build/scripts/maps.js index 3cc33473..b9f770de 100644 --- a/build/scripts/maps.js +++ b/build/scripts/maps.js @@ -1,361 +1,361 @@ -"use strict"; -/* -Copyright © BalaM314, 2026. All Rights Reserved. -This file contains the map run tracker and statistics computation. -*/ -var __extends = (this && this.__extends) || (function () { - var extendStatics = function (d, b) { - extendStatics = Object.setPrototypeOf || - ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || - function (d, b) { for (var p in b) if (Object.prototype.hasOwnProperty.call(b, p)) d[p] = b[p]; }; - return extendStatics(d, b); - }; - return function (d, b) { - if (typeof b !== "function" && b !== null) - throw new TypeError("Class extends value " + String(b) + " is not a constructor or null"); - extendStatics(d, b); - function __() { this.constructor = d; } - d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); - }; -})(); -var __esDecorate = (this && this.__esDecorate) || function (ctor, descriptorIn, decorators, contextIn, initializers, extraInitializers) { - function accept(f) { if (f !== void 0 && typeof f !== "function") throw new TypeError("Function expected"); return f; } - var kind = contextIn.kind, key = kind === "getter" ? "get" : kind === "setter" ? "set" : "value"; - var target = !descriptorIn && ctor ? contextIn["static"] ? ctor : ctor.prototype : null; - var descriptor = descriptorIn || (target ? Object.getOwnPropertyDescriptor(target, contextIn.name) : {}); - var _, done = false; - for (var i = decorators.length - 1; i >= 0; i--) { - var context = {}; - for (var p in contextIn) context[p] = p === "access" ? {} : contextIn[p]; - for (var p in contextIn.access) context.access[p] = contextIn.access[p]; - context.addInitializer = function (f) { if (done) throw new TypeError("Cannot add initializers after decoration has completed"); extraInitializers.push(accept(f || null)); }; - var result = (0, decorators[i])(kind === "accessor" ? { get: descriptor.get, set: descriptor.set } : descriptor[key], context); - if (kind === "accessor") { - if (result === void 0) continue; - if (result === null || typeof result !== "object") throw new TypeError("Object expected"); - if (_ = accept(result.get)) descriptor.get = _; - if (_ = accept(result.set)) descriptor.set = _; - if (_ = accept(result.init)) initializers.unshift(_); - } - else if (_ = accept(result)) { - if (kind === "field") initializers.unshift(_); - else descriptor[key] = _; - } - } - if (target) Object.defineProperty(target, contextIn.name, descriptor); - done = true; -}; -var __runInitializers = (this && this.__runInitializers) || function (thisArg, initializers, value) { - var useValue = arguments.length > 2; - for (var i = 0; i < initializers.length; i++) { - value = useValue ? initializers[i].call(thisArg, value) : initializers[i].call(thisArg); - } - return useValue ? value : void 0; -}; -var __read = (this && this.__read) || function (o, n) { - var m = typeof Symbol === "function" && o[Symbol.iterator]; - if (!m) return o; - var i = m.call(o), r, ar = [], e; - try { - while ((n === void 0 || n-- > 0) && !(r = i.next()).done) ar.push(r.value); - } - catch (error) { e = { error: error }; } - finally { - try { - if (r && !r.done && (m = i["return"])) m.call(i); - } - finally { if (e) throw e.error; } - } - return ar; -}; -var _this = this; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.FMap = exports.PartialMapRun = exports.FinishedMapRun = void 0; -var config_1 = require("/config"); -var io_1 = require("/frameworks/io"); -var funcs_1 = require("/funcs"); -var globals_1 = require("/globals"); -var utils_1 = require("/utils"); -var FinishedMapRun = /** @class */ (function (_super) { - __extends(FinishedMapRun, _super); - //this constructor is useless, but rhino crashes with a bizarre error when trying to run the emitted code - //do not remove this useless constructor - function FinishedMapRun(data) { - return _super.call(this, data) || this; - } - FinishedMapRun.prototype.duration = function () { - return this.endTime - this.startTime; - }; - FinishedMapRun.prototype.outcome = function () { - if (config_1.Gamemode.pvp()) { - if (this.winTeam === Team.derelict) { - if (this.duration() > funcs_1.Duration.minutes(20)) - return ["rtv", "late rtv"]; - else - return ["rtv", "early rtv"]; - } - else - return ["win", "win"]; - } - else { - if (this.success) - return ["win", "win"]; - else if (this.winTeam === Team.derelict) { - if (this.duration() > funcs_1.Duration.minutes(3)) - return ["loss", "late rtv"]; - else - return ["rtv", "early rtv"]; - } - else - return ["loss", "loss"]; - } - }; - return FinishedMapRun; -}((0, io_1.dataClass)())); -exports.FinishedMapRun = FinishedMapRun; -var PartialMapRun = /** @class */ (function () { - function PartialMapRun() { - this.startTime = Date.now(); - this.maxPlayerCount = 0; - } - /** In milliseconds */ - PartialMapRun.prototype.duration = function () { - return Date.now() - this.startTime; - }; - PartialMapRun.prototype.update = function () { - this.maxPlayerCount = Math.max(this.maxPlayerCount, Groups.player.size()); - }; - PartialMapRun.prototype.finish = function (_b) { - var winTeam = _b.winTeam; - return new FinishedMapRun({ - winTeam: winTeam, - success: config_1.Gamemode.pvp() ? true : winTeam == Vars.state.rules.defaultTeam, - startTime: this.startTime, - endTime: Date.now(), - maxPlayerCount: this.maxPlayerCount, - wave: Vars.state.wave, - }); - }; - //Used for continuing through a restart - PartialMapRun.prototype.write = function () { - return "".concat(Date.now() - this.startTime, "/").concat(this.maxPlayerCount); - }; - PartialMapRun.read = function (data) { - var _b = __read(data.split("/").map(Number), 2), duration = _b[0], maxPlayerCount = _b[1]; - if (isNaN(duration) || isNaN(maxPlayerCount)) { - Log.err("_FINDTAG_ failed to load map run stats data: ".concat(data)); - } - var out = new _a(); - out.startTime = Date.now() - duration; //move start time forward by time when the server was off - out.maxPlayerCount = maxPlayerCount; - return out; - }; - var _a; - _a = PartialMapRun; - PartialMapRun.key = "fish-partial-map-run"; - PartialMapRun.current = null; - (function () { - globals_1.FishEvents.on("saveData", function () { - if (_a.current) - Core.settings.put(_a.key, _a.current.write()); - }); - globals_1.FishEvents.on("loadData", function () { - var data = Core.settings.getString(_a.key); - if (data) { - _a.current = _a.read(data); - } - else { - //loading a map, but there is no run information, create one - _a.current = new _a(); - } - }); - Events.on(EventType.SaveLoadEvent, function (e) { - var _b; - //create a new run, if there isn't one already - //loadData will have run first if it is a server restart - (_b = _a.current) !== null && _b !== void 0 ? _b : (_a.current = new _a()); - }); - Timer.schedule(function () { - var _b; - (_b = _a.current) === null || _b === void 0 ? void 0 : _b.update(); - }, 0, 5); - Events.on(EventType.GameOverEvent, function (e) { - var _b; - if (_a.current) { - var finishedRun = _a.current.finish({ winTeam: (_b = e.winner) !== null && _b !== void 0 ? _b : Team.derelict }); - var fmap = FMap.getCreate(Vars.state.map); - if (!fmap) - return; - //Highscore message - if (config_1.Gamemode.attack() && finishedRun.success) { - var bestPreviousTime = fmap.stats().shortestWinTime; - var duration = finishedRun.duration(); - Call.sendMessage("[orange]--------\n".concat(finishedRun.success && duration < bestPreviousTime ? - "[green]New highscore! Map completed in [accent]".concat((0, utils_1.formatTimeShort)(duration), "[]") - : "[orange]Map completed in [accent]".concat((0, utils_1.formatTimeShort)(duration), "[]. Current highscore: [green]").concat((0, utils_1.formatTimeShort)(bestPreviousTime), "[]"), "\n[orange]--------")); - } - else if (config_1.Gamemode.survival()) { - var bestPreviousWave = fmap.stats().highestWave; - var wave = finishedRun.wave; - Call.sendMessage("[orange]--------\n".concat(finishedRun.success && wave < bestPreviousWave ? - "[green]New highscore! Reached wave [accent]".concat(wave, "[].") - : "[orange]Reached wave [accent]".concat(wave, "[]. Current highscore: [green]").concat(bestPreviousWave, "[]"), "\n[orange]--------")); - } - fmap.runs.push(finishedRun); - globals_1.FishEvents.fire("saveMaps", []); - } - Core.settings.remove(_a.key); - _a.current = null; - }); - })(); - return PartialMapRun; -}()); -exports.PartialMapRun = PartialMapRun; -var FMap = function () { - var _b; - var _classSuper = (0, io_1.dataClass)(); - var _static_allMaps_decorators; - var _static_allMaps_initializers = []; - var _static_allMaps_extraInitializers = []; - return _b = /** @class */ (function (_super) { - __extends(FMap, _super); - function FMap(data, - //O(n^2)... should be fine? - map) { - if (map === void 0) { map = Vars.maps.customMaps().find(function (m) { return m.file.name() === data.mapFileName; }); } - var _this = _super.call(this, data) || this; - _this.map = map; - return _this; - } - FMap.getCreate = function (map) { - if (this.allMaps == null) - return null; - var mapFileName = map.file.name(); - if (Object.prototype.hasOwnProperty.call(this.maps, mapFileName)) - return this.maps[mapFileName]; - var fmap = new this({ - runs: [], - mapFileName: mapFileName - }, map); - this.maps[mapFileName] = fmap; - this.allMaps.push(fmap); - return fmap; - }; - FMap.prototype.rules = function () { - var _c; - return (_c = this.map) === null || _c === void 0 ? void 0 : _c.rules(); - }; - FMap.prototype.stats = function () { - var _c; - var runs = this.runs.filter(function (r) { return r.maxPlayerCount > 0; }); //Remove all runs with no players on - var allRunCount = runs.length; - var victories = runs.filter(function (r) { return r.outcome()[1] === "win"; }); - var losses = runs.filter(function (r) { return r.outcome()[0] === "loss"; }).length; - var earlyRTVs = runs.filter(function (r) { return r.outcome()[1] === "early rtv"; }).length; - var lateRTVs = runs.filter(function (r) { return r.outcome()[1] === "late rtv"; }).length; - var significantRunCount = allRunCount - earlyRTVs; - var totalLosses = losses + lateRTVs; - var durations = runs.filter(function (r) { return r.outcome()[0] !== "rtv"; }).map(function (r) { return r.duration(); }); - var durationStats = (0, funcs_1.computeStatistics)(durations); - var winDurationStats = (0, funcs_1.computeStatistics)(runs.filter(function (r) { return r.outcome()[0] === "win"; }).map(function (r) { return r.duration(); })); - var teamWins = runs.filter(function (r) { return r.outcome()[1] !== "early rtv"; }).reduce(function (acc, item) { - var _c; - acc[item.winTeam.name] = ((_c = acc[item.winTeam.name]) !== null && _c !== void 0 ? _c : 0) + 1; - return acc; - }, {}); - var teamWinRate = Object.fromEntries(Object.entries(teamWins).map(function (_c) { - var _d = __read(_c, 2), team = _d[0], wins = _d[1]; - return [team, wins / significantRunCount]; - })); - //Remove runs that were on wave 0, due to a silly bug we have thousands of runs with a max wave of 0 - var waveStats = (0, funcs_1.computeStatistics)(runs.filter(function (r) { return r.outcome()[0] !== "rtv" && r.wave !== 0; }).map(function (r) { return r.wave; })); - return { - allRunCount: allRunCount, - significantRunCount: significantRunCount, - victories: victories.length, - losses: losses, - totalLosses: totalLosses, - earlyRTVs: earlyRTVs, - lateRTVs: lateRTVs, - earlyRTVRate: earlyRTVs / allRunCount, - winRate: victories.length / significantRunCount, - lossRate: losses / significantRunCount, - averagePlaytime: durationStats.average, - shortestWinTime: winDurationStats.lowest, - longestTime: durationStats.highest, - shortestTime: durationStats.lowest, - averageHighestPlayerCount: (0, funcs_1.computeStatistics)(runs.map(function (r) { return r.maxPlayerCount; })).average, - teamWins: teamWins, - teamWinRate: teamWinRate, - highestWave: waveStats.highest, - averageWave: waveStats.average, - mostRecentWin: (_c = victories.at(-1)) === null || _c === void 0 ? void 0 : _c.startTime - }; - }; - FMap.prototype.displayStats = function (f) { - var map = this.map; - if (!map) - return null; - var stats = this.stats(); - var rules = this.rules(); - var modeSpecificStats = (0, utils_1.match)(config_1.Gamemode.name(), { - attack: "[#CCFFCC]Total runs: ".concat(stats.allRunCount, " (").concat(stats.victories, " wins, ").concat(stats.totalLosses, " losses, ").concat(stats.earlyRTVs, " RTVs)\n[#CCFFCC]Outcomes: ").concat(f.percent(stats.winRate, 1), " wins, ").concat(f.percent(stats.lossRate, 1), " losses, ").concat(f.percent(stats.earlyRTVRate, 1), " RTVs\n[#CCFFCC]Average playtime: ").concat((0, utils_1.formatTime)(stats.averagePlaytime), "\n[#CCFFCC]Shortest win time: ").concat((0, utils_1.formatTime)(stats.shortestWinTime), "\n[#CCFFCC]Most recent win: ").concat(stats.mostRecentWin ? (0, utils_1.formatTimestamp)(stats.mostRecentWin) : "[red]none[]"), - survival: "[#CCFFCC]Highest wave reached: ".concat(stats.highestWave, "\n[#CCFFCC]Average wave reached: ").concat(stats.averageWave, "\n[#CCFFCC]Total runs: ").concat(stats.allRunCount, " (").concat(stats.earlyRTVs, " RTVs)\n[#CCFFCC]RTV rate: ").concat(f.percent(stats.earlyRTVRate, 1), "\n[#CCFFCC]Average duration: ").concat((0, utils_1.formatTime)(stats.averagePlaytime), "\n[#CCFFCC]Longest duration: ").concat((0, utils_1.formatTime)(stats.longestTime)), - pvp: "[#CCFFCC]Total runs: ".concat(stats.allRunCount, " (").concat(stats.earlyRTVs, " RTVs)\n[#CCFFCC]Team win rates: ").concat(Object.entries(stats.teamWinRate).map(function (_c) { - var _d = __read(_c, 2), team = _d[0], rate = _d[1]; - return "".concat(team, " ").concat(f.percent(rate, 1)); - }).join(", "), "\n[#CCFFCC]RTV rate: ").concat(f.percent(stats.earlyRTVRate, 1), "\n[#CCFFCC]Average match duration: ").concat((0, utils_1.formatTime)(stats.averagePlaytime), "\n[#CCFFCC]Shortest match duration: ").concat((0, utils_1.formatTime)(stats.shortestWinTime)), - hexed: "[#CCFFCC]Total runs: ".concat(stats.allRunCount, " (").concat(stats.earlyRTVs, " RTVs)\n[#CCFFCC]RTV rate: ").concat(f.percent(stats.earlyRTVRate, 1), "\n[#CCFFCC]Average match duration: ").concat((0, utils_1.formatTime)(stats.averagePlaytime), "\n[#CCFFCC]Shortest match duration: ").concat((0, utils_1.formatTime)(stats.shortestWinTime)), - sandbox: "[#CCFFCC]Total plays: ".concat(stats.allRunCount, "\n[#CCFFCC]Average play time: ").concat((0, utils_1.formatTime)(stats.averagePlaytime), "\n[#CCFFCC]Shortest play time: ").concat((0, utils_1.formatTime)(stats.shortestTime)), - }, ""); - return ("[coral]".concat(map.name(), "\n[gray](").concat(map.file.name(), ")\n\n[accent]Map by: [white]").concat(map.author(), "\n[accent]Description: [white]").concat(map.description(), "\n[accent]Size: [white]").concat(map.width, "x").concat(map.height, "\n[accent]Last updated: [white]").concat(new Date(map.file.lastModified()).toLocaleDateString(), "\n[accent]BvB allowed: ").concat(f.boolGood(rules.placeRangeCheck), ", unit item transfer allowed: ").concat(f.boolGood(rules.onlyDepositCore), "\n\n").concat(modeSpecificStats, "\n[#CCFFCC]Longest play time: ").concat((0, utils_1.formatTime)(stats.longestTime), "\n[#CCFFCC]Average player count: ").concat(f.number(stats.averageHighestPlayerCount, 1))); - }; - return FMap; - }(_classSuper)), - (function () { - var _c; - var _metadata = typeof Symbol === "function" && Symbol.metadata ? Object.create((_c = _classSuper[Symbol.metadata]) !== null && _c !== void 0 ? _c : null) : void 0; - _static_allMaps_decorators = [(0, io_1.serialize)("fish-map-data", function () { return ["version", 1, ["array", "u16", ["class", FMap, [ - ["runs", ["array", "u32", ["class", FinishedMapRun, [ - ["startTime", ["number", "i64"]], - ["endTime", ["number", "i64"]], - ["maxPlayerCount", ["number", "u8"]], - ["success", ["boolean"]], - ["winTeam", ["team"]], - ["wave", ["number", "u16"]] - ]]]], - ["mapFileName", ["string"]], - ]]]]; }, function () { return ["array", "u16", ["class", FMap, [ - ["runs", ["array", "u32", ["class", FinishedMapRun, [ - ["startTime", ["number", "i64"]], - ["endTime", ["number", "i64"]], - ["maxPlayerCount", ["number", "u8"]], - ["success", ["boolean"]], - ["winTeam", ["team"]], - ]]]], - ["mapFileName", ["string"]], - ]]]; }, undefined, "saveMaps")]; - __esDecorate(null, null, _static_allMaps_decorators, { kind: "field", name: "allMaps", static: true, private: false, access: { has: function (obj) { return "allMaps" in obj; }, get: function (obj) { return obj.allMaps; }, set: function (obj, value) { obj.allMaps = value; } }, metadata: _metadata }, _static_allMaps_initializers, _static_allMaps_extraInitializers); - if (_metadata) Object.defineProperty(_b, Symbol.metadata, { enumerable: true, configurable: true, writable: true, value: _metadata }); - })(), - _b.allMaps = __runInitializers(_b, _static_allMaps_initializers, null), - _b.maps = (__runInitializers(_b, _static_allMaps_extraInitializers), {}), - (function () { - globals_1.FishEvents.on("dataLoaded", function () { - var _c; - //This event listener runs after the data has been loaded into allMaps - ((_c = _b.allMaps) !== null && _c !== void 0 ? _c : (_b.allMaps = [])).forEach(function (map) { - _b.maps[map.mapFileName] = map; - map.runs.forEach(function (run) { - var _c; - //this should not even happen, I think GameOverEvent is sending winTeam as null sometimes?? - (_c = run.winTeam) !== null && _c !== void 0 ? _c : (run.winTeam = Team.derelict); - }); - }); - //create all the data - Vars.maps.customMaps().each(function (m) { return void _b.getCreate(m); }); - }); - })(), - _b; -}(); -exports.FMap = FMap; +"use strict"; +/* +Copyright © BalaM314, 2026. All Rights Reserved. +This file contains the map run tracker and statistics computation. +*/ +var __extends = (this && this.__extends) || (function () { + var extendStatics = function (d, b) { + extendStatics = Object.setPrototypeOf || + ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || + function (d, b) { for (var p in b) if (Object.prototype.hasOwnProperty.call(b, p)) d[p] = b[p]; }; + return extendStatics(d, b); + }; + return function (d, b) { + if (typeof b !== "function" && b !== null) + throw new TypeError("Class extends value " + String(b) + " is not a constructor or null"); + extendStatics(d, b); + function __() { this.constructor = d; } + d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); + }; +})(); +var __esDecorate = (this && this.__esDecorate) || function (ctor, descriptorIn, decorators, contextIn, initializers, extraInitializers) { + function accept(f) { if (f !== void 0 && typeof f !== "function") throw new TypeError("Function expected"); return f; } + var kind = contextIn.kind, key = kind === "getter" ? "get" : kind === "setter" ? "set" : "value"; + var target = !descriptorIn && ctor ? contextIn["static"] ? ctor : ctor.prototype : null; + var descriptor = descriptorIn || (target ? Object.getOwnPropertyDescriptor(target, contextIn.name) : {}); + var _, done = false; + for (var i = decorators.length - 1; i >= 0; i--) { + var context = {}; + for (var p in contextIn) context[p] = p === "access" ? {} : contextIn[p]; + for (var p in contextIn.access) context.access[p] = contextIn.access[p]; + context.addInitializer = function (f) { if (done) throw new TypeError("Cannot add initializers after decoration has completed"); extraInitializers.push(accept(f || null)); }; + var result = (0, decorators[i])(kind === "accessor" ? { get: descriptor.get, set: descriptor.set } : descriptor[key], context); + if (kind === "accessor") { + if (result === void 0) continue; + if (result === null || typeof result !== "object") throw new TypeError("Object expected"); + if (_ = accept(result.get)) descriptor.get = _; + if (_ = accept(result.set)) descriptor.set = _; + if (_ = accept(result.init)) initializers.unshift(_); + } + else if (_ = accept(result)) { + if (kind === "field") initializers.unshift(_); + else descriptor[key] = _; + } + } + if (target) Object.defineProperty(target, contextIn.name, descriptor); + done = true; +}; +var __runInitializers = (this && this.__runInitializers) || function (thisArg, initializers, value) { + var useValue = arguments.length > 2; + for (var i = 0; i < initializers.length; i++) { + value = useValue ? initializers[i].call(thisArg, value) : initializers[i].call(thisArg); + } + return useValue ? value : void 0; +}; +var __read = (this && this.__read) || function (o, n) { + var m = typeof Symbol === "function" && o[Symbol.iterator]; + if (!m) return o; + var i = m.call(o), r, ar = [], e; + try { + while ((n === void 0 || n-- > 0) && !(r = i.next()).done) ar.push(r.value); + } + catch (error) { e = { error: error }; } + finally { + try { + if (r && !r.done && (m = i["return"])) m.call(i); + } + finally { if (e) throw e.error; } + } + return ar; +}; +var _this = this; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.FMap = exports.PartialMapRun = exports.FinishedMapRun = void 0; +var config_1 = require("/config"); +var io_1 = require("/frameworks/io"); +var funcs_1 = require("/funcs"); +var globals_1 = require("/globals"); +var utils_1 = require("/utils"); +var FinishedMapRun = /** @class */ (function (_super) { + __extends(FinishedMapRun, _super); + //this constructor is useless, but rhino crashes with a bizarre error when trying to run the emitted code + //do not remove this useless constructor + function FinishedMapRun(data) { + return _super.call(this, data) || this; + } + FinishedMapRun.prototype.duration = function () { + return this.endTime - this.startTime; + }; + FinishedMapRun.prototype.outcome = function () { + if (config_1.Gamemode.pvp()) { + if (this.winTeam === Team.derelict) { + if (this.duration() > funcs_1.Duration.minutes(20)) + return ["rtv", "late rtv"]; + else + return ["rtv", "early rtv"]; + } + else + return ["win", "win"]; + } + else { + if (this.success) + return ["win", "win"]; + else if (this.winTeam === Team.derelict) { + if (this.duration() > funcs_1.Duration.minutes(3)) + return ["loss", "late rtv"]; + else + return ["rtv", "early rtv"]; + } + else + return ["loss", "loss"]; + } + }; + return FinishedMapRun; +}((0, io_1.dataClass)())); +exports.FinishedMapRun = FinishedMapRun; +var PartialMapRun = /** @class */ (function () { + function PartialMapRun() { + this.startTime = Date.now(); + this.maxPlayerCount = 0; + } + /** In milliseconds */ + PartialMapRun.prototype.duration = function () { + return Date.now() - this.startTime; + }; + PartialMapRun.prototype.update = function () { + this.maxPlayerCount = Math.max(this.maxPlayerCount, Groups.player.size()); + }; + PartialMapRun.prototype.finish = function (_b) { + var winTeam = _b.winTeam; + return new FinishedMapRun({ + winTeam: winTeam, + success: config_1.Gamemode.pvp() ? true : winTeam == Vars.state.rules.defaultTeam, + startTime: this.startTime, + endTime: Date.now(), + maxPlayerCount: this.maxPlayerCount, + wave: Vars.state.wave, + }); + }; + //Used for continuing through a restart + PartialMapRun.prototype.write = function () { + return "".concat(Date.now() - this.startTime, "/").concat(this.maxPlayerCount); + }; + PartialMapRun.read = function (data) { + var _b = __read(data.split("/").map(Number), 2), duration = _b[0], maxPlayerCount = _b[1]; + if (isNaN(duration) || isNaN(maxPlayerCount)) { + Log.err("_FINDTAG_ failed to load map run stats data: ".concat(data)); + } + var out = new _a(); + out.startTime = Date.now() - duration; //move start time forward by time when the server was off + out.maxPlayerCount = maxPlayerCount; + return out; + }; + var _a; + _a = PartialMapRun; + PartialMapRun.key = "fish-partial-map-run"; + PartialMapRun.current = null; + (function () { + globals_1.FishEvents.on("saveData", function () { + if (_a.current) + Core.settings.put(_a.key, _a.current.write()); + }); + globals_1.FishEvents.on("loadData", function () { + var data = Core.settings.getString(_a.key); + if (data) { + _a.current = _a.read(data); + } + else { + //loading a map, but there is no run information, create one + _a.current = new _a(); + } + }); + Events.on(EventType.SaveLoadEvent, function (e) { + var _b; + //create a new run, if there isn't one already + //loadData will have run first if it is a server restart + (_b = _a.current) !== null && _b !== void 0 ? _b : (_a.current = new _a()); + }); + Timer.schedule(function () { + var _b; + (_b = _a.current) === null || _b === void 0 ? void 0 : _b.update(); + }, 0, 5); + Events.on(EventType.GameOverEvent, function (e) { + var _b; + if (_a.current) { + var finishedRun = _a.current.finish({ winTeam: (_b = e.winner) !== null && _b !== void 0 ? _b : Team.derelict }); + var fmap = FMap.getCreate(Vars.state.map); + if (!fmap) + return; + //Highscore message + if (config_1.Gamemode.attack() && finishedRun.success) { + var bestPreviousTime = fmap.stats().shortestWinTime; + var duration = finishedRun.duration(); + Call.sendMessage("[orange]--------\n".concat(finishedRun.success && duration < bestPreviousTime ? + "[green]New highscore! Map completed in [accent]".concat((0, utils_1.formatTimeShort)(duration), "[]") + : "[orange]Map completed in [accent]".concat((0, utils_1.formatTimeShort)(duration), "[]. Current highscore: [green]").concat((0, utils_1.formatTimeShort)(bestPreviousTime), "[]"), "\n[orange]--------")); + } + else if (config_1.Gamemode.survival()) { + var bestPreviousWave = fmap.stats().highestWave; + var wave = finishedRun.wave; + Call.sendMessage("[orange]--------\n".concat(finishedRun.success && wave < bestPreviousWave ? + "[green]New highscore! Reached wave [accent]".concat(wave, "[].") + : "[orange]Reached wave [accent]".concat(wave, "[]. Current highscore: [green]").concat(bestPreviousWave, "[]"), "\n[orange]--------")); + } + fmap.runs.push(finishedRun); + globals_1.FishEvents.fire("saveMaps", []); + } + Core.settings.remove(_a.key); + _a.current = null; + }); + })(); + return PartialMapRun; +}()); +exports.PartialMapRun = PartialMapRun; +var FMap = function () { + var _b; + var _classSuper = (0, io_1.dataClass)(); + var _static_allMaps_decorators; + var _static_allMaps_initializers = []; + var _static_allMaps_extraInitializers = []; + return _b = /** @class */ (function (_super) { + __extends(FMap, _super); + function FMap(data, + //O(n^2)... should be fine? + map) { + if (map === void 0) { map = Vars.maps.customMaps().find(function (m) { return m.file.name() === data.mapFileName; }); } + var _this = _super.call(this, data) || this; + _this.map = map; + return _this; + } + FMap.getCreate = function (map) { + if (this.allMaps == null) + return null; + var mapFileName = map.file.name(); + if (Object.prototype.hasOwnProperty.call(this.maps, mapFileName)) + return this.maps[mapFileName]; + var fmap = new this({ + runs: [], + mapFileName: mapFileName + }, map); + this.maps[mapFileName] = fmap; + this.allMaps.push(fmap); + return fmap; + }; + FMap.prototype.rules = function () { + var _c; + return (_c = this.map) === null || _c === void 0 ? void 0 : _c.rules(); + }; + FMap.prototype.stats = function () { + var _c; + var runs = this.runs.filter(function (r) { return r.maxPlayerCount > 0; }); //Remove all runs with no players on + var allRunCount = runs.length; + var victories = runs.filter(function (r) { return r.outcome()[1] === "win"; }); + var losses = runs.filter(function (r) { return r.outcome()[0] === "loss"; }).length; + var earlyRTVs = runs.filter(function (r) { return r.outcome()[1] === "early rtv"; }).length; + var lateRTVs = runs.filter(function (r) { return r.outcome()[1] === "late rtv"; }).length; + var significantRunCount = allRunCount - earlyRTVs; + var totalLosses = losses + lateRTVs; + var durations = runs.filter(function (r) { return r.outcome()[0] !== "rtv"; }).map(function (r) { return r.duration(); }); + var durationStats = (0, funcs_1.computeStatistics)(durations); + var winDurationStats = (0, funcs_1.computeStatistics)(runs.filter(function (r) { return r.outcome()[0] === "win"; }).map(function (r) { return r.duration(); })); + var teamWins = runs.filter(function (r) { return r.outcome()[1] !== "early rtv"; }).reduce(function (acc, item) { + var _c; + acc[item.winTeam.name] = ((_c = acc[item.winTeam.name]) !== null && _c !== void 0 ? _c : 0) + 1; + return acc; + }, {}); + var teamWinRate = Object.fromEntries(Object.entries(teamWins).map(function (_c) { + var _d = __read(_c, 2), team = _d[0], wins = _d[1]; + return [team, wins / significantRunCount]; + })); + //Remove runs that were on wave 0, due to a silly bug we have thousands of runs with a max wave of 0 + var waveStats = (0, funcs_1.computeStatistics)(runs.filter(function (r) { return r.outcome()[0] !== "rtv" && r.wave !== 0; }).map(function (r) { return r.wave; })); + return { + allRunCount: allRunCount, + significantRunCount: significantRunCount, + victories: victories.length, + losses: losses, + totalLosses: totalLosses, + earlyRTVs: earlyRTVs, + lateRTVs: lateRTVs, + earlyRTVRate: earlyRTVs / allRunCount, + winRate: victories.length / significantRunCount, + lossRate: losses / significantRunCount, + averagePlaytime: durationStats.average, + shortestWinTime: winDurationStats.lowest, + longestTime: durationStats.highest, + shortestTime: durationStats.lowest, + averageHighestPlayerCount: (0, funcs_1.computeStatistics)(runs.map(function (r) { return r.maxPlayerCount; })).average, + teamWins: teamWins, + teamWinRate: teamWinRate, + highestWave: waveStats.highest, + averageWave: waveStats.average, + mostRecentWin: (_c = victories.at(-1)) === null || _c === void 0 ? void 0 : _c.startTime + }; + }; + FMap.prototype.displayStats = function (f) { + var map = this.map; + if (!map) + return null; + var stats = this.stats(); + var rules = this.rules(); + var modeSpecificStats = (0, utils_1.match)(config_1.Gamemode.name(), { + attack: "[#CCFFCC]Total runs: ".concat(stats.allRunCount, " (").concat(stats.victories, " wins, ").concat(stats.totalLosses, " losses, ").concat(stats.earlyRTVs, " RTVs)\n[#CCFFCC]Outcomes: ").concat(f.percent(stats.winRate, 1), " wins, ").concat(f.percent(stats.lossRate, 1), " losses, ").concat(f.percent(stats.earlyRTVRate, 1), " RTVs\n[#CCFFCC]Average playtime: ").concat((0, utils_1.formatTime)(stats.averagePlaytime), "\n[#CCFFCC]Shortest win time: ").concat((0, utils_1.formatTime)(stats.shortestWinTime), "\n[#CCFFCC]Most recent win: ").concat(stats.mostRecentWin ? (0, utils_1.formatTimestamp)(stats.mostRecentWin) : "[red]none[]"), + survival: "[#CCFFCC]Highest wave reached: ".concat(stats.highestWave, "\n[#CCFFCC]Average wave reached: ").concat(stats.averageWave, "\n[#CCFFCC]Total runs: ").concat(stats.allRunCount, " (").concat(stats.earlyRTVs, " RTVs)\n[#CCFFCC]RTV rate: ").concat(f.percent(stats.earlyRTVRate, 1), "\n[#CCFFCC]Average duration: ").concat((0, utils_1.formatTime)(stats.averagePlaytime), "\n[#CCFFCC]Longest duration: ").concat((0, utils_1.formatTime)(stats.longestTime)), + pvp: "[#CCFFCC]Total runs: ".concat(stats.allRunCount, " (").concat(stats.earlyRTVs, " RTVs)\n[#CCFFCC]Team win rates: ").concat(Object.entries(stats.teamWinRate).map(function (_c) { + var _d = __read(_c, 2), team = _d[0], rate = _d[1]; + return "".concat(team, " ").concat(f.percent(rate, 1)); + }).join(", "), "\n[#CCFFCC]RTV rate: ").concat(f.percent(stats.earlyRTVRate, 1), "\n[#CCFFCC]Average match duration: ").concat((0, utils_1.formatTime)(stats.averagePlaytime), "\n[#CCFFCC]Shortest match duration: ").concat((0, utils_1.formatTime)(stats.shortestWinTime)), + hexed: "[#CCFFCC]Total runs: ".concat(stats.allRunCount, " (").concat(stats.earlyRTVs, " RTVs)\n[#CCFFCC]RTV rate: ").concat(f.percent(stats.earlyRTVRate, 1), "\n[#CCFFCC]Average match duration: ").concat((0, utils_1.formatTime)(stats.averagePlaytime), "\n[#CCFFCC]Shortest match duration: ").concat((0, utils_1.formatTime)(stats.shortestWinTime)), + sandbox: "[#CCFFCC]Total plays: ".concat(stats.allRunCount, "\n[#CCFFCC]Average play time: ").concat((0, utils_1.formatTime)(stats.averagePlaytime), "\n[#CCFFCC]Shortest play time: ").concat((0, utils_1.formatTime)(stats.shortestTime)), + }, ""); + return ("[coral]".concat(map.name(), "\n[gray](").concat(map.file.name(), ")\n\n[accent]Map by: [white]").concat(map.author(), "\n[accent]Description: [white]").concat(map.description(), "\n[accent]Size: [white]").concat(map.width, "x").concat(map.height, "\n[accent]Last updated: [white]").concat(new Date(map.file.lastModified()).toLocaleDateString(), "\n[accent]BvB allowed: ").concat(f.boolGood(rules.placeRangeCheck), ", unit item transfer allowed: ").concat(f.boolGood(rules.onlyDepositCore), "\n\n").concat(modeSpecificStats, "\n[#CCFFCC]Longest play time: ").concat((0, utils_1.formatTime)(stats.longestTime), "\n[#CCFFCC]Average player count: ").concat(f.number(stats.averageHighestPlayerCount, 1))); + }; + return FMap; + }(_classSuper)), + (function () { + var _c; + var _metadata = typeof Symbol === "function" && Symbol.metadata ? Object.create((_c = _classSuper[Symbol.metadata]) !== null && _c !== void 0 ? _c : null) : void 0; + _static_allMaps_decorators = [(0, io_1.serialize)("fish-map-data", function () { return ["version", 1, ["array", "u16", ["class", FMap, [ + ["runs", ["array", "u32", ["class", FinishedMapRun, [ + ["startTime", ["number", "i64"]], + ["endTime", ["number", "i64"]], + ["maxPlayerCount", ["number", "u8"]], + ["success", ["boolean"]], + ["winTeam", ["team"]], + ["wave", ["number", "u16"]] + ]]]], + ["mapFileName", ["string"]], + ]]]]; }, function () { return ["array", "u16", ["class", FMap, [ + ["runs", ["array", "u32", ["class", FinishedMapRun, [ + ["startTime", ["number", "i64"]], + ["endTime", ["number", "i64"]], + ["maxPlayerCount", ["number", "u8"]], + ["success", ["boolean"]], + ["winTeam", ["team"]], + ]]]], + ["mapFileName", ["string"]], + ]]]; }, undefined, "saveMaps")]; + __esDecorate(null, null, _static_allMaps_decorators, { kind: "field", name: "allMaps", static: true, private: false, access: { has: function (obj) { return "allMaps" in obj; }, get: function (obj) { return obj.allMaps; }, set: function (obj, value) { obj.allMaps = value; } }, metadata: _metadata }, _static_allMaps_initializers, _static_allMaps_extraInitializers); + if (_metadata) Object.defineProperty(_b, Symbol.metadata, { enumerable: true, configurable: true, writable: true, value: _metadata }); + })(), + _b.allMaps = __runInitializers(_b, _static_allMaps_initializers, null), + _b.maps = (__runInitializers(_b, _static_allMaps_extraInitializers), {}), + (function () { + globals_1.FishEvents.on("dataLoaded", function () { + var _c; + //This event listener runs after the data has been loaded into allMaps + ((_c = _b.allMaps) !== null && _c !== void 0 ? _c : (_b.allMaps = [])).forEach(function (map) { + _b.maps[map.mapFileName] = map; + map.runs.forEach(function (run) { + var _c; + //this should not even happen, I think GameOverEvent is sending winTeam as null sometimes?? + (_c = run.winTeam) !== null && _c !== void 0 ? _c : (run.winTeam = Team.derelict); + }); + }); + //create all the data + Vars.maps.customMaps().each(function (m) { return void _b.getCreate(m); }); + }); + })(), + _b; +}(); +exports.FMap = FMap; diff --git a/build/scripts/metrics.js b/build/scripts/metrics.js index 21a755cb..5c77f587 100644 --- a/build/scripts/metrics.js +++ b/build/scripts/metrics.js @@ -1,76 +1,76 @@ -"use strict"; -/* -Copyright © BalaM314, 2026. All Rights Reserved. -This file contains the player count tracking. -*/ -Object.defineProperty(exports, "__esModule", { value: true }); -exports.Metrics = void 0; -var Metrics = /** @class */ (function () { - function Metrics() { - } - Metrics.weekNumber = function (date) { - if (date === void 0) { date = Date.now(); } - return Math.floor((date - this.startDate) / this.millisPerWeek); - }; - Metrics.readingNumber = function (date) { - if (date === void 0) { date = Date.now(); } - return Math.floor(((date - this.startDate) % this.millisPerWeek) / this.millisBetweenReadings); - }; - Metrics.newWeek = function () { - return Array(2520).fill(this.noData); - }; - Metrics.currentWeek = function () { - var _b; - var _c, _d; - return (_b = (_c = this.weeks)[_d = this.weekNumber()]) !== null && _b !== void 0 ? _b : (_c[_d] = this.newWeek()); - }; - Metrics.update = function () { - Time.mark(); - var playerCount = Groups.player.size(); - this.currentWeek()[this.readingNumber()] = - Math.max(playerCount, this.currentWeek()[this.readingNumber()]); - Log.debug("metrics update @", Time.elapsed()); - }; - Metrics.exportRange = function (startDate, endDate) { - var _this = this; - if (startDate === void 0) { startDate = this.startDate; } - if (endDate === void 0) { endDate = Date.now(); } - if (typeof startDate !== "number") - throw new Error('startDate should be a number'); - var startWeek = this.weekNumber(startDate); - var endWeek = this.weekNumber(endDate); - return this.weeks.slice(startWeek, endWeek + 1).map(function (week, weekNumber) { - return week.filter(function (v) { return v >= 0; }).map(function (v, i) { return [ - v, - _this.startDate + - weekNumber * _this.millisPerWeek + - i * _this.millisBetweenReadings - ]; }); - }).flat(); - }; - var _a; - _a = Metrics; - /** 4 May 2025 */ - Metrics.startDate = new Date(2025, 4, 4).getTime(); - Metrics.millisPerWeek = 604800000; - Metrics.millisBetweenReadings = 240000; - Metrics.noData = -1; - /** - * Weeks are numbered starting at the week of 4 May 2025. - * A value is taken every 4 minutes, for a total of 15 readings per hour. - */ - // @serialize("player-count-data", () => ["version", 0, - // ["array", "u16", ["array", 2520, ["number", "i8"]]] - // ], undefined, weeks => { - // for(let i = 0; i <= Metrics.weekNumber(); i ++){ - // weeks[i] ??= Metrics.newWeek(); - // } - // return weeks; - // }) - Metrics.weeks = Array(_a.weekNumber() + 1).fill(0).map(function () { return _a.newWeek(); }); - (function () { - Timer.schedule(function () { return _a.update(); }, 15, 60); - })(); - return Metrics; -}()); -exports.Metrics = Metrics; +"use strict"; +/* +Copyright © BalaM314, 2026. All Rights Reserved. +This file contains the player count tracking. +*/ +Object.defineProperty(exports, "__esModule", { value: true }); +exports.Metrics = void 0; +var Metrics = /** @class */ (function () { + function Metrics() { + } + Metrics.weekNumber = function (date) { + if (date === void 0) { date = Date.now(); } + return Math.floor((date - this.startDate) / this.millisPerWeek); + }; + Metrics.readingNumber = function (date) { + if (date === void 0) { date = Date.now(); } + return Math.floor(((date - this.startDate) % this.millisPerWeek) / this.millisBetweenReadings); + }; + Metrics.newWeek = function () { + return Array(2520).fill(this.noData); + }; + Metrics.currentWeek = function () { + var _b; + var _c, _d; + return (_b = (_c = this.weeks)[_d = this.weekNumber()]) !== null && _b !== void 0 ? _b : (_c[_d] = this.newWeek()); + }; + Metrics.update = function () { + Time.mark(); + var playerCount = Groups.player.size(); + this.currentWeek()[this.readingNumber()] = + Math.max(playerCount, this.currentWeek()[this.readingNumber()]); + Log.debug("metrics update @", Time.elapsed()); + }; + Metrics.exportRange = function (startDate, endDate) { + var _this = this; + if (startDate === void 0) { startDate = this.startDate; } + if (endDate === void 0) { endDate = Date.now(); } + if (typeof startDate !== "number") + throw new Error('startDate should be a number'); + var startWeek = this.weekNumber(startDate); + var endWeek = this.weekNumber(endDate); + return this.weeks.slice(startWeek, endWeek + 1).map(function (week, weekNumber) { + return week.filter(function (v) { return v >= 0; }).map(function (v, i) { return [ + v, + _this.startDate + + weekNumber * _this.millisPerWeek + + i * _this.millisBetweenReadings + ]; }); + }).flat(); + }; + var _a; + _a = Metrics; + /** 4 May 2025 */ + Metrics.startDate = new Date(2025, 4, 4).getTime(); + Metrics.millisPerWeek = 604800000; + Metrics.millisBetweenReadings = 240000; + Metrics.noData = -1; + /** + * Weeks are numbered starting at the week of 4 May 2025. + * A value is taken every 4 minutes, for a total of 15 readings per hour. + */ + // @serialize("player-count-data", () => ["version", 0, + // ["array", "u16", ["array", 2520, ["number", "i8"]]] + // ], undefined, weeks => { + // for(let i = 0; i <= Metrics.weekNumber(); i ++){ + // weeks[i] ??= Metrics.newWeek(); + // } + // return weeks; + // }) + Metrics.weeks = Array(_a.weekNumber() + 1).fill(0).map(function () { return _a.newWeek(); }); + (function () { + Timer.schedule(function () { return _a.update(); }, 15, 60); + })(); + return Metrics; +}()); +exports.Metrics = Metrics; diff --git a/build/scripts/mindustryTypes.js b/build/scripts/mindustryTypes.js index 5c879b56..4d9bf344 100644 --- a/build/scripts/mindustryTypes.js +++ b/build/scripts/mindustryTypes.js @@ -1,12 +1,12 @@ -"use strict"; -/* -Copyright © BalaM314, 2026. All Rights Reserved. -This file contains TypeScript type definitions for Mindustry's code. -Mindustry is written in Java, which has strong types. -Mindustry supports loading Javascript, which does not have types. -Javascript will have access to Mindustry's functions, which have types. -We are writing Typescript, which does have types. We are able to call Mindustry's functions, but because those are written in Java we cannot directly use those types. -This file contains some of those type definitions, ported over from the Java definitions. -*/ -//this is fine -Object.defineProperty(exports, "__esModule", { value: true }); +"use strict"; +/* +Copyright © BalaM314, 2026. All Rights Reserved. +This file contains TypeScript type definitions for Mindustry's code. +Mindustry is written in Java, which has strong types. +Mindustry supports loading Javascript, which does not have types. +Javascript will have access to Mindustry's functions, which have types. +We are writing Typescript, which does have types. We are able to call Mindustry's functions, but because those are written in Java we cannot directly use those types. +This file contains some of those type definitions, ported over from the Java definitions. +*/ +//this is fine +Object.defineProperty(exports, "__esModule", { value: true }); diff --git a/build/scripts/packetHandlers.js b/build/scripts/packetHandlers.js index 0de90069..c8c49be4 100644 --- a/build/scripts/packetHandlers.js +++ b/build/scripts/packetHandlers.js @@ -1,281 +1,281 @@ -"use strict"; -/* -Copyright © BalaM314, 2026. All Rights Reserved. -This file contains the visual effects system. -Original contributor: @author TheEt1234 -Fixes: @author BalaM314 -Fixes: @author Dart25 -Fixes: @author Jurorno9 -*/ -Object.defineProperty(exports, "__esModule", { value: true }); -exports.commands = void 0; -exports.loadPacketHandlers = loadPacketHandlers; -exports.bulkInfoMsg = bulkInfoMsg; -var commands_1 = require("/frameworks/commands"); -var players_1 = require("/players"); -//some much needed restrictions -/** point in which effects will refuse to render */ -var MIN_EFFECT_TPS = 20; -/** maximum duration for user-created labels (seconds) */ -var MAX_LABEL_TIME = 20; -//info tracker -var lastLabel = ''; -var lastAccessedBulkLabel = null; -var lastAccessedLabel = null; -var lastAccessedBulkLine = null; -var lastAccessedLine = null; -var bulkLimit = 1000; -var noPermissionText = "[red]You don't have permission to use this packet."; -var invalidContentText = '[red]Invalid label content.'; -var tooLongText = '[red]Bulk content length exceeded, please use fewer effects.'; -var bulkSeparator = '|'; -var procError = '[red]An error occured while processing your request.'; -var invalidReq = '[red]Invalid request. Please consult the documentation.'; -var lowTPSError = '[red]Low server TPS, skipping request.'; -var tmpLinePacket = new EffectCallPacket2(); -var tmpLabelPacket = new LabelReliableCallPacket(); -function loadPacketHandlers() { - //initialize line packet - tmpLinePacket.effect = Fx.pointBeam; - tmpLinePacket.rotation = 0.0; - tmpLinePacket.color = Tmp.c1; - tmpLinePacket.data = Tmp.v1; - //labels - //fmt: "content,duration,x,y" - Vars.netServer.addPacketHandler('label', function (player, content) { - var p = players_1.FishPlayer.get(player); - try { - if (Core.graphics.getFramesPerSecond() < MIN_EFFECT_TPS) { - p.sendMessage(lowTPSError, 1000); - return; - } - if (!p.hasPerm("visualEffects")) { - p.sendMessage(noPermissionText, 1000); - return; - } - lastAccessedLabel = p; - handleLabel(player, content, true); - } - catch (_a) { - p.sendMessage(procError, 1000); - } - }); - Vars.netServer.addPacketHandler('bulkLabel', function (player, content) { - var p = players_1.FishPlayer.get(player); - try { - if (Core.graphics.getFramesPerSecond() < MIN_EFFECT_TPS) { - p.sendMessage(lowTPSError, 1000); - return; - } - if (!p.hasPerm('bulkVisualEffects')) { - p.sendMessage(noPermissionText, 1000); - return; - } - lastAccessedBulkLabel = p; - //get individual labels - var labels = []; - var inQuotes = false; - var startIdx = 0; - for (var i = 0; i < content.length; i++) { - switch (content[i]) { - case '"': - if (i > 0 && content[i - 1] == '\\') - break; - inQuotes = !inQuotes; - break; - //separate - case bulkSeparator: - if (inQuotes) - break; - labels.push(content.substring(startIdx, i)); - startIdx = i + 1; - break; - default: - break; - } - } - //last label - if (startIdx < content.length) { - labels.push(content.substring(startIdx, content.length - 1)); - } - if (labels.length > bulkLimit) { - p.sendMessage(tooLongText, 1000); - return; - } - //display labels - // eslint-disable-next-line @typescript-eslint/prefer-for-of - for (var i = 0; i < labels.length; i++) { - var label = labels[i]; - if (label.trim().length <= 0) - continue; - if (!handleLabel(player, label, false)) - return; - } - } - catch (_a) { - p.sendMessage(procError, 1000); - } - }); - //lines - Vars.netServer.addPacketHandler('lineEffect', function (player, content) { - var p = players_1.FishPlayer.get(player); - try { - if (Core.graphics.getFramesPerSecond() < MIN_EFFECT_TPS) { - p.sendMessage(lowTPSError, 1000); - return; - } - if (!p.hasPerm("visualEffects")) { - p.sendMessage(noPermissionText, 1000); - return; - } - if (!handleLine(content, player)) - return; - lastAccessedLine = p; - } - catch (_a) { - p.sendMessage(procError, 1000); - } - }); - //this is the silas effect but it's way too real - Vars.netServer.addPacketHandler('bulkLineEffect', function (player, content) { - var p = players_1.FishPlayer.get(player); - if (Core.graphics.getFramesPerSecond() < MIN_EFFECT_TPS) { - p.sendMessage(lowTPSError, 1000); - return; - } - if (!p.hasPerm('bulkVisualEffects')) { - p.sendMessage(noPermissionText, 1000); - return; - } - try { - var lines = content.split(bulkSeparator); - if (lines.length > bulkLimit) { - p.sendMessage(tooLongText, 1000); - return; - } - // eslint-disable-next-line @typescript-eslint/prefer-for-of - for (var i = 0; i < lines.length; i++) { - var line = lines[i]; - if (line.trim().length <= 0) - continue; - if (!handleLine(line, player)) - return; - } - lastAccessedBulkLine = p; - } - catch (_a) { - p.sendMessage(procError, 1000); - } - }); -} -//commands -exports.commands = (0, commands_1.commandList)({ - pklast: { - args: [], - description: 'Tells you who last accessed the packet handlers.', - perm: commands_1.Perm.none, - handler: function (_a) { - var output = _a.output; - var outputLines = []; - if (lastAccessedLabel && lastLabel) { - outputLines.push("".concat(lastAccessedLabel.name, "[white] created label \"").concat(lastLabel, "\".")); - } - if (lastAccessedBulkLabel) { - outputLines.push("".concat(lastAccessedBulkLabel.name, "[white] last used the bulk label effect.")); - } - if (lastAccessedLine) { - outputLines.push("".concat(lastAccessedLine.name, "[white] last used the line effect.")); - } - if (lastAccessedBulkLine) { - outputLines.push("".concat(lastAccessedBulkLine.name, "[white] last used the bulk line effect.")); - } - output(outputLines.length > 0 ? outputLines.join('\n') : 'No packet handlers have been accessed yet.'); - } - }, - pkdocs: { - description: 'Packet handler documentation.', - args: [], - perm: commands_1.Perm.none, - handler: function (_a) { - var sender = _a.sender, output = _a.output; - output("\t\t\t\t[blue]FISH[white] Packet Handler Docs\n[white]Usage:[accent]\n\t- Run the javascript function \"Call.serverPacketReliable()\" to send these. (!js in foos)\n\t- You need to multiply world coordinates by Vars.tilesize (8) for things to work properly. This is a relic from the v3 days where every tile was 8 pixels.\n\n[white]Packet types[accent]:\n\t- Line effect: \"lineEffect\", \"x0,y0,x1,y1,hexColor\" (for example \"20.7,19.3,50.4,28.9,#FF0000\")\n\t- Bulk line effect: \"bulkLineEffect\", equivalent to multiple lineEffect packets, with every line separated by a '|' symbol.\n\t- Label effect: \"label\", \"content,duration,x,y\" (for example \"\"Hi!\",10,20,28\")\n\t- Bulk label effect: \"bulkLabel\", equivalent to multiple label packets, with every label separated by a '|' symbol.\n\n[white]Limitations[accent]:\n\t- You ".concat((sender.hasPerm('bulkVisualEffects') ? ("[green]have been granted[accent]") : ("[red]do not have[accent]")), " access to bulk effects.\n\t- Effects will no longer be drawn at ").concat(MIN_EFFECT_TPS, " for server preformance.\n\t- Labels cannot last longer than ").concat(MAX_LABEL_TIME, " seconds.\n\t- There is a set ratelimit for sending packets, be careful ...\n\n[white]Starter Example[accent]:\n\n\tTo place a label saying \"hello\" at (0,0);\n\tFoos users: [lightgray]!js Call.serverPacketReliable(\"label\", [\"\\\"hello\\\"\", 10, 0, 0].join(\",\"))[accent]\n\tnewConsole users: [lightgrey]Call.serverPacketReliable(\"label\", [\"hello\", 10, 0, 10].join(\",\"))[accent]\n\n[white]Comments and Credits[accent]:\n\t- 'These packet handlers and everything related to them were made by [green]frog[accent].\n\t- 'The code style when submitted was beyond drunk... but it worked... barely' -BalaM314\n\t- \"worst error handling i have ever seen, why kick the player???\" -ASimpleBeginner'\n\t- Most of the code was rewritten in 2024 by [#6e00fb]D[#9e15de]a[#cd29c2]r[#fd3ea5]t[accent].'\n\t- Small tweaks by [#00cf]s[#00bf]w[#009f]a[#007f]m[#005f]p[accent]")); - } - } -}); -//#region utils -function findEndQuote(content, startPos) { - if (content[startPos] != '"') { - //not a start quote?? - return -1; - } - for (var i = startPos + 1; i < content.length; i++) { - if (content[i] == '"' && (i < 1 || content[i - 1] != '\\')) { - return i; - } - } - return -1; -} -function handleLabel(player, content, isSingle) { - var endPos = findEndQuote(content, 0); - if (endPos == -1) { - //invalid content - player.sendMessage(invalidContentText); - return false; - } - //label, clean up \"s - var message = content.substring(1, endPos).replace('\\"', '"'); - var parts = content.substring(endPos + 2).split(','); - if (parts.length != 3) { //dur,x,y - player.sendMessage(invalidReq); - return false; - } - if (isSingle && Strings.stripColors(message).length > 150) { - player.sendMessage('Label too large. Maximum is 150 characters, not including color tags.'); - } - if (isSingle) { - lastLabel = message; - } - var duration = Number(parts[0]); - var x = Number(parts[1]), y = Number(parts[2]); - if (Number.isNaN(duration) || duration > MAX_LABEL_TIME || Number.isNaN(x) || Number.isNaN(y)) { - player.sendMessage(invalidReq); - return false; - } - /*Call.labelReliable( - message, //message - Number(parts[0]), //duration - Number(parts[1]), //x - Number(parts[2]) //y - );*/ - tmpLabelPacket.message = message; - tmpLabelPacket.duration = duration; - tmpLabelPacket.worldx = x; - tmpLabelPacket.worldy = y; - Vars.net.send(tmpLabelPacket, false); - return true; -} -function handleLine(content, player) { - var parts = content.split(','); - if (parts.length != 5) { //x0,y0,x1,y1,color - player.sendMessage(invalidReq); - return false; - } - Tmp.v1.set(Number(parts[2]), Number(parts[3])); //x1,y1 - Color.valueOf(Tmp.c1, parts[4]); //color - /*Call.effect( - Fx.pointBeam, - Number(parts[0]), Number(parts[1]), //x,y - 0, Tmp.c1, //color - Tmp.v1 //x1,y1 - );*/ - tmpLinePacket.x = Number(parts[0]); - tmpLinePacket.y = Number(parts[1]); - Vars.net.send(tmpLinePacket, false); - return true; -} -function bulkInfoMsg(messages, conn) { - for (var i = messages.length - 1; i >= 0; i--) { - Call.infoMessage(conn, messages[i]); - } -} -//#endregion +"use strict"; +/* +Copyright © BalaM314, 2026. All Rights Reserved. +This file contains the visual effects system. +Original contributor: @author TheEt1234 +Fixes: @author BalaM314 +Fixes: @author Dart25 +Fixes: @author Jurorno9 +*/ +Object.defineProperty(exports, "__esModule", { value: true }); +exports.commands = void 0; +exports.loadPacketHandlers = loadPacketHandlers; +exports.bulkInfoMsg = bulkInfoMsg; +var commands_1 = require("/frameworks/commands"); +var players_1 = require("/players"); +//some much needed restrictions +/** point in which effects will refuse to render */ +var MIN_EFFECT_TPS = 20; +/** maximum duration for user-created labels (seconds) */ +var MAX_LABEL_TIME = 20; +//info tracker +var lastLabel = ''; +var lastAccessedBulkLabel = null; +var lastAccessedLabel = null; +var lastAccessedBulkLine = null; +var lastAccessedLine = null; +var bulkLimit = 1000; +var noPermissionText = "[red]You don't have permission to use this packet."; +var invalidContentText = '[red]Invalid label content.'; +var tooLongText = '[red]Bulk content length exceeded, please use fewer effects.'; +var bulkSeparator = '|'; +var procError = '[red]An error occured while processing your request.'; +var invalidReq = '[red]Invalid request. Please consult the documentation.'; +var lowTPSError = '[red]Low server TPS, skipping request.'; +var tmpLinePacket = new EffectCallPacket2(); +var tmpLabelPacket = new LabelReliableCallPacket(); +function loadPacketHandlers() { + //initialize line packet + tmpLinePacket.effect = Fx.pointBeam; + tmpLinePacket.rotation = 0.0; + tmpLinePacket.color = Tmp.c1; + tmpLinePacket.data = Tmp.v1; + //labels + //fmt: "content,duration,x,y" + Vars.netServer.addPacketHandler('label', function (player, content) { + var p = players_1.FishPlayer.get(player); + try { + if (Core.graphics.getFramesPerSecond() < MIN_EFFECT_TPS) { + p.sendMessage(lowTPSError, 1000); + return; + } + if (!p.hasPerm("visualEffects")) { + p.sendMessage(noPermissionText, 1000); + return; + } + lastAccessedLabel = p; + handleLabel(player, content, true); + } + catch (_a) { + p.sendMessage(procError, 1000); + } + }); + Vars.netServer.addPacketHandler('bulkLabel', function (player, content) { + var p = players_1.FishPlayer.get(player); + try { + if (Core.graphics.getFramesPerSecond() < MIN_EFFECT_TPS) { + p.sendMessage(lowTPSError, 1000); + return; + } + if (!p.hasPerm('bulkVisualEffects')) { + p.sendMessage(noPermissionText, 1000); + return; + } + lastAccessedBulkLabel = p; + //get individual labels + var labels = []; + var inQuotes = false; + var startIdx = 0; + for (var i = 0; i < content.length; i++) { + switch (content[i]) { + case '"': + if (i > 0 && content[i - 1] == '\\') + break; + inQuotes = !inQuotes; + break; + //separate + case bulkSeparator: + if (inQuotes) + break; + labels.push(content.substring(startIdx, i)); + startIdx = i + 1; + break; + default: + break; + } + } + //last label + if (startIdx < content.length) { + labels.push(content.substring(startIdx, content.length - 1)); + } + if (labels.length > bulkLimit) { + p.sendMessage(tooLongText, 1000); + return; + } + //display labels + // eslint-disable-next-line @typescript-eslint/prefer-for-of + for (var i = 0; i < labels.length; i++) { + var label = labels[i]; + if (label.trim().length <= 0) + continue; + if (!handleLabel(player, label, false)) + return; + } + } + catch (_a) { + p.sendMessage(procError, 1000); + } + }); + //lines + Vars.netServer.addPacketHandler('lineEffect', function (player, content) { + var p = players_1.FishPlayer.get(player); + try { + if (Core.graphics.getFramesPerSecond() < MIN_EFFECT_TPS) { + p.sendMessage(lowTPSError, 1000); + return; + } + if (!p.hasPerm("visualEffects")) { + p.sendMessage(noPermissionText, 1000); + return; + } + if (!handleLine(content, player)) + return; + lastAccessedLine = p; + } + catch (_a) { + p.sendMessage(procError, 1000); + } + }); + //this is the silas effect but it's way too real + Vars.netServer.addPacketHandler('bulkLineEffect', function (player, content) { + var p = players_1.FishPlayer.get(player); + if (Core.graphics.getFramesPerSecond() < MIN_EFFECT_TPS) { + p.sendMessage(lowTPSError, 1000); + return; + } + if (!p.hasPerm('bulkVisualEffects')) { + p.sendMessage(noPermissionText, 1000); + return; + } + try { + var lines = content.split(bulkSeparator); + if (lines.length > bulkLimit) { + p.sendMessage(tooLongText, 1000); + return; + } + // eslint-disable-next-line @typescript-eslint/prefer-for-of + for (var i = 0; i < lines.length; i++) { + var line = lines[i]; + if (line.trim().length <= 0) + continue; + if (!handleLine(line, player)) + return; + } + lastAccessedBulkLine = p; + } + catch (_a) { + p.sendMessage(procError, 1000); + } + }); +} +//commands +exports.commands = (0, commands_1.commandList)({ + pklast: { + args: [], + description: 'Tells you who last accessed the packet handlers.', + perm: commands_1.Perm.none, + handler: function (_a) { + var output = _a.output; + var outputLines = []; + if (lastAccessedLabel && lastLabel) { + outputLines.push("".concat(lastAccessedLabel.name, "[white] created label \"").concat(lastLabel, "\".")); + } + if (lastAccessedBulkLabel) { + outputLines.push("".concat(lastAccessedBulkLabel.name, "[white] last used the bulk label effect.")); + } + if (lastAccessedLine) { + outputLines.push("".concat(lastAccessedLine.name, "[white] last used the line effect.")); + } + if (lastAccessedBulkLine) { + outputLines.push("".concat(lastAccessedBulkLine.name, "[white] last used the bulk line effect.")); + } + output(outputLines.length > 0 ? outputLines.join('\n') : 'No packet handlers have been accessed yet.'); + } + }, + pkdocs: { + description: 'Packet handler documentation.', + args: [], + perm: commands_1.Perm.none, + handler: function (_a) { + var sender = _a.sender, output = _a.output; + output("\t\t\t\t[blue]FISH[white] Packet Handler Docs\n[white]Usage:[accent]\n\t- Run the javascript function \"Call.serverPacketReliable()\" to send these. (!js in foos)\n\t- You need to multiply world coordinates by Vars.tilesize (8) for things to work properly. This is a relic from the v3 days where every tile was 8 pixels.\n\n[white]Packet types[accent]:\n\t- Line effect: \"lineEffect\", \"x0,y0,x1,y1,hexColor\" (for example \"20.7,19.3,50.4,28.9,#FF0000\")\n\t- Bulk line effect: \"bulkLineEffect\", equivalent to multiple lineEffect packets, with every line separated by a '|' symbol.\n\t- Label effect: \"label\", \"content,duration,x,y\" (for example \"\"Hi!\",10,20,28\")\n\t- Bulk label effect: \"bulkLabel\", equivalent to multiple label packets, with every label separated by a '|' symbol.\n\n[white]Limitations[accent]:\n\t- You ".concat((sender.hasPerm('bulkVisualEffects') ? ("[green]have been granted[accent]") : ("[red]do not have[accent]")), " access to bulk effects.\n\t- Effects will no longer be drawn at ").concat(MIN_EFFECT_TPS, " for server preformance.\n\t- Labels cannot last longer than ").concat(MAX_LABEL_TIME, " seconds.\n\t- There is a set ratelimit for sending packets, be careful ...\n\n[white]Starter Example[accent]:\n\n\tTo place a label saying \"hello\" at (0,0);\n\tFoos users: [lightgray]!js Call.serverPacketReliable(\"label\", [\"\\\"hello\\\"\", 10, 0, 0].join(\",\"))[accent]\n\tnewConsole users: [lightgrey]Call.serverPacketReliable(\"label\", [\"hello\", 10, 0, 10].join(\",\"))[accent]\n\n[white]Comments and Credits[accent]:\n\t- 'These packet handlers and everything related to them were made by [green]frog[accent].\n\t- 'The code style when submitted was beyond drunk... but it worked... barely' -BalaM314\n\t- \"worst error handling i have ever seen, why kick the player???\" -ASimpleBeginner'\n\t- Most of the code was rewritten in 2024 by [#6e00fb]D[#9e15de]a[#cd29c2]r[#fd3ea5]t[accent].'\n\t- Small tweaks by [#00cf]s[#00bf]w[#009f]a[#007f]m[#005f]p[accent]")); + } + } +}); +//#region utils +function findEndQuote(content, startPos) { + if (content[startPos] != '"') { + //not a start quote?? + return -1; + } + for (var i = startPos + 1; i < content.length; i++) { + if (content[i] == '"' && (i < 1 || content[i - 1] != '\\')) { + return i; + } + } + return -1; +} +function handleLabel(player, content, isSingle) { + var endPos = findEndQuote(content, 0); + if (endPos == -1) { + //invalid content + player.sendMessage(invalidContentText); + return false; + } + //label, clean up \"s + var message = content.substring(1, endPos).replace('\\"', '"'); + var parts = content.substring(endPos + 2).split(','); + if (parts.length != 3) { //dur,x,y + player.sendMessage(invalidReq); + return false; + } + if (isSingle && Strings.stripColors(message).length > 150) { + player.sendMessage('Label too large. Maximum is 150 characters, not including color tags.'); + } + if (isSingle) { + lastLabel = message; + } + var duration = Number(parts[0]); + var x = Number(parts[1]), y = Number(parts[2]); + if (Number.isNaN(duration) || duration > MAX_LABEL_TIME || Number.isNaN(x) || Number.isNaN(y)) { + player.sendMessage(invalidReq); + return false; + } + /*Call.labelReliable( + message, //message + Number(parts[0]), //duration + Number(parts[1]), //x + Number(parts[2]) //y + );*/ + tmpLabelPacket.message = message; + tmpLabelPacket.duration = duration; + tmpLabelPacket.worldx = x; + tmpLabelPacket.worldy = y; + Vars.net.send(tmpLabelPacket, false); + return true; +} +function handleLine(content, player) { + var parts = content.split(','); + if (parts.length != 5) { //x0,y0,x1,y1,color + player.sendMessage(invalidReq); + return false; + } + Tmp.v1.set(Number(parts[2]), Number(parts[3])); //x1,y1 + Color.valueOf(Tmp.c1, parts[4]); //color + /*Call.effect( + Fx.pointBeam, + Number(parts[0]), Number(parts[1]), //x,y + 0, Tmp.c1, //color + Tmp.v1 //x1,y1 + );*/ + tmpLinePacket.x = Number(parts[0]); + tmpLinePacket.y = Number(parts[1]); + Vars.net.send(tmpLinePacket, false); + return true; +} +function bulkInfoMsg(messages, conn) { + for (var i = messages.length - 1; i >= 0; i--) { + Call.infoMessage(conn, messages[i]); + } +} +//#endregion diff --git a/build/scripts/players.js b/build/scripts/players.js index 73c005ff..91313b74 100644 --- a/build/scripts/players.js +++ b/build/scripts/players.js @@ -1,1994 +1,1994 @@ -"use strict"; -/* -Copyright © BalaM314, 2026. All Rights Reserved. -This file contains the FishPlayer class, and many player-related functions. -*/ -var __makeTemplateObject = (this && this.__makeTemplateObject) || function (cooked, raw) { - if (Object.defineProperty) { Object.defineProperty(cooked, "raw", { value: raw }); } else { cooked.raw = raw; } - return cooked; -}; -var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { - if (k2 === undefined) k2 = k; - var desc = Object.getOwnPropertyDescriptor(m, k); - if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { - desc = { enumerable: true, get: function() { return m[k]; } }; - } - Object.defineProperty(o, k2, desc); -}) : (function(o, m, k, k2) { - if (k2 === undefined) k2 = k; - o[k2] = m[k]; -})); -var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { - Object.defineProperty(o, "default", { enumerable: true, value: v }); -}) : function(o, v) { - o["default"] = v; -}); -var __importStar = (this && this.__importStar) || (function () { - var ownKeys = function(o) { - ownKeys = Object.getOwnPropertyNames || function (o) { - var ar = []; - for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k; - return ar; - }; - return ownKeys(o); - }; - return function (mod) { - if (mod && mod.__esModule) return mod; - var result = {}; - if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]); - __setModuleDefault(result, mod); - return result; - }; -})(); -var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { - function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } - return new (P || (P = Promise))(function (resolve, reject) { - function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } - function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } - function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } - step((generator = generator.apply(thisArg, _arguments || [])).next()); - }); -}; -var __generator = (this && this.__generator) || function (thisArg, body) { - var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g = Object.create((typeof Iterator === "function" ? Iterator : Object).prototype); - return g.next = verb(0), g["throw"] = verb(1), g["return"] = verb(2), typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g; - function verb(n) { return function (v) { return step([n, v]); }; } - function step(op) { - if (f) throw new TypeError("Generator is already executing."); - while (g && (g = 0, op[0] && (_ = 0)), _) try { - if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t; - if (y = 0, t) op = [op[0] & 2, t.value]; - switch (op[0]) { - case 0: case 1: t = op; break; - case 4: _.label++; return { value: op[1], done: false }; - case 5: _.label++; y = op[1]; op = [0]; continue; - case 7: op = _.ops.pop(); _.trys.pop(); continue; - default: - if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; } - if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; } - if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; } - if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; } - if (t[2]) _.ops.pop(); - _.trys.pop(); continue; - } - op = body.call(thisArg, _); - } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; } - if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true }; - } -}; -var __values = (this && this.__values) || function(o) { - var s = typeof Symbol === "function" && Symbol.iterator, m = s && o[s], i = 0; - if (m) return m.call(o); - if (o && typeof o.length === "number") return { - next: function () { - if (o && i >= o.length) o = void 0; - return { value: o && o[i++], done: !o }; - } - }; - throw new TypeError(s ? "Object is not iterable." : "Symbol.iterator is not defined."); -}; -var __read = (this && this.__read) || function (o, n) { - var m = typeof Symbol === "function" && o[Symbol.iterator]; - if (!m) return o; - var i = m.call(o), r, ar = [], e; - try { - while ((n === void 0 || n-- > 0) && !(r = i.next()).done) ar.push(r.value); - } - catch (error) { e = { error: error }; } - finally { - try { - if (r && !r.done && (m = i["return"])) m.call(i); - } - finally { if (e) throw e.error; } - } - return ar; -}; -var __spreadArray = (this && this.__spreadArray) || function (to, from, pack) { - if (pack || arguments.length === 2) for (var i = 0, l = from.length, ar; i < l; i++) { - if (ar || !(i in from)) { - if (!ar) ar = Array.prototype.slice.call(from, 0, i); - ar[i] = from[i]; - } - } - return to.concat(ar || Array.prototype.slice.call(from)); -}; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.FishPlayer = void 0; -var api = __importStar(require("/api")); -var config_1 = require("/config"); -var commands_1 = require("/frameworks/commands"); -var menus_1 = require("/frameworks/menus"); -var funcs_1 = require("/funcs"); -var globals = __importStar(require("/globals")); -var globals_1 = require("/globals"); -var maps_1 = require("/maps"); -var ranks_1 = require("/ranks"); -var utils_1 = require("/utils"); -var FishPlayer = /** @class */ (function () { - //#endregion - function FishPlayer(uuid, data, player) { - //#endregion - //#region Transient properties - //Commands framework - /** Front-to-back queue of menus to show. */ - this.activeMenus = []; - /** Mapping from command to usage data. */ - this.usageData = {}; - this.tapInfo = { - commandName: null, - lastArgs: {}, - mode: "once", - }; - //Misc - this.player = null; - /** Used for the /trail command. */ - this.trail = null; - this.cleanedName = "Unnamed player [ERROR}"; - this.prefixedName = "Unnamed player [ERROR}"; - /** Used to freeze players when votekicking. */ - this.frozen = false; - /** Used to avoid spamming players with ads by the tip message system */ - this.lastShownAd = globals.maxTime; - /** Used to avoid spamming players with ads by the tip message system */ - this.showAdNext = false; - /** Transient statistics, used by the automatic griefer detection. */ - this.tstats = { - //remember to clear this in updateSavedInfoFromPlayer! - blocksBroken: 0, - blockInteractionsThisMap: 0, - lastMapStartTime: 0, - lastMapPlayedTime: 0, - wavesSurvived: 0, - }; - /** Whether the player has manually marked themselves as AFK. */ - this.manualAfk = false; - //Used for AFK detection. - this.lastMousePosition = [0, 0]; - this.lastUnitPosition = [0, 0]; - this.lastActive = Date.now(); - /** Set this to false to disable automatic name updates. Used for the rename console command. */ - this.shouldUpdateName = true; - /** Used by the sendMessage() ratelimit system. */ - this.lastRatelimitedMessage = -1; - /** Keeps track of whether a player has changed team this match, for win rate calculation. */ - this.changedTeam = false; - /** Whether the player's IP was detected as a VPN. */ - this.ipDetectedVpn = false; - /** - * If a player's IP is detected as a VPN on their first join, - * they are autoflagged and cannot build or talk in chat. - */ - this.autoflagged = false; - /** Timestamp until which this player will not be allowed to control units. */ - this.blockedFromPossessingUnitsUntil = 0; - /** Timestamp until which this player will not be allowed to control units. */ - this.blockedFromCommandingUnitsUntil = 0; - // Used by the data syncing framework. - this.infoUpdated = false; - this.dataSynced = false; - this.restoreTeam = null; - this.name = "Unnamed player [ERROR}"; - this.muted = false; - this.unmarkTime = -1; - this.rank = ranks_1.Rank.player; - this.flags = new Set(); - /** Used to color chat messages for the member command */ - this.highlight = null; - /** Used to color the player's name for the member command */ - this.rainbow = null; - /** List of all moderation actions that have been performed on this player. */ - this.history = []; - /** - * The USID for this player. - * USID stands for Unique Server IDentifier. It is like a UUID, but unique to each server (by IP and port). - * It cannot be viewed by admins and it cannot be obtained by other servers. - */ - this.usid = null; - /** If chat strictness is set to "strict", the player will not be allowed to swear. */ - this.chatStrictness = "chat"; - /** -1 represents unknown */ - this.lastJoined = -1; - /** -1 represents unknown */ - this.firstJoined = -1; - /** -1 represents unknown */ - this.globalLastJoined = -1; - /** -1 represents unknown */ - this.globalFirstJoined = -1; - this.stats = { - blocksBroken: 0, - blocksPlaced: 0, - timeInGame: 0, - chatMessagesSent: 0, - gamesFinished: 0, - gamesWon: 0, - }; - this.globalStats = this.stats; - /** Used for the /vanish command. */ - this.showRankPrefix = true; - this.achievements = new Bits(); - this.uuid = uuid; - this.player = player; - this.updateData(data); - } - //#region getplayer - //Contains methods used to get FishPlayer instances. - FishPlayer.createFromPlayer = function (player) { - return new this(player.uuid(), {}, player); - }; - FishPlayer.createFromInfo = function (playerInfo) { - var _a; - return new this(playerInfo.id, { - uuid: playerInfo.id, - name: playerInfo.lastName, - usid: (_a = playerInfo.adminUsid) !== null && _a !== void 0 ? _a : null - }, null); - }; - FishPlayer.getFromInfo = function (playerInfo) { - var _a; - var _b, _c; - return (_a = (_b = FishPlayer.cachedPlayers)[_c = playerInfo.id]) !== null && _a !== void 0 ? _a : (_b[_c] = FishPlayer.createFromInfo(playerInfo)); - }; - FishPlayer.get = function (player) { - var _a; - var _b, _c; - return (_a = (_b = FishPlayer.cachedPlayers)[_c = player.uuid()]) !== null && _a !== void 0 ? _a : (_b[_c] = FishPlayer.createFromPlayer(player)); - }; - FishPlayer.resolve = function (player) { - var _a; - var _b, _c; - if (player instanceof FishPlayer) - return player; - else - return (_a = (_b = FishPlayer.cachedPlayers)[_c = player.uuid()]) !== null && _a !== void 0 ? _a : (_b[_c] = FishPlayer.createFromPlayer(player)); - }; - FishPlayer.getById = function (id) { - var _a; - return (_a = this.cachedPlayers[id]) !== null && _a !== void 0 ? _a : null; - }; - /** Returns the FishPlayer representing the first online player matching a given name. */ - FishPlayer.getByName = function (name) { - if (name == "") - return null; - var realPlayer = Groups.player.find(function (p) { - return p.name === name || - p.name.includes(name) || - p.name.toLowerCase().includes(name.toLowerCase()) || - Strings.stripColors(p.name).toLowerCase() === name.toLowerCase() || - Strings.stripColors(p.name).toLowerCase().includes(name.toLowerCase()) || - false; - }); - return realPlayer ? this.get(realPlayer) : null; - }; - ; - /** Returns the FishPlayers representing all online players matching a given name. */ - FishPlayer.getAllByName = function (name, strict) { - if (strict === void 0) { strict = true; } - if (name == "") - return []; - var output = []; - Groups.player.each(function (p) { - var fishP = FishPlayer.get(p); - if (fishP.connected() && fishP.cleanedName.includes(name) || (!strict && fishP.cleanedName.toLowerCase().includes(name))) - output.push(fishP); - }); - return output; - }; - FishPlayer.getOneMindustryPlayerByName = function (str) { - var e_1, _a; - if (str == "") - return "none"; - var players = (0, funcs_1.setToArray)(Groups.player); - var matchingPlayers; - var filters = [ - function (p) { return p.name === str; }, - // p => Strings.stripColors(p.name) === str, - function (p) { return Strings.stripColors(p.name).toLowerCase() === str.toLowerCase(); }, - // p => p.name.includes(str), - function (p) { return p.name.toLowerCase().includes(str.toLowerCase()); }, - function (p) { return Strings.stripColors(p.name).includes(str); }, - function (p) { return Strings.stripColors(p.name).toLowerCase().includes(str.toLowerCase()); }, - ]; - try { - for (var filters_1 = __values(filters), filters_1_1 = filters_1.next(); !filters_1_1.done; filters_1_1 = filters_1.next()) { - var filter = filters_1_1.value; - matchingPlayers = players.filter(filter); - if (matchingPlayers.length == 1) - return matchingPlayers[0]; - else if (matchingPlayers.length > 1) - return "multiple"; - } - } - catch (e_1_1) { e_1 = { error: e_1_1 }; } - finally { - try { - if (filters_1_1 && !filters_1_1.done && (_a = filters_1.return)) _a.call(filters_1); - } - finally { if (e_1) throw e_1.error; } - } - return "none"; - }; - //This method exists only because there is no easy way to turn an entitygroup into an array - FishPlayer.getAllOnline = function () { - var players = []; - Groups.player.each(function (p) { - var fishP = FishPlayer.get(p); - if (fishP.connected()) - players.push(fishP); - }); - return players; - }; - /** Returns all cached FishPlayers with names matching the search string. */ - FishPlayer.getAllOfflineByName = function (name) { - var e_2, _a; - var matching = []; - try { - for (var _b = __values(Object.entries(this.cachedPlayers)), _c = _b.next(); !_c.done; _c = _b.next()) { - var _d = __read(_c.value, 2), uuid = _d[0], player = _d[1]; - if (player.cleanedName.toLowerCase().includes(name)) - matching.push(player); - } - } - catch (e_2_1) { e_2 = { error: e_2_1 }; } - finally { - try { - if (_c && !_c.done && (_a = _b.return)) _a.call(_b); - } - finally { if (e_2) throw e_2.error; } - } - return matching; - }; - FishPlayer.onConnectPacket = function (_a) { - var _this = this; - var uuid = _a.uuid, name = _a.name; - var entry = this.cachedPlayers[uuid]; - if (entry) { - entry.infoUpdated = false; - entry.dataSynced = false; - entry.name = name; - } - api.getFishPlayerData(uuid).then(function (data) { - if (!data) - return; //nothing to sync - var fishP; - if (!(uuid in _this.cachedPlayers)) { - fishP = new FishPlayer(uuid, data, null); - fishP.originalName = name; - fishP.dataSynced = true; - _this.cachedPlayers[uuid] = fishP; - } - else { - fishP = _this.cachedPlayers[uuid]; - fishP.dataSynced = true; - fishP.updateData(data); - if (fishP.infoUpdated) { - //Player has already connected - //Run it again - if (fishP.player) - fishP.updateSavedInfoFromPlayer(fishP.player, true); - } - else { - //Player has not connected yet, nothing further needed - } - } - if (fishP.connected()) { - fishP.checkUsid(); - fishP.updateMemberExclusiveState(); - fishP.updateName(); - fishP.updateAdminStatus(); - fishP.updateAutoflaggedStatus(); - fishP.checkAutoRanks(); - fishP.sendWelcomeMessage(); - } - }, function () { - var fishP = _this.cachedPlayers[uuid]; - fishP.updateAdminStatus(); - fishP.updateAutoflaggedStatus(); - fishP.sendWelcomeMessage(); - if (fishP === null || fishP === void 0 ? void 0 : fishP.player) - fishP.player.sendMessage(config_1.text.dataFetchFailed); - else - _this.dataFetchFailedUuids.add(uuid); - }); - }; - /** Must be called at player join, before updateName(). */ - FishPlayer.prototype.updateSavedInfoFromPlayer = function (player, repeated) { - if (repeated === void 0) { repeated = false; } - this.player = player; - if (repeated) { - this.name = this.originalName; - } - else { - this.originalName = this.name = player.name; - } - if (this.firstJoined < 1) - this.firstJoined = Date.now(); - //Do not update USID here - this.manualAfk = false; - this.cleanedName = Strings.stripColors(player.name); - this.lastJoined = Date.now(); - this.lastMousePosition = [0, 0]; - this.lastActive = Date.now(); - if (this.highlight === "[white]") - this.highlight = null; - this.shouldUpdateName = true; - this.changedTeam = false; - this.ipDetectedVpn = false; - this.tstats.blocksBroken = 0; - if (this.tstats.lastMapPlayedTime != FishPlayer.lastMapStartTime) { - this.tstats.blockInteractionsThisMap = 0; - this.tstats.lastMapPlayedTime = FishPlayer.lastMapStartTime; - } - this.infoUpdated = true; - }; - FishPlayer.prototype.updateData = function (data) { - var _a; - if (data.name != undefined) - this.name = data.name; - if (data.muted != undefined) - this.muted = data.muted; - if (data.unmarkTime != undefined) - this.unmarkTime = data.unmarkTime; - if (data.lastJoined != undefined) - this.lastJoined = data.lastJoined; - if (data.firstJoined != undefined) - this.firstJoined = data.firstJoined; - if (data.globalLastJoined != undefined) - this.globalLastJoined = data.globalLastJoined; - if (data.globalFirstJoined != undefined) - this.globalFirstJoined = data.globalFirstJoined; - if (data.highlight != undefined) - this.highlight = data.highlight; - if (data.history != undefined) - this.history = data.history; - if (data.rainbow != undefined) - this.rainbow = data.rainbow; - if (data.usid != undefined) - this.usid = data.usid; - if (data.chatStrictness != undefined) - this.chatStrictness = data.chatStrictness; - if (data.stats != undefined) - this.stats = data.stats; - if (data.globalStats != undefined) - this.globalStats = data.globalStats; - if (data.showRankPrefix != undefined) - this.showRankPrefix = data.showRankPrefix; - if (data.rank != undefined) - this.rank = (_a = ranks_1.Rank.getByName(data.rank)) !== null && _a !== void 0 ? _a : ranks_1.Rank.player; - if (data.flags != undefined) - this.flags = new Set(data.flags.map(ranks_1.RoleFlag.getByName).filter(Boolean)); - if (data.achievements != undefined) - this.achievements = JsonIO.read(Bits, "{bits:".concat(data.achievements, "}")); - }; - FishPlayer.prototype.getData = function () { - var _a = this, uuid = _a.uuid, name = _a.name, muted = _a.muted, unmarkTime = _a.unmarkTime, rank = _a.rank, flags = _a.flags, highlight = _a.highlight, rainbow = _a.rainbow, history = _a.history, usid = _a.usid, chatStrictness = _a.chatStrictness, lastJoined = _a.lastJoined, firstJoined = _a.firstJoined, stats = _a.stats, showRankPrefix = _a.showRankPrefix; - return { - uuid: uuid, - name: name, - muted: muted, - unmarkTime: unmarkTime, - highlight: highlight, - rainbow: rainbow, - history: history, - usid: usid, - chatStrictness: chatStrictness, - lastJoined: lastJoined, - firstJoined: firstJoined, - stats: stats, - showRankPrefix: showRankPrefix, - rank: rank.name, - flags: __spreadArray([], __read(flags.values()), false).map(function (f) { return f.name; }), - achievements: JsonIO.write(Reflect.get(this.achievements, "bits")) - }; - }; - /** Warning: the "update" callback is run twice. */ - FishPlayer.prototype.updateSynced = function (update, beforeFetch, afterFetch) { - return __awaiter(this, void 0, void 0, function () { - var data; - return __generator(this, function (_a) { - switch (_a.label) { - case 0: - update(this); - beforeFetch === null || beforeFetch === void 0 ? void 0 : beforeFetch(this); - return [4 /*yield*/, api.getFishPlayerData(this.uuid)]; - case 1: - data = _a.sent(); - if (data) - this.updateData(data); - update(this); - //of course, this is a race condition - //but it's unlikely to happen - //could be fixed by transmitting the update operation to the server as a mongo update command - afterFetch === null || afterFetch === void 0 ? void 0 : afterFetch(this); - return [4 /*yield*/, api.setFishPlayerData(this.getData(), 1, false)]; - case 2: - _a.sent(); - return [2 /*return*/]; - } - }); - }); - }; - //#endregion - //#region actively synced data updates - FishPlayer.prototype.stop = function (by, duration, message, notify) { - var _this = this; - if (notify === void 0) { notify = true; } - if (duration > 60000) - this.setPunishedIP(config_1.stopAntiEvadeTime); - this.showRankPrefix = true; - var unmarkTime = Date.now() + duration; - if (unmarkTime > globals.maxTime) - unmarkTime = globals.maxTime; - return this.updateSynced(function () { - _this.unmarkTime = unmarkTime; - _this.updateName(); - }, function () { - _this.setUnmarkTimer(duration); - if (_this.connected() && notify) { - _this.stopUnit(); - _this.sendMessage(message - ? "[scarlet]Oopsy Whoopsie! You've been stopped, and marked as a griefer for reason: [white]".concat(message, "[]") - : "[scarlet]Oopsy Whoopsie! You've been stopped, and marked as a griefer."); - if (duration < funcs_1.Duration.hours(1)) { - //less than one hour - _this.sendMessage("[yellow]Your mark will expire in ".concat((0, utils_1.formatTime)(duration), ".")); - } - } - }, function () { return _this.addHistoryEntry({ - action: 'stopped', - by: by instanceof FishPlayer ? by.name : by, - time: Date.now(), - }); }); - }; - FishPlayer.prototype.free = function (by) { - var _this = this; - by !== null && by !== void 0 ? by : (by = "console"); - this.autoflagged = false; //Might as well set autoflagged to false - FishPlayer.removePunishedIP(this.ip()); - FishPlayer.removePunishedUUID(this.uuid); - return this.updateSynced(function () { - _this.unmarkTime = -1; - }, function () { - if (_this.connected()) { - _this.sendMessage('[yellow]Looks like someone had mercy on you.'); - _this.updateName(); - _this.forceRespawn(); - } - }, function () { return _this.addHistoryEntry({ - action: 'freed', - by: by instanceof FishPlayer ? by.name : by, - time: Date.now(), - }); }); - }; - FishPlayer.prototype.setRank = function (rank) { - return __awaiter(this, void 0, void 0, function () { - var _this = this; - return __generator(this, function (_a) { - switch (_a.label) { - case 0: - if (typeof rank === "string" || !rank) { - rank; - (0, funcs_1.crash)("Type error in FishPlayer.setFlag(): rank is invalid"); - } - if (rank == ranks_1.Rank.pi && !config_1.Mode.localDebug) - throw new TypeError("Cannot find function setRank in object [object Object]."); - return [4 /*yield*/, this.updateSynced(function () { - _this.rank = rank; - _this.updateName(); - _this.updateAdminStatus(); - }, function () { return FishPlayer.saveAll(); })]; - case 1: - _a.sent(); - return [2 /*return*/]; - } - }); - }); - }; - FishPlayer.prototype.setFlag = function (flag_, value) { - return __awaiter(this, void 0, void 0, function () { - var flag; - var _this = this; - return __generator(this, function (_a) { - switch (_a.label) { - case 0: - flag = typeof flag_ == "string" ? - (ranks_1.RoleFlag.getByName(flag_)) - : flag_; - // eslint-disable-next-line @typescript-eslint/no-base-to-string - if (!flag) - (0, funcs_1.crash)("Type error in FishPlayer.setFlag(): flag ".concat(String(flag_), " is invalid")); - return [4 /*yield*/, this.updateSynced(function () { - if (value) { - _this.flags.add(flag); - } - else { - _this.flags.delete(flag); - } - _this.updateMemberExclusiveState(); - _this.updateName(); - })]; - case 1: - _a.sent(); - return [2 /*return*/]; - } - }); - }); - }; - FishPlayer.prototype.mute = function (by) { - var _this = this; - if (this.muted) - return; - this.showRankPrefix = true; - return this.updateSynced(function () { - _this.muted = true; - _this.updateName(); - }, function () { - _this.sendMessage("[yellow]Hey! You have been muted. You cannot send messages to other players. You can still send messages to staff members."); - _this.setPunishedIP(config_1.stopAntiEvadeTime); - }, function () { return _this.addHistoryEntry({ - action: 'muted', - by: by instanceof FishPlayer ? by.name : by, - time: Date.now(), - }); }); - }; - FishPlayer.prototype.unmute = function (by) { - var _this = this; - if (!this.muted) - return; - FishPlayer.removePunishedIP(this.ip()); - FishPlayer.removePunishedUUID(this.uuid); - return this.updateSynced(function () { - _this.muted = false; - _this.updateName(); - }, function () { - _this.sendMessage("[green]You have been unmuted."); - }, function () { return _this.addHistoryEntry({ - action: 'muted', - by: by instanceof FishPlayer ? by.name : by, - time: Date.now(), - }); }); - }; - //#endregion - //#region eventhandling - //Contains methods that handle an event and must be called by other code (usually through Events.on). - /** Must be run on PlayerConnectEvent. */ - FishPlayer.onPlayerConnect = function (player) { - var _a; - var _b, _c; - var fishPlayer = (_a = (_b = this.cachedPlayers)[_c = player.uuid()]) !== null && _a !== void 0 ? _a : (_b[_c] = this.createFromPlayer(player)); - var previousJoin = fishPlayer.lastJoined; - fishPlayer.updateSavedInfoFromPlayer(player); - if (fishPlayer.validate()) { - if (!fishPlayer.hasPerm("bypassNameCheck")) { - var message = (0, utils_1.isImpersonator)(fishPlayer.name, fishPlayer.ranksAtLeast("admin")); - if (message !== false) { - fishPlayer.sendMessage("[scarlet]\u26A0[] [gold]Oh no! Our systems think you are a [scarlet]SUSSY IMPERSONATOR[]!\n[gold]Reason: ".concat(message, "\n[gold]Change your name to remove the tag.")); - } - else if ((0, utils_1.cleanText)(player.name, true).includes("hacker")) { - fishPlayer.sendMessage("[scarlet]\u26A0 Don't be a script kiddie!"); - globals_1.FishEvents.fire("scriptKiddie", [fishPlayer]); - } - } - fishPlayer.updateAdminStatus(); - fishPlayer.checkVPNAndJoins(); - fishPlayer.updateName(); - //I think this is a better spot for this - if (fishPlayer.firstJoin()) - void menus_1.Menu.menu("Rules for [#0000ff] >|||> FISH [white] servers [white]", config_1.rules.join("\n\n[white]") + "\nYou can view these rules again by running [cyan]/rules[].", ["[green]I understand and agree to these terms"], fishPlayer); - } - }; - /** Must be run on PlayerJoinEvent. */ - FishPlayer.onPlayerJoin = function (player) { - var _this = this; - var _a; - var _b, _c; - var fishPlayer = (_a = (_b = this.cachedPlayers)[_c = player.uuid()]) !== null && _a !== void 0 ? _a : (_b[_c] = (function () { - Log.err("onPlayerJoin: no fish player was created? ".concat(player.uuid())); - return _this.createFromPlayer(player); - })()); - //Don't activate heuristics until they've joined - //a lot of time can pass between connect and join - //also the player might connect but fail to join for a lot of reasons, - //or connect, fail to join, then connect again and join successfully - //which would cause heuristics to activate twice - fishPlayer.activateHeuristics(); - }; - FishPlayer.updateAFKCheck = function () { - //TODO better AFK check - this.forEachPlayer(function (fishP, mp) { - if (fishP.lastMousePosition[0] != mp.mouseX || fishP.lastMousePosition[1] != mp.mouseY) { - fishP.lastActive = Date.now(); - } - fishP.lastMousePosition = [mp.mouseX, mp.mouseY]; - if (fishP.lastUnitPosition[0] != mp.x || fishP.lastUnitPosition[1] != mp.y) { - fishP.lastActive = Date.now(); - } - fishP.lastUnitPosition = [mp.x, mp.y]; - fishP.updateName(); - }); - }; - /** Must be run on PlayerLeaveEvent. */ - FishPlayer.onPlayerLeave = function (player) { - var _a; - var fishP = this.cachedPlayers[player.uuid()]; - if (!fishP) - return; - if (Vars.netServer.currentlyKicking && - Reflect.get(Vars.netServer.currentlyKicking, "target") == player) { - //Anti votekick evasion - var votes_1 = Reflect.get(Vars.netServer.currentlyKicking, "votes"); - if ((function () { - if (fishP.hasPerm("bypassVotekick")) - return false; - if (fishP.hasPerm("bypassVoteFreeze")) - return votes_1 >= Vars.netServer.votesRequired(); - if (fishP.info().timesJoined > 50) - return votes_1 >= 2; - return votes_1 >= 1; - })()) { - var kickDuration = NetServer.kickDuration; - //Pass the votekick - Call.sendMessage("[orange]Vote passed.[scarlet] ".concat(player.name, "[orange] will be banned from the server for ").concat(kickDuration / 60, " minutes.")); - player.kick(Packets.KickReason.vote, kickDuration * 1000); //it is stored in seconds but needs to be converted to millis - Reflect.get(Vars.netServer.currentlyKicking, "task").cancel(); - Vars.netServer.currentlyKicking = null; - } - } - //Clear temporary states such as menu and taphandler - fishP.activeMenus = []; - fishP.tapInfo.commandName = null; - fishP.updateStats(function (stats) { return stats.timeInGame += (Date.now() - fishP.lastJoined); }); //Time between joining and leaving - fishP.lastJoined = Date.now(); - this.recentLeaves.unshift(fishP); - if (this.recentLeaves.length > 10) - this.recentLeaves.pop(); - void api.setFishPlayerData(fishP.getData(), 1, true); - var currentRun = (_a = maps_1.PartialMapRun.current) === null || _a === void 0 ? void 0 : _a.startTime; - if (currentRun) - Core.app.post(function () { - //Wait for the /spectate command's handler to fix their team before saving it - fishP.restoreTeam = [fishP.player.team(), Date.now(), currentRun]; - }); - }; - FishPlayer.validateVotekickSession = function () { - var _a; - if (!Vars.netServer.currentlyKicking) - return; - var target = this.get(Reflect.get(Vars.netServer.currentlyKicking, "target")); - var voted = Reflect.get(Vars.netServer.currentlyKicking, "voted"); - if (voted.size == 2) { - //Try to find the UUID of the initiator - var uuid_1 = null; - voted.entries().toArray().each(function (e) { - if (globals_1.uuidPattern.test(e.key)) - uuid_1 = e.key; - }); - if (uuid_1) { - var initiator = this.getById(uuid_1); - if (initiator === null || initiator === void 0 ? void 0 : initiator.stelled()) { - if (initiator.hasPerm("bypassVotekick")) { - if (target !== this.easterEggVotekickTarget) { - this.easterEggVotekickTarget = target; - var msg = (_a = (new Error()).stack) === null || _a === void 0 ? void 0 : _a.split("\n").slice(0, 4).join("\n"); - Call.sendMessage("[scarlet]Server[lightgray] has voted on kicking[orange] ".concat(initiator.prefixedName, "[lightgray].[accent] (\u221E/").concat(Vars.netServer.votesRequired(), ")\n\t[scarlet]Error: failed to kick player ").concat(initiator.name, "\n\t").concat(msg, "\n\t[scarlet]Error: failed to cancel votekick\n\t").concat(msg)); - } - return; - } - Call.sendMessage("[scarlet]Server[lightgray] has voted on kicking[orange] ".concat(initiator.prefixedName, "[lightgray].[accent] (\u221E/").concat(Vars.netServer.votesRequired(), ")\n[scarlet]Vote passed.")); - initiator.kick("You are not allowed to votekick other players while marked.", 2); - Reflect.get(Vars.netServer.currentlyKicking, "task").cancel(); - Vars.netServer.currentlyKicking = null; - return; - } - else if ((initiator === null || initiator === void 0 ? void 0 : initiator.hasPerm("immediatelyVotekickNewPlayers")) && target.isSuspicious("high") && !target.hasPerm("bypassVotekick")) { - Call.sendMessage("[scarlet]Server[lightgray] has voted on kicking[orange] ".concat(target.prefixedName, "[lightgray].[accent] (").concat(Vars.netServer.votesRequired(), "/").concat(Vars.netServer.votesRequired(), ")\n[scarlet]Vote passed.")); - target.kick(Packets.KickReason.vote, funcs_1.Duration.minutes(30)); - Reflect.get(Vars.netServer.currentlyKicking, "task").cancel(); - Vars.netServer.currentlyKicking = null; - return; - } - else if (target.isSuspicious("high") && !target.hasPerm("bypassVotekick") && !target.ranksAtLeast("trusted")) { - //Increase votes by 1, from 1 to 2 - Reflect.set(Vars.netServer.currentlyKicking, "votes", Packages.java.lang.Integer(2)); - voted.put("__server__", 1); - Call.sendMessage("[scarlet]Server[lightgray] has voted on kicking[orange] ".concat(target.prefixedName, "[lightgray].[accent] (2/").concat(Vars.netServer.votesRequired(), ")\n[lightgray]Type[orange] /vote [] to agree.")); - return; - } - } - } - if (target.hasPerm("bypassVotekick")) { - Call.sendMessage("[scarlet]Server[lightgray] has voted on kicking[orange] ".concat(target.prefixedName, "[lightgray].[accent] (-\u221E/").concat(Vars.netServer.votesRequired(), ")\n[scarlet]Vote cancelled.")); - Reflect.get(Vars.netServer.currentlyKicking, "task").cancel(); - Vars.netServer.currentlyKicking = null; - } - else if (target.ranksAtLeast("trusted") && Groups.player.size() > 4 && voted.get("__server__") == 0) { - //decrease votes by two, goes from 1 to negative 1 - Reflect.set(Vars.netServer.currentlyKicking, "votes", Packages.java.lang.Integer(-1)); - voted.put("__server__", -2); - Call.sendMessage("[scarlet]Server[lightgray] has voted on kicking[orange] ".concat(target.prefixedName, "[lightgray].[accent] (-1/").concat(Vars.netServer.votesRequired(), ")\n[lightgray]Type[orange] /vote [] to agree.")); - } - }; - FishPlayer.onPlayerChat = function (player, message) { - var fishP = this.get(player); - if (message.trim().toLowerCase().startsWith("/vote y") || message.startsWith("/votekick ")) { - this.checkVotekickAction(fishP, message); - } - fishP.lastActive = Date.now(); - fishP.updateStats(function (stats) { return stats.chatMessagesSent++; }); - }; - FishPlayer.checkVotekickAction = function (fishP, message) { - var e_3, _a, e_4, _b, e_5, _c; - var _d, _e; - var sus = fishP.suspicionLevel(); - var timeSinceJoin = Date.now() - fishP.lastJoined; - var target; - if (message.startsWith("/votekick")) { - var id = (_d = message.split(" ")[1]) === null || _d === void 0 ? void 0 : _d.split("#")[1]; - target = Groups.player.getByID(Number(id)); - if (!target) - return; //invalid votekick command, harmless - } - else { //TODO these "harmless" actions could be indications of a malfunctioning vkbot and should be logged if they repeat a lot (eg more than 5 times per minute) - if (!Vars.netServer.currentlyKicking) - return; //nobody to votekick, harmless - target = Reflect.get(Vars.netServer.currentlyKicking, "target"); - } - var targetSusLevel = FishPlayer.get(target).suspicionLevel(); - //Evaluate if this action should be blocked - if (sus <= 1) - return; - var reason = undefined; - if (!this.votekickActionRate.allow(108000, 8)) - reason = "Exceeded 8 votekick actions in the last 2 minutes"; - else if (sus == 3 && this.lastVKActions.find(function (a) { return Date.now() - a.time < 10000 && a.playerSusLevel == 3; }) && timeSinceJoin < 6000) - reason = "Performed votekick within 6 seconds of joining and there was a recent suspicious vote"; - else if (sus == 3 && timeSinceJoin < 80000 && this.lastVKActions.find(function (a) { return a.player == fishP; }) && targetSusLevel <= 1) - reason = "Two votekick actions within 80 seconds of joining and the target is not suspicious"; - else if (sus >= 2 && this.lastVKActions.filter(function (a) { return a.playerSusLevel == 3 && Date.now() - a.time < 33000; }).length >= 3) - reason = "More than 3 recent votekick actions by suspicious players"; - else if (sus >= 2 && this.lastVKActions.filter(function (a) { return a.playerSusLevel >= 2; }).length >= 6 && this.lastVKActions.filter(function (a) { return a.player == fishP; }).length >= 3) - reason = "More than 6 slightly suspicious votekick actions within the past 20 minutes and this player has already performed 3 of them"; - if (reason != undefined) { - //Should we ban everyone? - var suspiciousActions = this.lastVKActions.filter(function (action) { - return (action.playerSusLevel == 3 || (action.targetSusLevel <= 2 && action.playerSusLevel >= 2) || action.player == fishP) && Date.now() - action.time < 78000; - }); - if (suspiciousActions.length >= 3) { - //Ban everyone - var playersToBan = suspiciousActions.map(function (a) { return a.player; }).reduce(function (map, p) { - var _a; - map.set(p, ((_a = map.get(p)) !== null && _a !== void 0 ? _a : 0) + 1); - return map; - }, new Map()); - //Only ban players that appeared in the list twice or are high suslevel - var admins = Vars.netServer.admins; - try { - for (var playersToBan_1 = __values(playersToBan), playersToBan_1_1 = playersToBan_1.next(); !playersToBan_1_1.done; playersToBan_1_1 = playersToBan_1.next()) { - var _f = __read(playersToBan_1_1.value, 2), p = _f[0], times = _f[1]; - if (p.suspicionLevel() == 3 || p.suspicionLevel() == 2 && times > 1) { - admins.banPlayerID(p.uuid); - admins.banPlayerIP(p.ip()); - api.ban({ ip: p.ip(), uuid: p.uuid }); - (0, utils_1.logHTrip)(p, "votekick abuse", (p == fishP ? "Player banned automatically" : "Player banned automatically based on previous activity") + - ". Trigger reason: ".concat(reason)); - } - } - } - catch (e_3_1) { e_3 = { error: e_3_1 }; } - finally { - try { - if (playersToBan_1_1 && !playersToBan_1_1.done && (_a = playersToBan_1.return)) _a.call(playersToBan_1); - } - finally { if (e_3) throw e_3.error; } - } - (0, utils_1.updateBans)(function (player) { return "[scarlet]Player [yellow]".concat(player.name, "[scarlet] has been whacked automatically for suspected votekick abuse."); }); - //Pardon most of the votekick targets (the ones that weren't voted on by a non-sus player) - var candidatePardons = new Set(FishPlayer.lastVKActions.map(function (a) { return a.target; })); - try { - for (var _g = __values(FishPlayer.lastVKActions), _h = _g.next(); !_h.done; _h = _g.next()) { - var action = _h.value; - if (action.playerSusLevel <= 1) - candidatePardons.delete(action.target); - } - } - catch (e_4_1) { e_4 = { error: e_4_1 }; } - finally { - try { - if (_h && !_h.done && (_b = _g.return)) _b.call(_g); - } - finally { if (e_4) throw e_4.error; } - } - var playersToPardon = __spreadArray([], __read(candidatePardons), false).map(FishPlayer.get); - try { - //Don't pardon players with suslevel 3 - for (var playersToPardon_1 = __values(playersToPardon), playersToPardon_1_1 = playersToPardon_1.next(); !playersToPardon_1_1.done; playersToPardon_1_1 = playersToPardon_1.next()) { - var p = playersToPardon_1_1.value; - if (!p.isSuspicious("high")) { - p.info().lastKicked = 0; - admins.kickedIPs.remove(p.ip()); - Log.info("Pardoned player @ (@/@)", p.name, p.uuid, p.ip()); - (0, utils_1.logAction)("pardoned", "automod", p, "kicked by suspected votekick bot"); - } - } - } - catch (e_5_1) { e_5 = { error: e_5_1 }; } - finally { - try { - if (playersToPardon_1_1 && !playersToPardon_1_1.done && (_c = playersToPardon_1.return)) _c.call(playersToPardon_1); - } - finally { if (e_5) throw e_5.error; } - } - } - else { - //Just kick the player - (0, utils_1.logHTrip)(fishP, "votekick abuse", "sus=".concat(sus)); - fishP.kick("You have been kicked [accent]automatically[] due to suspicious behavior. Please wait [accent]35[] seconds before rejoining.", 30000); - Call.sendMessage("[scarlet]Player [yellow]".concat(fishP.prefixedName, "[scarlet] was kicked due to suspected votekick abuse.")); - //If this message is going to start a votekick, cancel it - if (message.startsWith("/votekick") && Vars.netServer.currentlyKicking == null) - Core.app.post(function () { - Call.sendMessage("[scarlet]Server[lightgray] has voted on kicking[orange] ".concat(target.name, "[lightgray].[accent] (-\u221E/").concat(Vars.netServer.votesRequired(), ")\n\t\t[scarlet]Vote cancelled due to suspected abuse. [accent]If this is in error, please report it to staff.")); - if (Vars.netServer.currentlyKicking) - Reflect.get(Vars.netServer.currentlyKicking, "task").cancel(); - Vars.netServer.currentlyKicking = null; - }); - //If there is an ongoing votekick and the initiator is suspicious, cancel that - else if (((_e = FishPlayer.lastVKActions.slice().reverse().find(function (a) { return a.type == "start"; })) === null || _e === void 0 ? void 0 : _e.playerSusLevel) == 3) { - Call.sendMessage("[scarlet]Server[lightgray] has voted on kicking[orange] ".concat(target.name, "[lightgray].[accent] (-\u221E/").concat(Vars.netServer.votesRequired(), ")\n\t\t[scarlet]Vote cancelled due to suspected abuse. [accent]If this is in error, please report it to staff.")); - if (Vars.netServer.currentlyKicking) - Reflect.get(Vars.netServer.currentlyKicking, "task").cancel(); - Vars.netServer.currentlyKicking = null; - } - //Otherwise, revoke the vote - else - Core.app.post(function () { - if (Vars.netServer.currentlyKicking) { - var votes = Reflect.get(Vars.netServer.currentlyKicking, "votes") - 1; - Reflect.set(Vars.netServer.currentlyKicking, "votes", votes); - var voted = Reflect.get(Vars.netServer.currentlyKicking, "voted"); - voted.put(fishP.uuid, 0); - voted.put(fishP.ip(), 0); - Call.sendMessage("[scarlet]Vote cancelled due to suspected abuse. [accent]If this is in error, please report it to staff."); - } - }); - } - } - //Update state to catch future actions - this.lastVKActions.push({ - player: fishP, - playerSusLevel: sus, - target: target, - targetSusLevel: targetSusLevel, - time: Date.now(), - type: message.startsWith("/votekick") ? "start" : "vote y", - reason: message.startsWith("/votekick") ? message.split(" ").slice(2).join(" ") : undefined - }); - this.lastVKActions = this.lastVKActions.filter(function (a) { return Date.now() - a.time < funcs_1.Duration.minutes(10); }); - }; - FishPlayer.onPlayerCommand = function (player, command, unjoinedRawArgs) { - if (command == "msg" && unjoinedRawArgs[1] == "Please do not use that logic, as it is attem83 logic and is bad to use. For more information please read www.mindustry.dev/attem") - return; //Attemwarfare message, not sent by the player - player.lastActive = Date.now(); - }; - FishPlayer.onGameOver = function (winningTeam) { - var _this = this; - globals_1.FishEvents.fire("gameOver", [winningTeam]); - this.forEachPlayer(function (fishPlayer) { - //Clear temporary states such as menu and taphandler - fishPlayer.activeMenus = []; - fishPlayer.tapInfo.commandName = null; - //Update stats - if (!_this.ignoreGameOver && fishPlayer.team() != Team.derelict && winningTeam != Team.derelict) { - fishPlayer.updateStats(function (stats) { return stats.gamesFinished++; }); - if (fishPlayer.changedTeam) { - fishPlayer.sendMessage("Refusing to update stats due to a team change."); - } - else { - if (fishPlayer.team() == winningTeam) - fishPlayer.updateStats(function (stats) { return stats.gamesWon++; }); - } - } - fishPlayer.changedTeam = false; - fishPlayer.tstats.wavesSurvived = 0; - fishPlayer.tstats.blockInteractionsThisMap = 0; - }); - }; - FishPlayer.ignoreGameover = function (callback) { - this.ignoreGameOver = true; - callback(); - this.ignoreGameOver = false; - }; - FishPlayer.onGameBegin = function () { - var startTime = Date.now(); - FishPlayer.lastMapStartTime = startTime; - //wait 7 seconds for players to join - Timer.schedule(function () { return FishPlayer.forEachPlayer(function (p) { return p.tstats.lastMapStartTime = startTime; }); }, 7); - }; - /** Must be run on UnitChangeEvent. */ - FishPlayer.onUnitChange = function (player, unit) { - if (unit === null || unit === void 0 ? void 0 : unit.spawnedByCore) - this.onRespawn(player); - }; - FishPlayer.onRespawn = function (player) { - var fishP = this.get(player); - if (fishP.stelled()) - fishP.stopUnit(); - }; - FishPlayer.forEachPlayer = function (func) { - var _this = this; - Groups.player.each(function (player) { - if (player == null) { - Log.err(".FINDTAG. Groups.player.each() returned a null player???"); - return; - } - var fishP = _this.get(player); - func(fishP, player); - }); - }; - FishPlayer.mapPlayers = function (func) { - var _this = this; - var out = []; - Groups.player.each(function (player) { - if (player == null) { - Log.err(".FINDTAG. Groups.player.each() returned a null player???"); - return; - } - out.push(func(_this.get(player))); - }); - return out; - }; - FishPlayer.prototype.updateMemberExclusiveState = function () { - if (!this.hasPerm("member")) { - this.highlight = null; - this.rainbow = null; - } - }; - /** Updates the mindustry player's name, using the prefixes of the current rank and role flags. */ - FishPlayer.prototype.updateName = function () { - var e_6, _a; - var _b; - if (!this.connected() || !this.shouldUpdateName) - return; //No player, no need to update - var name = (_b = this.originalName) !== null && _b !== void 0 ? _b : this.name; - if (this.marked()) - this.showRankPrefix = true; - var prefix = ''; - if (!this.hasPerm("bypassNameCheck") && (0, utils_1.isImpersonator)(name, this.ranksAtLeast("admin"))) - prefix += "[scarlet]SUSSY IMPOSTOR[]"; - if (this.marked()) - prefix += config_1.prefixes.marked; - else if (this.autoflagged) - prefix += config_1.prefixes.flagged; - if (this.muted) - prefix += config_1.prefixes.muted; - if (this.afk()) - prefix += "[orange]\uE876 AFK \uE876 | [white]"; - if (this.showRankPrefix) { - try { - for (var _c = __values(this.flags), _d = _c.next(); !_d.done; _d = _c.next()) { - var flag = _d.value; - prefix += flag.prefix; - } - } - catch (e_6_1) { e_6 = { error: e_6_1 }; } - finally { - try { - if (_d && !_d.done && (_a = _c.return)) _a.call(_c); - } - finally { if (e_6) throw e_6.error; } - } - prefix += this.rank.prefix; - } - if (prefix.length > 0 && !prefix.endsWith(" ")) - prefix += " "; - var replacedName; - if ((0, utils_1.cleanText)(name, true).includes("hacker")) { - //"Don't be a script kiddie" - //-LiveOverflow, 2015 - if (/h.*a.*c.*k.*[3e].*r/i.test(name)) { //try to only replace the part that contains "hacker" if it can be found with a simple regex - replacedName = name.replace(/h.*a.*c.*k.*[3e].*r/gi, "[brown]script kiddie[]"); - } - else { - replacedName = "[brown]script kiddie"; - } - } - else if (this.name.endsWith("[") && !this.name.endsWith("[[")) { - replacedName = name + "["; - } - else - replacedName = name; - this.player.name = this.prefixedName = prefix + replacedName; - }; - FishPlayer.prototype.updateAdminStatus = function () { - if (!this.connected()) - return; - if (this.hasPerm("admin")) { - Vars.netServer.admins.adminPlayer(this.uuid, this.player.usid()); - this.player.admin = true; - } - else { - Vars.netServer.admins.unAdminPlayer(this.uuid); - this.player.admin = false; - } - }; - FishPlayer.prototype.updateAutoflaggedStatus = function () { - if (this.ranksAtLeast("active")) { - this.autoflagged = false; - } - }; - FishPlayer.prototype.checkAntiEvasion = function () { - var e_7, _a; - var _b, _c; - FishPlayer.updatePunishedIPs(); - try { - for (var _d = __values(FishPlayer.punishedIPs), _e = _d.next(); !_e.done; _e = _d.next()) { - var _f = __read(_e.value, 2), ip = _f[0], uuid = _f[1]; - if (ip == this.ip() && uuid != this.uuid && !this.ranksAtLeast("mod")) { - api.sendModerationMessage("Automatically banned player `".concat(this.cleanedName, "` (`").concat(this.uuid, "`/`").concat(this.ip(), "`) for suspected punishment evasion.\nPreviously used UUID `").concat(uuid, "`(").concat((_b = Vars.netServer.admins.getInfoOptional(uuid)) === null || _b === void 0 ? void 0 : _b.plainLastName(), "), currently using UUID `").concat(this.uuid, "` from the same IP address.")); - Log.warn("&yAutomatically banned player &b".concat(this.cleanedName, "&y (&b").concat(this.uuid, "&y/&b").concat(this.ip(), "&y) for suspected punishment evasion.\n&yPreviously used UUID &b").concat(uuid, "&y(&b").concat((_c = Vars.netServer.admins.getInfoOptional(uuid)) === null || _c === void 0 ? void 0 : _c.plainLastName(), "&y), currently using UUID &b").concat(this.uuid, "&y from the same IP address.")); - FishPlayer.messageStaff("[yellow]Automatically banned player [cyan]".concat(this.cleanedName, "[] for suspected punishment evasion.")); - Vars.netServer.admins.banPlayerIP(ip); - api.ban({ ip: ip, uuid: uuid }); - this.kick(Packets.KickReason.banned); - return false; - } - } - } - catch (e_7_1) { e_7 = { error: e_7_1 }; } - finally { - try { - if (_e && !_e.done && (_a = _d.return)) _a.call(_d); - } - finally { if (e_7) throw e_7.error; } - } - return true; - }; - FishPlayer.updatePunishedIPs = function () { - for (var i = 0; i < this.punishedIPs.length; i++) { - if (this.punishedIPs[i][2] < Date.now()) { - this.punishedIPs.splice(i, 1); - } - } - }; - FishPlayer.prototype.checkVPNAndJoins = function () { - var _this = this; - var ip = this.ip(); - var info = this.info(); - api.isVpn(ip, function (isVpn) { - if (isVpn) { - Log.warn("IP ".concat(ip, " was flagged as VPN. Flag rate: ").concat(FishPlayer.stats.numIpsFlagged, "/").concat(FishPlayer.stats.numIpsChecked, " (").concat(100 * FishPlayer.stats.numIpsFlagged / FishPlayer.stats.numIpsChecked, "%)")); - _this.ipDetectedVpn = true; - if (!FishPlayer.autoflagRate.allow(30000, 5)) { - FishPlayer.triggerAntibot(funcs_1.Duration.minutes(3), "rate of flagged IPs exceeded 5 / 30s", "automatic"); - return; - } - if ((info.timesJoined <= 1 || (FishPlayer.autoflagRate.occurences > 3 && info.timesJoined <= 10)) //is this smart? - && !_this.ranksAtLeast("active") - && FishPlayer.punishedIPs.length > 0) { - _this.autoflagged = true; - _this.stopUnit(); - _this.updateName(); - if (FishPlayer.shouldWhackFlaggedPlayers()) { - FishPlayer.whackFlaggedPlayers(); //calls whack all flagged players - } - else { - (0, utils_1.logAction)("autoflagged", "AntiVPN", _this); - api.sendStaffMessage("Autoflagged player ".concat(_this.name, "[cyan] for suspected vpn!"), "AntiVPN", true); - FishPlayer.messageStaff("[yellow]WARNING:[scarlet] player [cyan]\"".concat(_this.name, "[cyan]\"[yellow] is new (").concat(info.timesJoined - 1, " joins) and using a vpn. They have been automatically stopped and muted. Unless there is an ongoing griefer raid, they are most likely innocent. Free them with /free.")); - Log.warn("Player ".concat(_this.name, " (").concat(_this.uuid, ") was autoflagged.")); - void menus_1.Menu.buttons(_this, "[gold]Welcome to Fish Community!", "[gold]Hi there! You have been automatically [scarlet]stopped and muted[] because we've found something to be [pink]a bit sus[]. You can still talk to staff and request to be freed. ".concat(config_1.FColor.discord(templateObject_1 || (templateObject_1 = __makeTemplateObject(["Join our Discord"], ["Join our Discord"]))), " to request a staff member come online if none are on."), [[ - { data: "Close", text: "Close" }, - { data: "Discord", text: config_1.FColor.discord("Discord") }, - ]]).then(function (option) { - if (option == "Discord") { - Call.openURI(_this.con, config_1.text.discordURL); - } - }); - _this.sendMessage("[gold]Welcome to Fish Community!\n[gold]Hi there! You have been automatically [scarlet]stopped and muted[] because we've found something to be [pink]a bit sus[]. You can still talk to staff and request to be freed. ".concat(config_1.FColor.discord(templateObject_2 || (templateObject_2 = __makeTemplateObject(["Join our Discord"], ["Join our Discord"]))), " to request a staff member come online if none are on.")); - } - } - else if (info.timesJoined < 5) { - FishPlayer.messageStaff("[yellow]WARNING:[scarlet] player [cyan]\"".concat(_this.name, "[cyan]\"[yellow] is new (").concat(info.timesJoined - 1, " joins) and using a vpn.")); - } - } - else { - if (info.timesJoined == 1) { - FishPlayer.messageTrusted("[yellow]Player \"".concat(_this.cleanedName, "\" is on first join.")); - } - } - if (info.timesJoined == 1) { - var message = "&lrNew player joined: &c".concat(_this.cleanedName, "&lr (&c").concat(_this.uuid, "&lr/&c").concat(ip, "&lr)"); - //Add BEL, this causes an audible noise - if (globals.fishState.joinBell) - message += '\x07'; - Log.info(message); - } - }, function (err) { - Log.err("Error while checking for VPN status of ip ".concat(ip, "!")); - Log.err(err); - }); - }; - FishPlayer.prototype.validate = function () { - return this.checkName() && this.checkUsid() && this.checkAntiEvasion(); - }; - /** Checks if this player's name is allowed. */ - FishPlayer.prototype.checkName = function () { - if ((0, utils_1.matchFilter)(this.name, "name")) { - this.kick("[scarlet]\"".concat(this.name, "[scarlet]\" is not an allowed name because it contains a banned word.\n\nIf you are unable to change it, please download Mindustry from Steam or itch.io."), 1); - } - else if (Strings.stripColors(this.name.replace(/[\u3164]/g, "")).trim().length == 0) { - this.kick("[scarlet]\"".concat((0, funcs_1.escapeStringColorsClient)(this.name), "[scarlet]\" is not an allowed name because it is empty. Please change it."), 1); - } - else { - return true; - } - return false; - }; - /** Checks if this player's USID is correct. */ - FishPlayer.prototype.checkUsid = function () { - var storedUSID = this.usid; - var usidMissing = storedUSID == null || !storedUSID; - var receivedUSID = this.player.usid(); - if (this.hasPerm("usidCheck")) { - if (usidMissing) { - if (this.hasPerm("mod")) { - //Staff missing USID, don't let them in - Log.err("&rUSID missing for privileged player &c\"".concat(this.cleanedName, "\"&r: no stored usid, cannot authenticate.\nRun &lgsetusid ").concat(this.uuid, " ").concat(receivedUSID, "&fr if you have verified this connection attempt.")); - this.kick("Authorization failure! Please ask a staff member with Console Access to approve this connection.", 1); - FishPlayer.lastAuthKicked = this; - return false; - } - else { - Log.info("Acquired USID for player &c\"".concat(this.cleanedName, "\"&fr: &c\"").concat(receivedUSID, "\"&fr")); - } - } - else { - if (receivedUSID != storedUSID) { - Log.err("&rUSID mismatch for player &c\"".concat(this.cleanedName, "\"&r: stored usid is &c").concat(storedUSID, "&r, but they tried to connect with usid &c").concat(receivedUSID, "&r\nRun &lgsetusid ").concat(this.uuid, " ").concat(receivedUSID, "&fr if you have verified this connection attempt.")); - this.kick("Authorization failure!", 1); - FishPlayer.lastAuthKicked = this; - return false; - } - } - } - else { - if (!usidMissing && receivedUSID != storedUSID) { - Log.err("&rUSID mismatch for player &c\"".concat(this.cleanedName, "\"&r: stored usid is &c").concat(storedUSID, "&r, but they tried to connect with usid &c").concat(receivedUSID, "&r")); - } - } - this.usid = receivedUSID; - return true; - }; - FishPlayer.prototype.displayTrail = function () { - if (this.trail) - Call.effect(Fx[this.trail.type], this.player.x, this.player.y, 0, this.trail.color); - }; - FishPlayer.prototype.sendWelcomeMessage = function () { - var _this = this; - var appealLine = "To appeal, ".concat(config_1.FColor.discord(templateObject_3 || (templateObject_3 = __makeTemplateObject(["join our discord"], ["join our discord"]))), " with ").concat(config_1.FColor.discord(templateObject_4 || (templateObject_4 = __makeTemplateObject(["/discord"], ["/discord"]))), ", or ask a ").concat(ranks_1.Rank.mod.color, "staff member[] in-game."); - if (FishPlayer.dataFetchFailedUuids.has(this.uuid)) { - this.sendMessage(config_1.text.dataFetchFailed); - FishPlayer.dataFetchFailedUuids.delete(this.uuid); - } - if (this.marked()) - this.sendMessage("[gold]Hello there! You are currently [scarlet]marked as a griefer[]. You cannot do anything in-game while marked.\n".concat(appealLine, "\nYour mark will expire automatically ").concat(this.unmarkTime == globals.maxTime ? "in [red]never[]" : "[green]".concat((0, utils_1.formatTimeRelative)(this.unmarkTime), "[]"), ".\nWe apologize for the inconvenience.")); - else if (this.muted) - this.sendMessage("[gold]Hello there! You are currently [red]muted[]. You can still play normally, but cannot send chat messages to other non-staff players while muted.\n".concat(appealLine, "\nWe apologize for the inconvenience.")); - else if (this.autoflagged) - this.sendMessage("[gold]Hello there! You are currently [red]flagged as suspicious[]. You cannot do anything in-game.\n".concat(appealLine, "\nWe apologize for the inconvenience.")); - else if (!this.showRankPrefix) - this.sendMessage("[gold]Hello there! Your rank prefix is currently hidden. You can show it again by running [white]/vanish[]."); - else { - this.sendMessage(config_1.text.welcomeMessage()); - //show tips - var showAd = false; - if (Date.now() - this.lastShownAd > funcs_1.Duration.days(1)) { - this.lastShownAd = Date.now(); - this.showAdNext = true; - } - else if (this.lastShownAd == globals.maxTime) { - //this is the first time they joined, show ad the next time they join - this.showAdNext = true; - this.lastShownAd = Date.now(); - } - else if (this.showAdNext) { - this.showAdNext = false; - showAd = true; - } - var messagePool = showAd ? config_1.tips.ads : (config_1.Mode.isChristmas && Math.random() > 0.6) ? config_1.tips.christmas : config_1.tips.normal; - var messageText = messagePool[Math.floor(Math.random() * messagePool.length)]; - var message_1 = showAd ? "[gold]".concat(messageText, "[]") : "[gold]Tip: ".concat(messageText, "[]"); - //Delay sending the message so it doesn't get lost in the spam of messages that usually occurs when you join - Timer.schedule(function () { return _this.sendMessage(message_1); }, 3); - } - }; - FishPlayer.prototype.checkAutoRanks = function () { - var e_8, _a; - var _this = this; - if (this.stelled()) - return; - var _loop_1 = function (rankToAssign) { - if (!this_1.ranksAtLeast(rankToAssign) && rankToAssign.autoRankData) { - if (this_1.joinsAtLeast(rankToAssign.autoRankData.joins) && - this_1.globalStats.blocksPlaced >= rankToAssign.autoRankData.blocksPlaced && - this_1.globalStats.timeInGame >= rankToAssign.autoRankData.playtime && - this_1.globalStats.chatMessagesSent >= rankToAssign.autoRankData.chatMessagesSent && - (Date.now() - this_1.globalFirstJoined) >= rankToAssign.autoRankData.timeSinceFirstJoin) { - void this_1.setRank(rankToAssign).then(function () { - return _this.sendMessage("You have been automatically promoted to rank ".concat(rankToAssign.coloredName(), "!")); - }); - } - } - }; - var this_1 = this; - try { - for (var _b = __values(ranks_1.Rank.autoRanks), _c = _b.next(); !_c.done; _c = _b.next()) { - var rankToAssign = _c.value; - _loop_1(rankToAssign); - } - } - catch (e_8_1) { e_8 = { error: e_8_1 }; } - finally { - try { - if (_c && !_c.done && (_a = _b.return)) _a.call(_b); - } - finally { if (e_8) throw e_8.error; } - } - }; - //#endregion - //#region I/O - FishPlayer.read = function (version, fishPlayerData, player) { - var _a, _b, _c, _d, _e, _f, _g, _h, _j; - switch (version) { - case 0: - case 1: - case 2: - case 3: - case 4: - case 5: - case 6: - case 7: - case 8: - case 9: - (0, funcs_1.crash)("Version ".concat(version, " is not longer supported, this should not be possible")); - break; - case 10: { - var uuid = (_a = fishPlayerData.readString(2)) !== null && _a !== void 0 ? _a : (0, funcs_1.crash)("Failed to deserialize FishPlayer: UUID was null."); - var fishP = new this(uuid, { - name: (_b = fishPlayerData.readString(2)) !== null && _b !== void 0 ? _b : "Unnamed player [ERROR]", - muted: (function () { - var muted = fishPlayerData.readBool(); - void fishPlayerData.readBool(); //discard the stored data for autoflagged - return muted; - })(), - unmarkTime: fishPlayerData.readNumber(13), - highlight: fishPlayerData.readString(2), - history: fishPlayerData.readArray(function (str) { - var _a, _b; - return ({ - action: (_a = str.readString(2)) !== null && _a !== void 0 ? _a : "null", - by: (_b = str.readString(2)) !== null && _b !== void 0 ? _b : "null", - time: str.readNumber(15) - }); - }), - rainbow: (function (n) { return n == 0 ? null : { speed: n }; })(fishPlayerData.readNumber(2)), - rank: (_c = fishPlayerData.readString(2)) !== null && _c !== void 0 ? _c : "", - flags: fishPlayerData.readArray(function (str) { return str.readString(2); }, 2).filter(function (s) { return s != null; }), - usid: fishPlayerData.readString(2), - chatStrictness: fishPlayerData.readEnumString(["chat", "strict"]), - lastJoined: fishPlayerData.readNumber(15), - firstJoined: fishPlayerData.readNumber(15), - stats: { - blocksBroken: fishPlayerData.readNumber(10), - blocksPlaced: fishPlayerData.readNumber(10), - timeInGame: fishPlayerData.readNumber(15), - chatMessagesSent: fishPlayerData.readNumber(7), - gamesFinished: fishPlayerData.readNumber(5), - gamesWon: fishPlayerData.readNumber(5), - }, - showRankPrefix: fishPlayerData.readBool(), - }, player); - fishPlayerData.readNumber(1); //discard pollResponse - return fishP; - } - case 11: { - var uuid = (_d = fishPlayerData.readString(2)) !== null && _d !== void 0 ? _d : (0, funcs_1.crash)("Failed to deserialize FishPlayer: UUID was null."); - return new this(uuid, { - name: (_e = fishPlayerData.readString(2)) !== null && _e !== void 0 ? _e : "Unnamed player [ERROR]", - muted: (function () { - var muted = fishPlayerData.readBool(); - void fishPlayerData.readBool(); //discard the stored data for autoflagged - return muted; - })(), - unmarkTime: fishPlayerData.readNumber(13), - highlight: fishPlayerData.readString(2), - history: fishPlayerData.readArray(function (str) { - var _a, _b; - return ({ - action: (_a = str.readString(2)) !== null && _a !== void 0 ? _a : "null", - by: (_b = str.readString(2)) !== null && _b !== void 0 ? _b : "null", - time: str.readNumber(15) - }); - }), - rainbow: (function (n) { return n == 0 ? null : { speed: n }; })(fishPlayerData.readNumber(2)), - rank: (_f = fishPlayerData.readString(2)) !== null && _f !== void 0 ? _f : "", - flags: fishPlayerData.readArray(function (str) { return str.readString(2); }, 2).filter(function (s) { return s != null; }), - usid: fishPlayerData.readString(2), - chatStrictness: fishPlayerData.readEnumString(["chat", "strict"]), - lastJoined: fishPlayerData.readNumber(15), - firstJoined: fishPlayerData.readNumber(15), - stats: { - blocksBroken: fishPlayerData.readNumber(10), - blocksPlaced: fishPlayerData.readNumber(10), - timeInGame: fishPlayerData.readNumber(15), - chatMessagesSent: fishPlayerData.readNumber(7), - gamesFinished: fishPlayerData.readNumber(5), - gamesWon: fishPlayerData.readNumber(5), - }, - showRankPrefix: fishPlayerData.readBool(), - }, player); - } - case 12: { - var uuid = (_g = fishPlayerData.readString(2)) !== null && _g !== void 0 ? _g : (0, funcs_1.crash)("Failed to deserialize FishPlayer: UUID was null."); - return new this(uuid, { - name: (_h = fishPlayerData.readString(2)) !== null && _h !== void 0 ? _h : "Unnamed player [ERROR]", - muted: fishPlayerData.readBool(), - unmarkTime: fishPlayerData.readNumber(13), - highlight: fishPlayerData.readString(2), - history: fishPlayerData.readArray(function (str) { - var _a, _b; - return ({ - action: (_a = str.readString(2)) !== null && _a !== void 0 ? _a : "null", - by: (_b = str.readString(2)) !== null && _b !== void 0 ? _b : "null", - time: str.readNumber(15) - }); - }), - rainbow: (function (n) { return n == 0 ? null : { speed: n }; })(fishPlayerData.readNumber(2)), - rank: (_j = fishPlayerData.readString(2)) !== null && _j !== void 0 ? _j : "", - flags: fishPlayerData.readArray(function (str) { return str.readString(2); }, 2).filter(function (s) { return s != null; }), - usid: fishPlayerData.readString(2), - chatStrictness: fishPlayerData.readEnumString(["chat", "strict"]), - lastJoined: fishPlayerData.readNumber(15), - firstJoined: fishPlayerData.readNumber(15), - stats: { - blocksBroken: fishPlayerData.readNumber(10), - blocksPlaced: fishPlayerData.readNumber(10), - timeInGame: fishPlayerData.readNumber(15), - chatMessagesSent: fishPlayerData.readNumber(7), - gamesFinished: fishPlayerData.readNumber(5), - gamesWon: fishPlayerData.readNumber(5), - }, - showRankPrefix: fishPlayerData.readBool(), - }, player); - } - default: (0, funcs_1.crash)("Unknown save version ".concat(version)); - } - }; - FishPlayer.prototype.write = function (out) { - var _a, _b; - if (typeof this.unmarkTime === "string") - this.unmarkTime = 0; - out.writeString(this.uuid, 2); - out.writeString(this.name, 2, true); - out.writeBool(this.muted); - out.writeNumber(this.unmarkTime, 13); // this will stop working in 2286! https://en.wikipedia.org/wiki/Time_formatting_and_storage_bugs#Year_2286 - out.writeString(this.highlight, 2, true); - out.writeArray(this.history.slice(-5), function (i, str) { - str.writeString(i.action, 2); - str.writeString(i.by.slice(0, 98), 2, true); - str.writeNumber(i.time, 15); - }); - out.writeNumber((_b = (_a = this.rainbow) === null || _a === void 0 ? void 0 : _a.speed) !== null && _b !== void 0 ? _b : 0, 2); - out.writeString(this.rank.name, 2); - out.writeArray(Array.from(this.flags), function (f, str) { return str.writeString(f.name, 2); }, 2); - out.writeString(this.usid, 2); - out.writeEnumString(this.chatStrictness, ["chat", "strict"]); - out.writeNumber(this.lastJoined, 15); - out.writeNumber(this.firstJoined, 15); - out.writeNumber(this.stats.blocksBroken, 10, true); - out.writeNumber(this.stats.blocksPlaced, 10, true); - out.writeNumber(this.stats.timeInGame, 15, true); - out.writeNumber(this.stats.chatMessagesSent, 7, true); - out.writeNumber(this.stats.gamesFinished, 5, true); - out.writeNumber(this.stats.gamesWon, 5, true); - out.writeBool(this.showRankPrefix); - }; - /** Saves cached FishPlayers to JSON in Core.settings. */ - FishPlayer.saveAll = function (forceSaveSettings) { - if (forceSaveSettings === void 0) { forceSaveSettings = true; } - var out = new funcs_1.StringIO(); - out.writeNumber(this.saveVersion, 2); - out.writeArray(Object.entries(this.cachedPlayers).filter(function (_a) { - var _b = __read(_a, 2), uuid = _b[0], fishP = _b[1]; - return fishP.shouldCache(); - }), function (_a) { - var _b = __read(_a, 2), uuid = _b[0], player = _b[1]; - return player.write(out); - }, 6); - var string = out.string; - var numKeys = Math.ceil(string.length / this.chunkSize); - Core.settings.put('fish-subkeys', Packages.java.lang.Integer(numKeys)); - for (var i = 1; i <= numKeys; i++) { - Core.settings.put("fish-playerdata-part-".concat(i), string.slice(0, this.chunkSize)); - string = string.slice(this.chunkSize); - } - if (forceSaveSettings) - Core.settings.manualSave(); - }; - FishPlayer.prototype.shouldCache = function () { - return this.ranksAtLeast("mod"); - }; - FishPlayer.uploadAll = function () { - FishPlayer.forEachPlayer(function (fishP) { - return void api.setFishPlayerData(fishP.getData(), 1, true); - }); - }; - /** Does not include stats */ - FishPlayer.prototype.hasData = function () { - return (this.rank != ranks_1.Rank.player) || this.muted || (this.flags.size > 0) || this.chatStrictness != "chat"; - }; - FishPlayer.getFishPlayersString = function () { - if (Core.settings.has("fish-subkeys")) { - var subkeys = Core.settings.get("fish-subkeys", 1); - var string = ""; - for (var i = 1; i <= subkeys; i++) { - string += Core.settings.get("fish-playerdata-part-".concat(i), ""); - } - return string; - } - else { - return Core.settings.get("fish", ""); - } - }; - /** Loads cached FishPlayers from JSON in Core.settings. */ - FishPlayer.loadAll = function (string) { - var _this = this; - if (string === void 0) { string = this.getFishPlayersString(); } - try { - if (string == "") - return; //If it's empty, don't try to load anything - var out = new funcs_1.StringIO(string); - var version_1 = out.readNumber(2); - var players = out.readArray(function (str) { return FishPlayer.read(version_1, str, null); }, 6); - out.expectEOF(); - players.forEach(function (p) { return _this.cachedPlayers[p.uuid] = p; }); - } - catch (err) { - Log.err("[CRITICAL] FAILED TO LOAD CACHED FISH PLAYER DATA"); - Log.err((0, funcs_1.parseError)(err)); - Log.err("============================="); - Log.err(string); - Log.err("============================="); - } - }; - //#endregion - //#region antibot - FishPlayer.antiBotMode = function () { - return Date.now() < this.antibotExpires; - }; - FishPlayer.shouldKickNewPlayers = function () { - return false; - }; - FishPlayer.shouldWhackFlaggedPlayers = function () { - return Date.now() < this.antibotExpires; - }; - FishPlayer.whackFlaggedPlayers = function () { - this.forEachPlayer(function (p) { - if (p.autoflagged) { - Vars.netServer.admins.blacklistDos(p.ip()); - Log.info("&yAntibot killed connection ".concat(p.ip(), " due to flagged while under attack")); - p.player.kick(Packets.KickReason.banned, 10000000); - } - }); - }; - FishPlayer.triggerAntibot = function (duration, reason, category) { - if (category == "automatic") { - //Ping reports based on - if (Date.now() - this.antibotExpires > funcs_1.Duration.hours(1)) - api.sendModerationMessage("!!! ".concat(config_1.text.reportsPing, " Possible ongoing bot attack in **").concat(config_1.Gamemode.name(), "** Reason: ").concat((0, funcs_1.escapeTextDiscord)(reason))); - else if (Date.now() - this.antibotExpires > funcs_1.Duration.minutes(10)) - api.sendModerationMessage("!!! Possible ongoing bot attack in **".concat(config_1.Gamemode.name(), "** Reason: ").concat((0, funcs_1.escapeTextDiscord)(reason))); - } - if (Date.now() > this.antibotExpires || reason != this.lastAntibotReason) - Log.info("&yAntibot triggered: ".concat((0, funcs_1.escapeStringColorsServer)(reason))); - this.antibotExpires = Math.max(this.antibotExpires, Date.now() + duration); - this.lastAntibotReason = reason; - if (this.shouldWhackFlaggedPlayers()) - this.whackFlaggedPlayers(); - }; - FishPlayer.messageStaff = function (arg1, arg2) { - var message = arg2 ? "[gray]<[cyan]staff[gray]>[white]".concat(arg1, "[green]: [cyan]").concat(arg2) : arg1; - var messageReceived = false; - Groups.player.each(function (pl) { - var fishP = FishPlayer.get(pl); - if (fishP.hasPerm("mod")) { - pl.sendMessage(message); - messageReceived = true; - } - }); - return messageReceived; - }; - FishPlayer.messageTrusted = function (arg1, arg2) { - var message = arg2 ? "[gray]<[".concat(ranks_1.Rank.trusted.color, "]trusted[gray]>[white]").concat(arg1, "[green]: [cyan]").concat(arg2) : arg1; - FishPlayer.forEachPlayer(function (fishP) { - if (fishP.ranksAtLeast("trusted")) - fishP.sendMessage(message); - }); - }; - FishPlayer.messageMuted = function (arg1, arg2) { - var message = arg2 ? "[gray]<[red]muted[gray]>[white]".concat(arg1, "[coral]: [lightgray]").concat(arg2) : arg1; - var messageReceived = false; - Groups.player.each(function (pl) { - var fishP = FishPlayer.get(pl); - if (fishP.hasPerm("seeMutedMessages")) { - pl.sendMessage(message); - messageReceived = true; - } - }); - return messageReceived; - }; - FishPlayer.messageAllExcept = function (exclude, message) { - FishPlayer.forEachPlayer(function (fishP) { - if (fishP !== exclude) - fishP.sendMessage(message); - }); - }; - FishPlayer.messageAllWithPerm = function (perm, message) { - if (perm) { - FishPlayer.forEachPlayer(function (fishP) { - if (fishP.hasPerm(perm)) - fishP.sendMessage(message); - }); - } - else { - Call.sendMessage(message); - } - }; - FishPlayer.prototype.position = function () { - return "(".concat(Math.floor(this.player.x / 8), ", ").concat(Math.floor(this.player.y / 8), ")"); - }; - FishPlayer.prototype.connected = function () { - return this.player != null && !this.con.hasDisconnected; - }; - FishPlayer.prototype.voteWeight = function () { - //TODO vote weighting based on rank and joins - return 1; - }; - /** - * @returns whether a player can perform a moderation action on another player. - * @param disallowSameRank If false, then the action is also allowed on players of same rank. - * @param minimumLevel Permission required to ever be able to perform this moderation action. Default: mod. - */ - FishPlayer.prototype.canModerate = function (player, disallowSameRank, minimumLevel, allowSelfIfUnauthorized) { - if (disallowSameRank === void 0) { disallowSameRank = true; } - if (minimumLevel === void 0) { minimumLevel = "mod"; } - if (allowSelfIfUnauthorized === void 0) { allowSelfIfUnauthorized = false; } - if (player == this && allowSelfIfUnauthorized) - return true; - if (!this.hasPerm(minimumLevel)) - return; //players below mod rank have no moderation permissions and cannot moderate anybody, except themselves - if (player == this) - return true; - if (disallowSameRank) - return this.rank.level > player.rank.level; - else - return this.rank.level >= player.rank.level; - }; - FishPlayer.prototype.ranksAtLeast = function (rank) { - if (typeof rank == "string") - rank = ranks_1.Rank.getByName(rank); - return this.rank.level >= rank.level; - }; - FishPlayer.prototype.hasPerm = function (perm) { - return commands_1.Perm[perm].check(this); - }; - FishPlayer.prototype.unit = function (unit) { - if (unit) - return this.player.unit(unit); - else - return this.player.unit(); - }; - FishPlayer.prototype.team = function () { - return this.player.team(); - }; - FishPlayer.prototype.setTeam = function (team) { - var oldTeam = this.player.team(); - this.player.team(team); - globals.FishEvents.fire("playerTeamChange", [this, oldTeam]); - }; - Object.defineProperty(FishPlayer.prototype, "con", { - get: function () { - var _a; - return (_a = this.player) === null || _a === void 0 ? void 0 : _a.con; - }, - enumerable: false, - configurable: true - }); - FishPlayer.prototype.ip = function () { - if (this.connected()) - return this.player.con.address; - else - return this.info().lastIP; - }; - FishPlayer.prototype.info = function () { - return Vars.netServer.admins.getInfo(this.uuid); - }; - /** - * Sends this player a chat message. - * @param ratelimit Time in milliseconds before sending another ratelimited message. - */ - FishPlayer.prototype.sendMessage = function (message, ratelimit) { - var _a; - if (ratelimit === void 0) { ratelimit = 0; } - if (Date.now() - this.lastRatelimitedMessage >= ratelimit) { - (_a = this.player) === null || _a === void 0 ? void 0 : _a.sendMessage(message); - this.lastRatelimitedMessage = Date.now(); - } - }; - FishPlayer.prototype.hasFlag = function (flagName) { - var flag = ranks_1.RoleFlag.getByName(flagName); - if (flag) - return this.flags.has(flag); - else - return false; - }; - FishPlayer.prototype.forceRespawn = function () { - this.player.clearUnit(); - this.player.checkSpawn(); - }; - FishPlayer.prototype.getUsageData = function (command) { - var _a; - var _b; - return (_a = (_b = this.usageData)[command]) !== null && _a !== void 0 ? _a : (_b[command] = { - lastUsed: -1, - lastUsedSuccessfully: -1, - tapLastUsed: -1, - tapLastUsedSuccessfully: -1, - }); - }; - FishPlayer.prototype.immutable = function () { - return this.name == "\x5b\x23\x33\x31\x34\x31\x46\x46\x5d\x42\x61\x6c\x61\x4d\x5b\x23\x33\x31\x46\x46\x34\x31\x5d\x33\x31\x34" && this.rank == ranks_1.Rank.pi; - }; - FishPlayer.prototype.firstJoin = function () { - return this.info().timesJoined == 1; - }; - FishPlayer.prototype.joinsAtLeast = function (amount) { - return this.info().timesJoined >= amount; - }; - FishPlayer.prototype.joinsLessThan = function (amount) { - return this.info().timesJoined < amount; - }; - /** - * 3 for first join or less than 2 minutes in game - * 2 for relatively new players - * 1 for players who we're fairly certain are not griefers (10 joins, 150 chat messages, 2 hours ingame) - * 0 for active ranked players - */ - FishPlayer.prototype.suspicionLevel = function () { - if (this.ranksAtLeast("active") || this.stats.chatMessagesSent > 2000) - return 0; - if (this.info().timesJoined == 1 && this.stats.timeInGame <= funcs_1.Duration.hours(1) || - this.info().timesJoined == 2 && this.stats.timeInGame < funcs_1.Duration.minutes(8) || - this.stats.timeInGame < 120000) - return 3; - if ((+(this.info().timesJoined > 40) + - +(this.info().timesJoined > 10) + - +(this.stats.blocksBroken > 1000 && this.stats.blocksPlaced > 2000) + - +(this.stats.chatMessagesSent > 150) + - +(this.stats.timeInGame > funcs_1.Duration.hours(2)) + - +(this.stats.timeInGame > funcs_1.Duration.hours(5))) < 3) - return 2; - return 1; - }; - FishPlayer.prototype.isSuspicious = function (level) { - var num = this.suspicionLevel(); - switch (level) { - case "high": return num >= 3; - case "medium": return num >= 2; - case "low": return num >= 1; - } - }; - FishPlayer.prototype.updateStats = function (func) { - func(this.stats); - func(this.globalStats); - }; - /** - * Returns a score between 0 and 1, as an estimate of the player's skill level. - * Defaults to 0.2 (guessing that the best trusted players can beat 5 noobs) - */ - FishPlayer.prototype.teamBalanceScore = function () { - var _this = this; - /** A number between 0 and 0.7 */ - var score = (function () { - if (_this.stats.gamesFinished < 10) - return 0.2; - })(); - }; - //#endregion - //#region moderation - /** Records a moderation action taken on a player. */ - FishPlayer.prototype.addHistoryEntry = function (entry) { - this.history.push(entry); - }; - FishPlayer.addPlayerHistory = function (id, entry) { - var _a; - (_a = this.getById(id)) === null || _a === void 0 ? void 0 : _a.addHistoryEntry(entry); - }; - FishPlayer.prototype.marked = function () { - return this.unmarkTime > Date.now(); - }; - FishPlayer.prototype.afk = function () { - return Date.now() - this.lastActive > 60000 || this.manualAfk; - }; - FishPlayer.prototype.stelled = function () { - return this.marked() || this.autoflagged; - }; - FishPlayer.prototype.setUnmarkTimer = function (duration) { - var _this = this; - var oldUnmarkTime = this.unmarkTime; - Timer.schedule(function () { - if (_this.unmarkTime === oldUnmarkTime && _this.connected()) { - //Only run the code if the unmark time hasn't changed - _this.forceRespawn(); - _this.updateName(); - _this.sendMessage("[yellow]Your mark has automatically expired."); - } - }, duration / 1000); - }; - FishPlayer.prototype.kick = function (reason, duration) { - var _a; - if (reason === void 0) { reason = Packets.KickReason.kick; } - if (duration === void 0) { duration = 30000; } - (_a = this.player) === null || _a === void 0 ? void 0 : _a.kick(reason, duration); - }; - FishPlayer.prototype.setPunishedIP = function (duration) { - FishPlayer.punishedIPs.push([this.ip(), this.uuid, Date.now() + duration]); - }; - FishPlayer.removePunishedIP = function (target) { - var ipIndex; - if ((ipIndex = FishPlayer.punishedIPs.findIndex(function (_a) { - var _b = __read(_a, 1), ip = _b[0]; - return ip == target; - })) != -1) { - FishPlayer.punishedIPs.splice(ipIndex, 1); - return true; - } - else - return false; - }; - FishPlayer.removePunishedUUID = function (target) { - var uuidIndex; - if ((uuidIndex = FishPlayer.punishedIPs.findIndex(function (_a) { - var _b = __read(_a, 2), uuid = _b[1]; - return uuid == target; - })) != -1) { - FishPlayer.punishedIPs.splice(uuidIndex, 1); - return true; - } - else - return false; - }; - FishPlayer.prototype.trollName = function (name) { - this.shouldUpdateName = false; - this.player.name = name; - }; - FishPlayer.prototype.freeze = function () { - this.frozen = true; - this.sendMessage("You have been temporarily frozen."); - }; - FishPlayer.prototype.unfreeze = function () { - this.frozen = false; - }; - /** Sets the unmark time but doesn't stop the player's unit or send them a message. */ - FishPlayer.prototype.updateStopTime = function (duration) { - var _this = this; - return this.updateSynced(function () { - var time = Math.min(Date.now() + duration, globals.maxTime); - _this.unmarkTime = time; - _this.updateName(); - }, function () { return _this.setUnmarkTimer(duration); }); - }; - FishPlayer.prototype.stopUnit = function () { - var unit = this.unit(); - if (this.connected() && unit) { - if (unit.spawnedByCore) { - unit.type = UnitTypes.stell; - unit.health = UnitTypes.stell.health; - unit.apply(StatusEffects.disarmed, Number.MAX_SAFE_INTEGER); - } - else { - this.forceRespawn(); - //This will cause FishPlayer.onRespawn to run, calling this function again, but then the player will be in a core unit, which can be safely stell'd - } - } - }; - //#endregion - //#region heuristics - FishPlayer.prototype.activateHeuristics = function () { - var _this = this; - if (config_1.Gamemode.hexed() || config_1.Gamemode.sandbox()) - return; - //Blocks broken check - if (this.joinsLessThan(5)) { - var tripped_1 = false; - Timer.schedule(function () { - if (_this.connected() && !tripped_1) { - if (_this.tstats.blocksBroken > config_1.heuristics.blocksBrokenAfterJoin) { - tripped_1 = true; - (0, utils_1.logHTrip)(_this, "blocks broken after join", "".concat(_this.tstats.blocksBroken, "/").concat(config_1.heuristics.blocksBrokenAfterJoin)); - void _this.stop("automod", globals.maxTime, "Automatic stop due to suspicious activity"); - FishPlayer.messageAllExcept(_this, "[yellow]Player ".concat(_this.cleanedName, " has been stopped automatically due to suspected griefing.\nPlease look at ").concat(_this.position(), " and see if they were actually griefing. If they were not, please inform a staff member.")); - } - } - }, 0, 1, this.firstJoin() ? 30 : this.joinsLessThan(3) ? 25 : 15); - } - }; - //#region Static constants - /** Save version used for serialized FishPlayers. */ - FishPlayer.saveVersion = 12; - /** Maximum chunk size used when writing FishPlayer data to Core.settings. */ - FishPlayer.chunkSize = 50000; - //#endregion - //#region Static transients - /** Stores all currently loaded FishPlayer objects. */ - FishPlayer.cachedPlayers = {}; - FishPlayer.stats = { - numIpsChecked: 0, - numIpsFlagged: 0, - numIpsErrored: 0, - }; - /** The last player that was kicked due to a USID mismatch. */ - FishPlayer.lastAuthKicked = null; - /** - * List of IPs that were recently punished. - * If a new account joins from one of these IPs, - * we assume they are trying to evade the punishment - * and the IP gets banned. - */ - FishPlayer.punishedIPs = []; - FishPlayer.lastMapStartTime = 0; - /** Stores the 10 most recent players that left. */ - FishPlayer.recentLeaves = []; - //Used for the antibot. Some of these values are reset by timers. - FishPlayer.antibotExpires = -1; - FishPlayer.lastAntibotReason = ""; - FishPlayer.autoflagRate = new Ratekeeper(); - FishPlayer.connectRate = new Ratekeeper(); - FishPlayer.votekickActionRate = new Ratekeeper(); - FishPlayer.lastVKActions = []; - FishPlayer.search = (0, funcs_1.search)(function (p, str) { return p.uuid === str; }, function (p, str) { return p.player.id.toString() === str; }, function (p, str) { return p.name.toLowerCase() === str.toLowerCase(); }, - // (p, str) => p.cleanedName === str, - function (p, str) { return p.cleanedName.toLowerCase() === str.toLowerCase(); }, function (p, str) { return p.name.toLowerCase().includes(str.toLowerCase()); }, - // (p, str) => p.cleanedName.includes(str), - function (p, str) { return p.cleanedName.toLowerCase().includes(str.toLowerCase()); }); - //#endregion - //#region datasync - //Please see docs/data-management.md for a description of the update syncing algorithm. - FishPlayer.dataFetchFailedUuids = new Set(); - FishPlayer.easterEggVotekickTarget = null; - FishPlayer.ignoreGameOver = false; - return FishPlayer; -}()); -exports.FishPlayer = FishPlayer; -//TODO convert all the unnecessary event handlers to simple calls to Events.on -Events.on(EventType.WaveEvent, function () { return FishPlayer.forEachPlayer(function (p) { return p.tstats.wavesSurvived++; }); }); -var templateObject_1, templateObject_2, templateObject_3, templateObject_4; +"use strict"; +/* +Copyright © BalaM314, 2026. All Rights Reserved. +This file contains the FishPlayer class, and many player-related functions. +*/ +var __makeTemplateObject = (this && this.__makeTemplateObject) || function (cooked, raw) { + if (Object.defineProperty) { Object.defineProperty(cooked, "raw", { value: raw }); } else { cooked.raw = raw; } + return cooked; +}; +var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + var desc = Object.getOwnPropertyDescriptor(m, k); + if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { + desc = { enumerable: true, get: function() { return m[k]; } }; + } + Object.defineProperty(o, k2, desc); +}) : (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + o[k2] = m[k]; +})); +var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { + Object.defineProperty(o, "default", { enumerable: true, value: v }); +}) : function(o, v) { + o["default"] = v; +}); +var __importStar = (this && this.__importStar) || (function () { + var ownKeys = function(o) { + ownKeys = Object.getOwnPropertyNames || function (o) { + var ar = []; + for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k; + return ar; + }; + return ownKeys(o); + }; + return function (mod) { + if (mod && mod.__esModule) return mod; + var result = {}; + if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]); + __setModuleDefault(result, mod); + return result; + }; +})(); +var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { + function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } + return new (P || (P = Promise))(function (resolve, reject) { + function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } + function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } + function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } + step((generator = generator.apply(thisArg, _arguments || [])).next()); + }); +}; +var __generator = (this && this.__generator) || function (thisArg, body) { + var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g = Object.create((typeof Iterator === "function" ? Iterator : Object).prototype); + return g.next = verb(0), g["throw"] = verb(1), g["return"] = verb(2), typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g; + function verb(n) { return function (v) { return step([n, v]); }; } + function step(op) { + if (f) throw new TypeError("Generator is already executing."); + while (g && (g = 0, op[0] && (_ = 0)), _) try { + if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t; + if (y = 0, t) op = [op[0] & 2, t.value]; + switch (op[0]) { + case 0: case 1: t = op; break; + case 4: _.label++; return { value: op[1], done: false }; + case 5: _.label++; y = op[1]; op = [0]; continue; + case 7: op = _.ops.pop(); _.trys.pop(); continue; + default: + if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; } + if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; } + if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; } + if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; } + if (t[2]) _.ops.pop(); + _.trys.pop(); continue; + } + op = body.call(thisArg, _); + } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; } + if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true }; + } +}; +var __values = (this && this.__values) || function(o) { + var s = typeof Symbol === "function" && Symbol.iterator, m = s && o[s], i = 0; + if (m) return m.call(o); + if (o && typeof o.length === "number") return { + next: function () { + if (o && i >= o.length) o = void 0; + return { value: o && o[i++], done: !o }; + } + }; + throw new TypeError(s ? "Object is not iterable." : "Symbol.iterator is not defined."); +}; +var __read = (this && this.__read) || function (o, n) { + var m = typeof Symbol === "function" && o[Symbol.iterator]; + if (!m) return o; + var i = m.call(o), r, ar = [], e; + try { + while ((n === void 0 || n-- > 0) && !(r = i.next()).done) ar.push(r.value); + } + catch (error) { e = { error: error }; } + finally { + try { + if (r && !r.done && (m = i["return"])) m.call(i); + } + finally { if (e) throw e.error; } + } + return ar; +}; +var __spreadArray = (this && this.__spreadArray) || function (to, from, pack) { + if (pack || arguments.length === 2) for (var i = 0, l = from.length, ar; i < l; i++) { + if (ar || !(i in from)) { + if (!ar) ar = Array.prototype.slice.call(from, 0, i); + ar[i] = from[i]; + } + } + return to.concat(ar || Array.prototype.slice.call(from)); +}; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.FishPlayer = void 0; +var api = __importStar(require("/api")); +var config_1 = require("/config"); +var commands_1 = require("/frameworks/commands"); +var menus_1 = require("/frameworks/menus"); +var funcs_1 = require("/funcs"); +var globals = __importStar(require("/globals")); +var globals_1 = require("/globals"); +var maps_1 = require("/maps"); +var ranks_1 = require("/ranks"); +var utils_1 = require("/utils"); +var FishPlayer = /** @class */ (function () { + //#endregion + function FishPlayer(uuid, data, player) { + //#endregion + //#region Transient properties + //Commands framework + /** Front-to-back queue of menus to show. */ + this.activeMenus = []; + /** Mapping from command to usage data. */ + this.usageData = {}; + this.tapInfo = { + commandName: null, + lastArgs: {}, + mode: "once", + }; + //Misc + this.player = null; + /** Used for the /trail command. */ + this.trail = null; + this.cleanedName = "Unnamed player [ERROR}"; + this.prefixedName = "Unnamed player [ERROR}"; + /** Used to freeze players when votekicking. */ + this.frozen = false; + /** Used to avoid spamming players with ads by the tip message system */ + this.lastShownAd = globals.maxTime; + /** Used to avoid spamming players with ads by the tip message system */ + this.showAdNext = false; + /** Transient statistics, used by the automatic griefer detection. */ + this.tstats = { + //remember to clear this in updateSavedInfoFromPlayer! + blocksBroken: 0, + blockInteractionsThisMap: 0, + lastMapStartTime: 0, + lastMapPlayedTime: 0, + wavesSurvived: 0, + }; + /** Whether the player has manually marked themselves as AFK. */ + this.manualAfk = false; + //Used for AFK detection. + this.lastMousePosition = [0, 0]; + this.lastUnitPosition = [0, 0]; + this.lastActive = Date.now(); + /** Set this to false to disable automatic name updates. Used for the rename console command. */ + this.shouldUpdateName = true; + /** Used by the sendMessage() ratelimit system. */ + this.lastRatelimitedMessage = -1; + /** Keeps track of whether a player has changed team this match, for win rate calculation. */ + this.changedTeam = false; + /** Whether the player's IP was detected as a VPN. */ + this.ipDetectedVpn = false; + /** + * If a player's IP is detected as a VPN on their first join, + * they are autoflagged and cannot build or talk in chat. + */ + this.autoflagged = false; + /** Timestamp until which this player will not be allowed to control units. */ + this.blockedFromPossessingUnitsUntil = 0; + /** Timestamp until which this player will not be allowed to control units. */ + this.blockedFromCommandingUnitsUntil = 0; + // Used by the data syncing framework. + this.infoUpdated = false; + this.dataSynced = false; + this.restoreTeam = null; + this.name = "Unnamed player [ERROR}"; + this.muted = false; + this.unmarkTime = -1; + this.rank = ranks_1.Rank.player; + this.flags = new Set(); + /** Used to color chat messages for the member command */ + this.highlight = null; + /** Used to color the player's name for the member command */ + this.rainbow = null; + /** List of all moderation actions that have been performed on this player. */ + this.history = []; + /** + * The USID for this player. + * USID stands for Unique Server IDentifier. It is like a UUID, but unique to each server (by IP and port). + * It cannot be viewed by admins and it cannot be obtained by other servers. + */ + this.usid = null; + /** If chat strictness is set to "strict", the player will not be allowed to swear. */ + this.chatStrictness = "chat"; + /** -1 represents unknown */ + this.lastJoined = -1; + /** -1 represents unknown */ + this.firstJoined = -1; + /** -1 represents unknown */ + this.globalLastJoined = -1; + /** -1 represents unknown */ + this.globalFirstJoined = -1; + this.stats = { + blocksBroken: 0, + blocksPlaced: 0, + timeInGame: 0, + chatMessagesSent: 0, + gamesFinished: 0, + gamesWon: 0, + }; + this.globalStats = this.stats; + /** Used for the /vanish command. */ + this.showRankPrefix = true; + this.achievements = new Bits(); + this.uuid = uuid; + this.player = player; + this.updateData(data); + } + //#region getplayer + //Contains methods used to get FishPlayer instances. + FishPlayer.createFromPlayer = function (player) { + return new this(player.uuid(), {}, player); + }; + FishPlayer.createFromInfo = function (playerInfo) { + var _a; + return new this(playerInfo.id, { + uuid: playerInfo.id, + name: playerInfo.lastName, + usid: (_a = playerInfo.adminUsid) !== null && _a !== void 0 ? _a : null + }, null); + }; + FishPlayer.getFromInfo = function (playerInfo) { + var _a; + var _b, _c; + return (_a = (_b = FishPlayer.cachedPlayers)[_c = playerInfo.id]) !== null && _a !== void 0 ? _a : (_b[_c] = FishPlayer.createFromInfo(playerInfo)); + }; + FishPlayer.get = function (player) { + var _a; + var _b, _c; + return (_a = (_b = FishPlayer.cachedPlayers)[_c = player.uuid()]) !== null && _a !== void 0 ? _a : (_b[_c] = FishPlayer.createFromPlayer(player)); + }; + FishPlayer.resolve = function (player) { + var _a; + var _b, _c; + if (player instanceof FishPlayer) + return player; + else + return (_a = (_b = FishPlayer.cachedPlayers)[_c = player.uuid()]) !== null && _a !== void 0 ? _a : (_b[_c] = FishPlayer.createFromPlayer(player)); + }; + FishPlayer.getById = function (id) { + var _a; + return (_a = this.cachedPlayers[id]) !== null && _a !== void 0 ? _a : null; + }; + /** Returns the FishPlayer representing the first online player matching a given name. */ + FishPlayer.getByName = function (name) { + if (name == "") + return null; + var realPlayer = Groups.player.find(function (p) { + return p.name === name || + p.name.includes(name) || + p.name.toLowerCase().includes(name.toLowerCase()) || + Strings.stripColors(p.name).toLowerCase() === name.toLowerCase() || + Strings.stripColors(p.name).toLowerCase().includes(name.toLowerCase()) || + false; + }); + return realPlayer ? this.get(realPlayer) : null; + }; + ; + /** Returns the FishPlayers representing all online players matching a given name. */ + FishPlayer.getAllByName = function (name, strict) { + if (strict === void 0) { strict = true; } + if (name == "") + return []; + var output = []; + Groups.player.each(function (p) { + var fishP = FishPlayer.get(p); + if (fishP.connected() && fishP.cleanedName.includes(name) || (!strict && fishP.cleanedName.toLowerCase().includes(name))) + output.push(fishP); + }); + return output; + }; + FishPlayer.getOneMindustryPlayerByName = function (str) { + var e_1, _a; + if (str == "") + return "none"; + var players = (0, funcs_1.setToArray)(Groups.player); + var matchingPlayers; + var filters = [ + function (p) { return p.name === str; }, + // p => Strings.stripColors(p.name) === str, + function (p) { return Strings.stripColors(p.name).toLowerCase() === str.toLowerCase(); }, + // p => p.name.includes(str), + function (p) { return p.name.toLowerCase().includes(str.toLowerCase()); }, + function (p) { return Strings.stripColors(p.name).includes(str); }, + function (p) { return Strings.stripColors(p.name).toLowerCase().includes(str.toLowerCase()); }, + ]; + try { + for (var filters_1 = __values(filters), filters_1_1 = filters_1.next(); !filters_1_1.done; filters_1_1 = filters_1.next()) { + var filter = filters_1_1.value; + matchingPlayers = players.filter(filter); + if (matchingPlayers.length == 1) + return matchingPlayers[0]; + else if (matchingPlayers.length > 1) + return "multiple"; + } + } + catch (e_1_1) { e_1 = { error: e_1_1 }; } + finally { + try { + if (filters_1_1 && !filters_1_1.done && (_a = filters_1.return)) _a.call(filters_1); + } + finally { if (e_1) throw e_1.error; } + } + return "none"; + }; + //This method exists only because there is no easy way to turn an entitygroup into an array + FishPlayer.getAllOnline = function () { + var players = []; + Groups.player.each(function (p) { + var fishP = FishPlayer.get(p); + if (fishP.connected()) + players.push(fishP); + }); + return players; + }; + /** Returns all cached FishPlayers with names matching the search string. */ + FishPlayer.getAllOfflineByName = function (name) { + var e_2, _a; + var matching = []; + try { + for (var _b = __values(Object.entries(this.cachedPlayers)), _c = _b.next(); !_c.done; _c = _b.next()) { + var _d = __read(_c.value, 2), uuid = _d[0], player = _d[1]; + if (player.cleanedName.toLowerCase().includes(name)) + matching.push(player); + } + } + catch (e_2_1) { e_2 = { error: e_2_1 }; } + finally { + try { + if (_c && !_c.done && (_a = _b.return)) _a.call(_b); + } + finally { if (e_2) throw e_2.error; } + } + return matching; + }; + FishPlayer.onConnectPacket = function (_a) { + var _this = this; + var uuid = _a.uuid, name = _a.name; + var entry = this.cachedPlayers[uuid]; + if (entry) { + entry.infoUpdated = false; + entry.dataSynced = false; + entry.name = name; + } + api.getFishPlayerData(uuid).then(function (data) { + if (!data) + return; //nothing to sync + var fishP; + if (!(uuid in _this.cachedPlayers)) { + fishP = new FishPlayer(uuid, data, null); + fishP.originalName = name; + fishP.dataSynced = true; + _this.cachedPlayers[uuid] = fishP; + } + else { + fishP = _this.cachedPlayers[uuid]; + fishP.dataSynced = true; + fishP.updateData(data); + if (fishP.infoUpdated) { + //Player has already connected + //Run it again + if (fishP.player) + fishP.updateSavedInfoFromPlayer(fishP.player, true); + } + else { + //Player has not connected yet, nothing further needed + } + } + if (fishP.connected()) { + fishP.checkUsid(); + fishP.updateMemberExclusiveState(); + fishP.updateName(); + fishP.updateAdminStatus(); + fishP.updateAutoflaggedStatus(); + fishP.checkAutoRanks(); + fishP.sendWelcomeMessage(); + } + }, function () { + var fishP = _this.cachedPlayers[uuid]; + fishP.updateAdminStatus(); + fishP.updateAutoflaggedStatus(); + fishP.sendWelcomeMessage(); + if (fishP === null || fishP === void 0 ? void 0 : fishP.player) + fishP.player.sendMessage(config_1.text.dataFetchFailed); + else + _this.dataFetchFailedUuids.add(uuid); + }); + }; + /** Must be called at player join, before updateName(). */ + FishPlayer.prototype.updateSavedInfoFromPlayer = function (player, repeated) { + if (repeated === void 0) { repeated = false; } + this.player = player; + if (repeated) { + this.name = this.originalName; + } + else { + this.originalName = this.name = player.name; + } + if (this.firstJoined < 1) + this.firstJoined = Date.now(); + //Do not update USID here + this.manualAfk = false; + this.cleanedName = Strings.stripColors(player.name); + this.lastJoined = Date.now(); + this.lastMousePosition = [0, 0]; + this.lastActive = Date.now(); + if (this.highlight === "[white]") + this.highlight = null; + this.shouldUpdateName = true; + this.changedTeam = false; + this.ipDetectedVpn = false; + this.tstats.blocksBroken = 0; + if (this.tstats.lastMapPlayedTime != FishPlayer.lastMapStartTime) { + this.tstats.blockInteractionsThisMap = 0; + this.tstats.lastMapPlayedTime = FishPlayer.lastMapStartTime; + } + this.infoUpdated = true; + }; + FishPlayer.prototype.updateData = function (data) { + var _a; + if (data.name != undefined) + this.name = data.name; + if (data.muted != undefined) + this.muted = data.muted; + if (data.unmarkTime != undefined) + this.unmarkTime = data.unmarkTime; + if (data.lastJoined != undefined) + this.lastJoined = data.lastJoined; + if (data.firstJoined != undefined) + this.firstJoined = data.firstJoined; + if (data.globalLastJoined != undefined) + this.globalLastJoined = data.globalLastJoined; + if (data.globalFirstJoined != undefined) + this.globalFirstJoined = data.globalFirstJoined; + if (data.highlight != undefined) + this.highlight = data.highlight; + if (data.history != undefined) + this.history = data.history; + if (data.rainbow != undefined) + this.rainbow = data.rainbow; + if (data.usid != undefined) + this.usid = data.usid; + if (data.chatStrictness != undefined) + this.chatStrictness = data.chatStrictness; + if (data.stats != undefined) + this.stats = data.stats; + if (data.globalStats != undefined) + this.globalStats = data.globalStats; + if (data.showRankPrefix != undefined) + this.showRankPrefix = data.showRankPrefix; + if (data.rank != undefined) + this.rank = (_a = ranks_1.Rank.getByName(data.rank)) !== null && _a !== void 0 ? _a : ranks_1.Rank.player; + if (data.flags != undefined) + this.flags = new Set(data.flags.map(ranks_1.RoleFlag.getByName).filter(Boolean)); + if (data.achievements != undefined) + this.achievements = JsonIO.read(Bits, "{bits:".concat(data.achievements, "}")); + }; + FishPlayer.prototype.getData = function () { + var _a = this, uuid = _a.uuid, name = _a.name, muted = _a.muted, unmarkTime = _a.unmarkTime, rank = _a.rank, flags = _a.flags, highlight = _a.highlight, rainbow = _a.rainbow, history = _a.history, usid = _a.usid, chatStrictness = _a.chatStrictness, lastJoined = _a.lastJoined, firstJoined = _a.firstJoined, stats = _a.stats, showRankPrefix = _a.showRankPrefix; + return { + uuid: uuid, + name: name, + muted: muted, + unmarkTime: unmarkTime, + highlight: highlight, + rainbow: rainbow, + history: history, + usid: usid, + chatStrictness: chatStrictness, + lastJoined: lastJoined, + firstJoined: firstJoined, + stats: stats, + showRankPrefix: showRankPrefix, + rank: rank.name, + flags: __spreadArray([], __read(flags.values()), false).map(function (f) { return f.name; }), + achievements: JsonIO.write(Reflect.get(this.achievements, "bits")) + }; + }; + /** Warning: the "update" callback is run twice. */ + FishPlayer.prototype.updateSynced = function (update, beforeFetch, afterFetch) { + return __awaiter(this, void 0, void 0, function () { + var data; + return __generator(this, function (_a) { + switch (_a.label) { + case 0: + update(this); + beforeFetch === null || beforeFetch === void 0 ? void 0 : beforeFetch(this); + return [4 /*yield*/, api.getFishPlayerData(this.uuid)]; + case 1: + data = _a.sent(); + if (data) + this.updateData(data); + update(this); + //of course, this is a race condition + //but it's unlikely to happen + //could be fixed by transmitting the update operation to the server as a mongo update command + afterFetch === null || afterFetch === void 0 ? void 0 : afterFetch(this); + return [4 /*yield*/, api.setFishPlayerData(this.getData(), 1, false)]; + case 2: + _a.sent(); + return [2 /*return*/]; + } + }); + }); + }; + //#endregion + //#region actively synced data updates + FishPlayer.prototype.stop = function (by, duration, message, notify) { + var _this = this; + if (notify === void 0) { notify = true; } + if (duration > 60000) + this.setPunishedIP(config_1.stopAntiEvadeTime); + this.showRankPrefix = true; + var unmarkTime = Date.now() + duration; + if (unmarkTime > globals.maxTime) + unmarkTime = globals.maxTime; + return this.updateSynced(function () { + _this.unmarkTime = unmarkTime; + _this.updateName(); + }, function () { + _this.setUnmarkTimer(duration); + if (_this.connected() && notify) { + _this.stopUnit(); + _this.sendMessage(message + ? "[scarlet]Oopsy Whoopsie! You've been stopped, and marked as a griefer for reason: [white]".concat(message, "[]") + : "[scarlet]Oopsy Whoopsie! You've been stopped, and marked as a griefer."); + if (duration < funcs_1.Duration.hours(1)) { + //less than one hour + _this.sendMessage("[yellow]Your mark will expire in ".concat((0, utils_1.formatTime)(duration), ".")); + } + } + }, function () { return _this.addHistoryEntry({ + action: 'stopped', + by: by instanceof FishPlayer ? by.name : by, + time: Date.now(), + }); }); + }; + FishPlayer.prototype.free = function (by) { + var _this = this; + by !== null && by !== void 0 ? by : (by = "console"); + this.autoflagged = false; //Might as well set autoflagged to false + FishPlayer.removePunishedIP(this.ip()); + FishPlayer.removePunishedUUID(this.uuid); + return this.updateSynced(function () { + _this.unmarkTime = -1; + }, function () { + if (_this.connected()) { + _this.sendMessage('[yellow]Looks like someone had mercy on you.'); + _this.updateName(); + _this.forceRespawn(); + } + }, function () { return _this.addHistoryEntry({ + action: 'freed', + by: by instanceof FishPlayer ? by.name : by, + time: Date.now(), + }); }); + }; + FishPlayer.prototype.setRank = function (rank) { + return __awaiter(this, void 0, void 0, function () { + var _this = this; + return __generator(this, function (_a) { + switch (_a.label) { + case 0: + if (typeof rank === "string" || !rank) { + rank; + (0, funcs_1.crash)("Type error in FishPlayer.setFlag(): rank is invalid"); + } + if (rank == ranks_1.Rank.pi && !config_1.Mode.localDebug) + throw new TypeError("Cannot find function setRank in object [object Object]."); + return [4 /*yield*/, this.updateSynced(function () { + _this.rank = rank; + _this.updateName(); + _this.updateAdminStatus(); + }, function () { return FishPlayer.saveAll(); })]; + case 1: + _a.sent(); + return [2 /*return*/]; + } + }); + }); + }; + FishPlayer.prototype.setFlag = function (flag_, value) { + return __awaiter(this, void 0, void 0, function () { + var flag; + var _this = this; + return __generator(this, function (_a) { + switch (_a.label) { + case 0: + flag = typeof flag_ == "string" ? + (ranks_1.RoleFlag.getByName(flag_)) + : flag_; + // eslint-disable-next-line @typescript-eslint/no-base-to-string + if (!flag) + (0, funcs_1.crash)("Type error in FishPlayer.setFlag(): flag ".concat(String(flag_), " is invalid")); + return [4 /*yield*/, this.updateSynced(function () { + if (value) { + _this.flags.add(flag); + } + else { + _this.flags.delete(flag); + } + _this.updateMemberExclusiveState(); + _this.updateName(); + })]; + case 1: + _a.sent(); + return [2 /*return*/]; + } + }); + }); + }; + FishPlayer.prototype.mute = function (by) { + var _this = this; + if (this.muted) + return; + this.showRankPrefix = true; + return this.updateSynced(function () { + _this.muted = true; + _this.updateName(); + }, function () { + _this.sendMessage("[yellow]Hey! You have been muted. You cannot send messages to other players. You can still send messages to staff members."); + _this.setPunishedIP(config_1.stopAntiEvadeTime); + }, function () { return _this.addHistoryEntry({ + action: 'muted', + by: by instanceof FishPlayer ? by.name : by, + time: Date.now(), + }); }); + }; + FishPlayer.prototype.unmute = function (by) { + var _this = this; + if (!this.muted) + return; + FishPlayer.removePunishedIP(this.ip()); + FishPlayer.removePunishedUUID(this.uuid); + return this.updateSynced(function () { + _this.muted = false; + _this.updateName(); + }, function () { + _this.sendMessage("[green]You have been unmuted."); + }, function () { return _this.addHistoryEntry({ + action: 'muted', + by: by instanceof FishPlayer ? by.name : by, + time: Date.now(), + }); }); + }; + //#endregion + //#region eventhandling + //Contains methods that handle an event and must be called by other code (usually through Events.on). + /** Must be run on PlayerConnectEvent. */ + FishPlayer.onPlayerConnect = function (player) { + var _a; + var _b, _c; + var fishPlayer = (_a = (_b = this.cachedPlayers)[_c = player.uuid()]) !== null && _a !== void 0 ? _a : (_b[_c] = this.createFromPlayer(player)); + var previousJoin = fishPlayer.lastJoined; + fishPlayer.updateSavedInfoFromPlayer(player); + if (fishPlayer.validate()) { + if (!fishPlayer.hasPerm("bypassNameCheck")) { + var message = (0, utils_1.isImpersonator)(fishPlayer.name, fishPlayer.ranksAtLeast("admin")); + if (message !== false) { + fishPlayer.sendMessage("[scarlet]\u26A0[] [gold]Oh no! Our systems think you are a [scarlet]SUSSY IMPERSONATOR[]!\n[gold]Reason: ".concat(message, "\n[gold]Change your name to remove the tag.")); + } + else if ((0, utils_1.cleanText)(player.name, true).includes("hacker")) { + fishPlayer.sendMessage("[scarlet]\u26A0 Don't be a script kiddie!"); + globals_1.FishEvents.fire("scriptKiddie", [fishPlayer]); + } + } + fishPlayer.updateAdminStatus(); + fishPlayer.checkVPNAndJoins(); + fishPlayer.updateName(); + //I think this is a better spot for this + if (fishPlayer.firstJoin()) + void menus_1.Menu.menu("Rules for [#0000ff] >|||> FISH [white] servers [white]", config_1.rules.join("\n\n[white]") + "\nYou can view these rules again by running [cyan]/rules[].", ["[green]I understand and agree to these terms"], fishPlayer); + } + }; + /** Must be run on PlayerJoinEvent. */ + FishPlayer.onPlayerJoin = function (player) { + var _this = this; + var _a; + var _b, _c; + var fishPlayer = (_a = (_b = this.cachedPlayers)[_c = player.uuid()]) !== null && _a !== void 0 ? _a : (_b[_c] = (function () { + Log.err("onPlayerJoin: no fish player was created? ".concat(player.uuid())); + return _this.createFromPlayer(player); + })()); + //Don't activate heuristics until they've joined + //a lot of time can pass between connect and join + //also the player might connect but fail to join for a lot of reasons, + //or connect, fail to join, then connect again and join successfully + //which would cause heuristics to activate twice + fishPlayer.activateHeuristics(); + }; + FishPlayer.updateAFKCheck = function () { + //TODO better AFK check + this.forEachPlayer(function (fishP, mp) { + if (fishP.lastMousePosition[0] != mp.mouseX || fishP.lastMousePosition[1] != mp.mouseY) { + fishP.lastActive = Date.now(); + } + fishP.lastMousePosition = [mp.mouseX, mp.mouseY]; + if (fishP.lastUnitPosition[0] != mp.x || fishP.lastUnitPosition[1] != mp.y) { + fishP.lastActive = Date.now(); + } + fishP.lastUnitPosition = [mp.x, mp.y]; + fishP.updateName(); + }); + }; + /** Must be run on PlayerLeaveEvent. */ + FishPlayer.onPlayerLeave = function (player) { + var _a; + var fishP = this.cachedPlayers[player.uuid()]; + if (!fishP) + return; + if (Vars.netServer.currentlyKicking && + Reflect.get(Vars.netServer.currentlyKicking, "target") == player) { + //Anti votekick evasion + var votes_1 = Reflect.get(Vars.netServer.currentlyKicking, "votes"); + if ((function () { + if (fishP.hasPerm("bypassVotekick")) + return false; + if (fishP.hasPerm("bypassVoteFreeze")) + return votes_1 >= Vars.netServer.votesRequired(); + if (fishP.info().timesJoined > 50) + return votes_1 >= 2; + return votes_1 >= 1; + })()) { + var kickDuration = NetServer.kickDuration; + //Pass the votekick + Call.sendMessage("[orange]Vote passed.[scarlet] ".concat(player.name, "[orange] will be banned from the server for ").concat(kickDuration / 60, " minutes.")); + player.kick(Packets.KickReason.vote, kickDuration * 1000); //it is stored in seconds but needs to be converted to millis + Reflect.get(Vars.netServer.currentlyKicking, "task").cancel(); + Vars.netServer.currentlyKicking = null; + } + } + //Clear temporary states such as menu and taphandler + fishP.activeMenus = []; + fishP.tapInfo.commandName = null; + fishP.updateStats(function (stats) { return stats.timeInGame += (Date.now() - fishP.lastJoined); }); //Time between joining and leaving + fishP.lastJoined = Date.now(); + this.recentLeaves.unshift(fishP); + if (this.recentLeaves.length > 10) + this.recentLeaves.pop(); + void api.setFishPlayerData(fishP.getData(), 1, true); + var currentRun = (_a = maps_1.PartialMapRun.current) === null || _a === void 0 ? void 0 : _a.startTime; + if (currentRun) + Core.app.post(function () { + //Wait for the /spectate command's handler to fix their team before saving it + fishP.restoreTeam = [fishP.player.team(), Date.now(), currentRun]; + }); + }; + FishPlayer.validateVotekickSession = function () { + var _a; + if (!Vars.netServer.currentlyKicking) + return; + var target = this.get(Reflect.get(Vars.netServer.currentlyKicking, "target")); + var voted = Reflect.get(Vars.netServer.currentlyKicking, "voted"); + if (voted.size == 2) { + //Try to find the UUID of the initiator + var uuid_1 = null; + voted.entries().toArray().each(function (e) { + if (globals_1.uuidPattern.test(e.key)) + uuid_1 = e.key; + }); + if (uuid_1) { + var initiator = this.getById(uuid_1); + if (initiator === null || initiator === void 0 ? void 0 : initiator.stelled()) { + if (initiator.hasPerm("bypassVotekick")) { + if (target !== this.easterEggVotekickTarget) { + this.easterEggVotekickTarget = target; + var msg = (_a = (new Error()).stack) === null || _a === void 0 ? void 0 : _a.split("\n").slice(0, 4).join("\n"); + Call.sendMessage("[scarlet]Server[lightgray] has voted on kicking[orange] ".concat(initiator.prefixedName, "[lightgray].[accent] (\u221E/").concat(Vars.netServer.votesRequired(), ")\n\t[scarlet]Error: failed to kick player ").concat(initiator.name, "\n\t").concat(msg, "\n\t[scarlet]Error: failed to cancel votekick\n\t").concat(msg)); + } + return; + } + Call.sendMessage("[scarlet]Server[lightgray] has voted on kicking[orange] ".concat(initiator.prefixedName, "[lightgray].[accent] (\u221E/").concat(Vars.netServer.votesRequired(), ")\n[scarlet]Vote passed.")); + initiator.kick("You are not allowed to votekick other players while marked.", 2); + Reflect.get(Vars.netServer.currentlyKicking, "task").cancel(); + Vars.netServer.currentlyKicking = null; + return; + } + else if ((initiator === null || initiator === void 0 ? void 0 : initiator.hasPerm("immediatelyVotekickNewPlayers")) && target.isSuspicious("high") && !target.hasPerm("bypassVotekick")) { + Call.sendMessage("[scarlet]Server[lightgray] has voted on kicking[orange] ".concat(target.prefixedName, "[lightgray].[accent] (").concat(Vars.netServer.votesRequired(), "/").concat(Vars.netServer.votesRequired(), ")\n[scarlet]Vote passed.")); + target.kick(Packets.KickReason.vote, funcs_1.Duration.minutes(30)); + Reflect.get(Vars.netServer.currentlyKicking, "task").cancel(); + Vars.netServer.currentlyKicking = null; + return; + } + else if (target.isSuspicious("high") && !target.hasPerm("bypassVotekick") && !target.ranksAtLeast("trusted")) { + //Increase votes by 1, from 1 to 2 + Reflect.set(Vars.netServer.currentlyKicking, "votes", Packages.java.lang.Integer(2)); + voted.put("__server__", 1); + Call.sendMessage("[scarlet]Server[lightgray] has voted on kicking[orange] ".concat(target.prefixedName, "[lightgray].[accent] (2/").concat(Vars.netServer.votesRequired(), ")\n[lightgray]Type[orange] /vote [] to agree.")); + return; + } + } + } + if (target.hasPerm("bypassVotekick")) { + Call.sendMessage("[scarlet]Server[lightgray] has voted on kicking[orange] ".concat(target.prefixedName, "[lightgray].[accent] (-\u221E/").concat(Vars.netServer.votesRequired(), ")\n[scarlet]Vote cancelled.")); + Reflect.get(Vars.netServer.currentlyKicking, "task").cancel(); + Vars.netServer.currentlyKicking = null; + } + else if (target.ranksAtLeast("trusted") && Groups.player.size() > 4 && voted.get("__server__") == 0) { + //decrease votes by two, goes from 1 to negative 1 + Reflect.set(Vars.netServer.currentlyKicking, "votes", Packages.java.lang.Integer(-1)); + voted.put("__server__", -2); + Call.sendMessage("[scarlet]Server[lightgray] has voted on kicking[orange] ".concat(target.prefixedName, "[lightgray].[accent] (-1/").concat(Vars.netServer.votesRequired(), ")\n[lightgray]Type[orange] /vote [] to agree.")); + } + }; + FishPlayer.onPlayerChat = function (player, message) { + var fishP = this.get(player); + if (message.trim().toLowerCase().startsWith("/vote y") || message.startsWith("/votekick ")) { + this.checkVotekickAction(fishP, message); + } + fishP.lastActive = Date.now(); + fishP.updateStats(function (stats) { return stats.chatMessagesSent++; }); + }; + FishPlayer.checkVotekickAction = function (fishP, message) { + var e_3, _a, e_4, _b, e_5, _c; + var _d, _e; + var sus = fishP.suspicionLevel(); + var timeSinceJoin = Date.now() - fishP.lastJoined; + var target; + if (message.startsWith("/votekick")) { + var id = (_d = message.split(" ")[1]) === null || _d === void 0 ? void 0 : _d.split("#")[1]; + target = Groups.player.getByID(Number(id)); + if (!target) + return; //invalid votekick command, harmless + } + else { //TODO these "harmless" actions could be indications of a malfunctioning vkbot and should be logged if they repeat a lot (eg more than 5 times per minute) + if (!Vars.netServer.currentlyKicking) + return; //nobody to votekick, harmless + target = Reflect.get(Vars.netServer.currentlyKicking, "target"); + } + var targetSusLevel = FishPlayer.get(target).suspicionLevel(); + //Evaluate if this action should be blocked + if (sus <= 1) + return; + var reason = undefined; + if (!this.votekickActionRate.allow(108000, 8)) + reason = "Exceeded 8 votekick actions in the last 2 minutes"; + else if (sus == 3 && this.lastVKActions.find(function (a) { return Date.now() - a.time < 10000 && a.playerSusLevel == 3; }) && timeSinceJoin < 6000) + reason = "Performed votekick within 6 seconds of joining and there was a recent suspicious vote"; + else if (sus == 3 && timeSinceJoin < 80000 && this.lastVKActions.find(function (a) { return a.player == fishP; }) && targetSusLevel <= 1) + reason = "Two votekick actions within 80 seconds of joining and the target is not suspicious"; + else if (sus >= 2 && this.lastVKActions.filter(function (a) { return a.playerSusLevel == 3 && Date.now() - a.time < 33000; }).length >= 3) + reason = "More than 3 recent votekick actions by suspicious players"; + else if (sus >= 2 && this.lastVKActions.filter(function (a) { return a.playerSusLevel >= 2; }).length >= 6 && this.lastVKActions.filter(function (a) { return a.player == fishP; }).length >= 3) + reason = "More than 6 slightly suspicious votekick actions within the past 20 minutes and this player has already performed 3 of them"; + if (reason != undefined) { + //Should we ban everyone? + var suspiciousActions = this.lastVKActions.filter(function (action) { + return (action.playerSusLevel == 3 || (action.targetSusLevel <= 2 && action.playerSusLevel >= 2) || action.player == fishP) && Date.now() - action.time < 78000; + }); + if (suspiciousActions.length >= 3) { + //Ban everyone + var playersToBan = suspiciousActions.map(function (a) { return a.player; }).reduce(function (map, p) { + var _a; + map.set(p, ((_a = map.get(p)) !== null && _a !== void 0 ? _a : 0) + 1); + return map; + }, new Map()); + //Only ban players that appeared in the list twice or are high suslevel + var admins = Vars.netServer.admins; + try { + for (var playersToBan_1 = __values(playersToBan), playersToBan_1_1 = playersToBan_1.next(); !playersToBan_1_1.done; playersToBan_1_1 = playersToBan_1.next()) { + var _f = __read(playersToBan_1_1.value, 2), p = _f[0], times = _f[1]; + if (p.suspicionLevel() == 3 || p.suspicionLevel() == 2 && times > 1) { + admins.banPlayerID(p.uuid); + admins.banPlayerIP(p.ip()); + api.ban({ ip: p.ip(), uuid: p.uuid }); + (0, utils_1.logHTrip)(p, "votekick abuse", (p == fishP ? "Player banned automatically" : "Player banned automatically based on previous activity") + + ". Trigger reason: ".concat(reason)); + } + } + } + catch (e_3_1) { e_3 = { error: e_3_1 }; } + finally { + try { + if (playersToBan_1_1 && !playersToBan_1_1.done && (_a = playersToBan_1.return)) _a.call(playersToBan_1); + } + finally { if (e_3) throw e_3.error; } + } + (0, utils_1.updateBans)(function (player) { return "[scarlet]Player [yellow]".concat(player.name, "[scarlet] has been whacked automatically for suspected votekick abuse."); }); + //Pardon most of the votekick targets (the ones that weren't voted on by a non-sus player) + var candidatePardons = new Set(FishPlayer.lastVKActions.map(function (a) { return a.target; })); + try { + for (var _g = __values(FishPlayer.lastVKActions), _h = _g.next(); !_h.done; _h = _g.next()) { + var action = _h.value; + if (action.playerSusLevel <= 1) + candidatePardons.delete(action.target); + } + } + catch (e_4_1) { e_4 = { error: e_4_1 }; } + finally { + try { + if (_h && !_h.done && (_b = _g.return)) _b.call(_g); + } + finally { if (e_4) throw e_4.error; } + } + var playersToPardon = __spreadArray([], __read(candidatePardons), false).map(FishPlayer.get); + try { + //Don't pardon players with suslevel 3 + for (var playersToPardon_1 = __values(playersToPardon), playersToPardon_1_1 = playersToPardon_1.next(); !playersToPardon_1_1.done; playersToPardon_1_1 = playersToPardon_1.next()) { + var p = playersToPardon_1_1.value; + if (!p.isSuspicious("high")) { + p.info().lastKicked = 0; + admins.kickedIPs.remove(p.ip()); + Log.info("Pardoned player @ (@/@)", p.name, p.uuid, p.ip()); + (0, utils_1.logAction)("pardoned", "automod", p, "kicked by suspected votekick bot"); + } + } + } + catch (e_5_1) { e_5 = { error: e_5_1 }; } + finally { + try { + if (playersToPardon_1_1 && !playersToPardon_1_1.done && (_c = playersToPardon_1.return)) _c.call(playersToPardon_1); + } + finally { if (e_5) throw e_5.error; } + } + } + else { + //Just kick the player + (0, utils_1.logHTrip)(fishP, "votekick abuse", "sus=".concat(sus)); + fishP.kick("You have been kicked [accent]automatically[] due to suspicious behavior. Please wait [accent]35[] seconds before rejoining.", 30000); + Call.sendMessage("[scarlet]Player [yellow]".concat(fishP.prefixedName, "[scarlet] was kicked due to suspected votekick abuse.")); + //If this message is going to start a votekick, cancel it + if (message.startsWith("/votekick") && Vars.netServer.currentlyKicking == null) + Core.app.post(function () { + Call.sendMessage("[scarlet]Server[lightgray] has voted on kicking[orange] ".concat(target.name, "[lightgray].[accent] (-\u221E/").concat(Vars.netServer.votesRequired(), ")\n\t\t[scarlet]Vote cancelled due to suspected abuse. [accent]If this is in error, please report it to staff.")); + if (Vars.netServer.currentlyKicking) + Reflect.get(Vars.netServer.currentlyKicking, "task").cancel(); + Vars.netServer.currentlyKicking = null; + }); + //If there is an ongoing votekick and the initiator is suspicious, cancel that + else if (((_e = FishPlayer.lastVKActions.slice().reverse().find(function (a) { return a.type == "start"; })) === null || _e === void 0 ? void 0 : _e.playerSusLevel) == 3) { + Call.sendMessage("[scarlet]Server[lightgray] has voted on kicking[orange] ".concat(target.name, "[lightgray].[accent] (-\u221E/").concat(Vars.netServer.votesRequired(), ")\n\t\t[scarlet]Vote cancelled due to suspected abuse. [accent]If this is in error, please report it to staff.")); + if (Vars.netServer.currentlyKicking) + Reflect.get(Vars.netServer.currentlyKicking, "task").cancel(); + Vars.netServer.currentlyKicking = null; + } + //Otherwise, revoke the vote + else + Core.app.post(function () { + if (Vars.netServer.currentlyKicking) { + var votes = Reflect.get(Vars.netServer.currentlyKicking, "votes") - 1; + Reflect.set(Vars.netServer.currentlyKicking, "votes", votes); + var voted = Reflect.get(Vars.netServer.currentlyKicking, "voted"); + voted.put(fishP.uuid, 0); + voted.put(fishP.ip(), 0); + Call.sendMessage("[scarlet]Vote cancelled due to suspected abuse. [accent]If this is in error, please report it to staff."); + } + }); + } + } + //Update state to catch future actions + this.lastVKActions.push({ + player: fishP, + playerSusLevel: sus, + target: target, + targetSusLevel: targetSusLevel, + time: Date.now(), + type: message.startsWith("/votekick") ? "start" : "vote y", + reason: message.startsWith("/votekick") ? message.split(" ").slice(2).join(" ") : undefined + }); + this.lastVKActions = this.lastVKActions.filter(function (a) { return Date.now() - a.time < funcs_1.Duration.minutes(10); }); + }; + FishPlayer.onPlayerCommand = function (player, command, unjoinedRawArgs) { + if (command == "msg" && unjoinedRawArgs[1] == "Please do not use that logic, as it is attem83 logic and is bad to use. For more information please read www.mindustry.dev/attem") + return; //Attemwarfare message, not sent by the player + player.lastActive = Date.now(); + }; + FishPlayer.onGameOver = function (winningTeam) { + var _this = this; + globals_1.FishEvents.fire("gameOver", [winningTeam]); + this.forEachPlayer(function (fishPlayer) { + //Clear temporary states such as menu and taphandler + fishPlayer.activeMenus = []; + fishPlayer.tapInfo.commandName = null; + //Update stats + if (!_this.ignoreGameOver && fishPlayer.team() != Team.derelict && winningTeam != Team.derelict) { + fishPlayer.updateStats(function (stats) { return stats.gamesFinished++; }); + if (fishPlayer.changedTeam) { + fishPlayer.sendMessage("Refusing to update stats due to a team change."); + } + else { + if (fishPlayer.team() == winningTeam) + fishPlayer.updateStats(function (stats) { return stats.gamesWon++; }); + } + } + fishPlayer.changedTeam = false; + fishPlayer.tstats.wavesSurvived = 0; + fishPlayer.tstats.blockInteractionsThisMap = 0; + }); + }; + FishPlayer.ignoreGameover = function (callback) { + this.ignoreGameOver = true; + callback(); + this.ignoreGameOver = false; + }; + FishPlayer.onGameBegin = function () { + var startTime = Date.now(); + FishPlayer.lastMapStartTime = startTime; + //wait 7 seconds for players to join + Timer.schedule(function () { return FishPlayer.forEachPlayer(function (p) { return p.tstats.lastMapStartTime = startTime; }); }, 7); + }; + /** Must be run on UnitChangeEvent. */ + FishPlayer.onUnitChange = function (player, unit) { + if (unit === null || unit === void 0 ? void 0 : unit.spawnedByCore) + this.onRespawn(player); + }; + FishPlayer.onRespawn = function (player) { + var fishP = this.get(player); + if (fishP.stelled()) + fishP.stopUnit(); + }; + FishPlayer.forEachPlayer = function (func) { + var _this = this; + Groups.player.each(function (player) { + if (player == null) { + Log.err(".FINDTAG. Groups.player.each() returned a null player???"); + return; + } + var fishP = _this.get(player); + func(fishP, player); + }); + }; + FishPlayer.mapPlayers = function (func) { + var _this = this; + var out = []; + Groups.player.each(function (player) { + if (player == null) { + Log.err(".FINDTAG. Groups.player.each() returned a null player???"); + return; + } + out.push(func(_this.get(player))); + }); + return out; + }; + FishPlayer.prototype.updateMemberExclusiveState = function () { + if (!this.hasPerm("member")) { + this.highlight = null; + this.rainbow = null; + } + }; + /** Updates the mindustry player's name, using the prefixes of the current rank and role flags. */ + FishPlayer.prototype.updateName = function () { + var e_6, _a; + var _b; + if (!this.connected() || !this.shouldUpdateName) + return; //No player, no need to update + var name = (_b = this.originalName) !== null && _b !== void 0 ? _b : this.name; + if (this.marked()) + this.showRankPrefix = true; + var prefix = ''; + if (!this.hasPerm("bypassNameCheck") && (0, utils_1.isImpersonator)(name, this.ranksAtLeast("admin"))) + prefix += "[scarlet]SUSSY IMPOSTOR[]"; + if (this.marked()) + prefix += config_1.prefixes.marked; + else if (this.autoflagged) + prefix += config_1.prefixes.flagged; + if (this.muted) + prefix += config_1.prefixes.muted; + if (this.afk()) + prefix += "[orange]\uE876 AFK \uE876 | [white]"; + if (this.showRankPrefix) { + try { + for (var _c = __values(this.flags), _d = _c.next(); !_d.done; _d = _c.next()) { + var flag = _d.value; + prefix += flag.prefix; + } + } + catch (e_6_1) { e_6 = { error: e_6_1 }; } + finally { + try { + if (_d && !_d.done && (_a = _c.return)) _a.call(_c); + } + finally { if (e_6) throw e_6.error; } + } + prefix += this.rank.prefix; + } + if (prefix.length > 0 && !prefix.endsWith(" ")) + prefix += " "; + var replacedName; + if ((0, utils_1.cleanText)(name, true).includes("hacker")) { + //"Don't be a script kiddie" + //-LiveOverflow, 2015 + if (/h.*a.*c.*k.*[3e].*r/i.test(name)) { //try to only replace the part that contains "hacker" if it can be found with a simple regex + replacedName = name.replace(/h.*a.*c.*k.*[3e].*r/gi, "[brown]script kiddie[]"); + } + else { + replacedName = "[brown]script kiddie"; + } + } + else if (this.name.endsWith("[") && !this.name.endsWith("[[")) { + replacedName = name + "["; + } + else + replacedName = name; + this.player.name = this.prefixedName = prefix + replacedName; + }; + FishPlayer.prototype.updateAdminStatus = function () { + if (!this.connected()) + return; + if (this.hasPerm("admin")) { + Vars.netServer.admins.adminPlayer(this.uuid, this.player.usid()); + this.player.admin = true; + } + else { + Vars.netServer.admins.unAdminPlayer(this.uuid); + this.player.admin = false; + } + }; + FishPlayer.prototype.updateAutoflaggedStatus = function () { + if (this.ranksAtLeast("active")) { + this.autoflagged = false; + } + }; + FishPlayer.prototype.checkAntiEvasion = function () { + var e_7, _a; + var _b, _c; + FishPlayer.updatePunishedIPs(); + try { + for (var _d = __values(FishPlayer.punishedIPs), _e = _d.next(); !_e.done; _e = _d.next()) { + var _f = __read(_e.value, 2), ip = _f[0], uuid = _f[1]; + if (ip == this.ip() && uuid != this.uuid && !this.ranksAtLeast("mod")) { + api.sendModerationMessage("Automatically banned player `".concat(this.cleanedName, "` (`").concat(this.uuid, "`/`").concat(this.ip(), "`) for suspected punishment evasion.\nPreviously used UUID `").concat(uuid, "`(").concat((_b = Vars.netServer.admins.getInfoOptional(uuid)) === null || _b === void 0 ? void 0 : _b.plainLastName(), "), currently using UUID `").concat(this.uuid, "` from the same IP address.")); + Log.warn("&yAutomatically banned player &b".concat(this.cleanedName, "&y (&b").concat(this.uuid, "&y/&b").concat(this.ip(), "&y) for suspected punishment evasion.\n&yPreviously used UUID &b").concat(uuid, "&y(&b").concat((_c = Vars.netServer.admins.getInfoOptional(uuid)) === null || _c === void 0 ? void 0 : _c.plainLastName(), "&y), currently using UUID &b").concat(this.uuid, "&y from the same IP address.")); + FishPlayer.messageStaff("[yellow]Automatically banned player [cyan]".concat(this.cleanedName, "[] for suspected punishment evasion.")); + Vars.netServer.admins.banPlayerIP(ip); + api.ban({ ip: ip, uuid: uuid }); + this.kick(Packets.KickReason.banned); + return false; + } + } + } + catch (e_7_1) { e_7 = { error: e_7_1 }; } + finally { + try { + if (_e && !_e.done && (_a = _d.return)) _a.call(_d); + } + finally { if (e_7) throw e_7.error; } + } + return true; + }; + FishPlayer.updatePunishedIPs = function () { + for (var i = 0; i < this.punishedIPs.length; i++) { + if (this.punishedIPs[i][2] < Date.now()) { + this.punishedIPs.splice(i, 1); + } + } + }; + FishPlayer.prototype.checkVPNAndJoins = function () { + var _this = this; + var ip = this.ip(); + var info = this.info(); + api.isVpn(ip, function (isVpn) { + if (isVpn) { + Log.warn("IP ".concat(ip, " was flagged as VPN. Flag rate: ").concat(FishPlayer.stats.numIpsFlagged, "/").concat(FishPlayer.stats.numIpsChecked, " (").concat(100 * FishPlayer.stats.numIpsFlagged / FishPlayer.stats.numIpsChecked, "%)")); + _this.ipDetectedVpn = true; + if (!FishPlayer.autoflagRate.allow(30000, 5)) { + FishPlayer.triggerAntibot(funcs_1.Duration.minutes(3), "rate of flagged IPs exceeded 5 / 30s", "automatic"); + return; + } + if ((info.timesJoined <= 1 || (FishPlayer.autoflagRate.occurences > 3 && info.timesJoined <= 10)) //is this smart? + && !_this.ranksAtLeast("active") + && FishPlayer.punishedIPs.length > 0) { + _this.autoflagged = true; + _this.stopUnit(); + _this.updateName(); + if (FishPlayer.shouldWhackFlaggedPlayers()) { + FishPlayer.whackFlaggedPlayers(); //calls whack all flagged players + } + else { + (0, utils_1.logAction)("autoflagged", "AntiVPN", _this); + api.sendStaffMessage("Autoflagged player ".concat(_this.name, "[cyan] for suspected vpn!"), "AntiVPN", true); + FishPlayer.messageStaff("[yellow]WARNING:[scarlet] player [cyan]\"".concat(_this.name, "[cyan]\"[yellow] is new (").concat(info.timesJoined - 1, " joins) and using a vpn. They have been automatically stopped and muted. Unless there is an ongoing griefer raid, they are most likely innocent. Free them with /free.")); + Log.warn("Player ".concat(_this.name, " (").concat(_this.uuid, ") was autoflagged.")); + void menus_1.Menu.buttons(_this, "[gold]Welcome to Fish Community!", "[gold]Hi there! You have been automatically [scarlet]stopped and muted[] because we've found something to be [pink]a bit sus[]. You can still talk to staff and request to be freed. ".concat(config_1.FColor.discord(templateObject_1 || (templateObject_1 = __makeTemplateObject(["Join our Discord"], ["Join our Discord"]))), " to request a staff member come online if none are on."), [[ + { data: "Close", text: "Close" }, + { data: "Discord", text: config_1.FColor.discord("Discord") }, + ]]).then(function (option) { + if (option == "Discord") { + Call.openURI(_this.con, config_1.text.discordURL); + } + }); + _this.sendMessage("[gold]Welcome to Fish Community!\n[gold]Hi there! You have been automatically [scarlet]stopped and muted[] because we've found something to be [pink]a bit sus[]. You can still talk to staff and request to be freed. ".concat(config_1.FColor.discord(templateObject_2 || (templateObject_2 = __makeTemplateObject(["Join our Discord"], ["Join our Discord"]))), " to request a staff member come online if none are on.")); + } + } + else if (info.timesJoined < 5) { + FishPlayer.messageStaff("[yellow]WARNING:[scarlet] player [cyan]\"".concat(_this.name, "[cyan]\"[yellow] is new (").concat(info.timesJoined - 1, " joins) and using a vpn.")); + } + } + else { + if (info.timesJoined == 1) { + FishPlayer.messageTrusted("[yellow]Player \"".concat(_this.cleanedName, "\" is on first join.")); + } + } + if (info.timesJoined == 1) { + var message = "&lrNew player joined: &c".concat(_this.cleanedName, "&lr (&c").concat(_this.uuid, "&lr/&c").concat(ip, "&lr)"); + //Add BEL, this causes an audible noise + if (globals.fishState.joinBell) + message += '\x07'; + Log.info(message); + } + }, function (err) { + Log.err("Error while checking for VPN status of ip ".concat(ip, "!")); + Log.err(err); + }); + }; + FishPlayer.prototype.validate = function () { + return this.checkName() && this.checkUsid() && this.checkAntiEvasion(); + }; + /** Checks if this player's name is allowed. */ + FishPlayer.prototype.checkName = function () { + if ((0, utils_1.matchFilter)(this.name, "name")) { + this.kick("[scarlet]\"".concat(this.name, "[scarlet]\" is not an allowed name because it contains a banned word.\n\nIf you are unable to change it, please download Mindustry from Steam or itch.io."), 1); + } + else if (Strings.stripColors(this.name.replace(/[\u3164]/g, "")).trim().length == 0) { + this.kick("[scarlet]\"".concat((0, funcs_1.escapeStringColorsClient)(this.name), "[scarlet]\" is not an allowed name because it is empty. Please change it."), 1); + } + else { + return true; + } + return false; + }; + /** Checks if this player's USID is correct. */ + FishPlayer.prototype.checkUsid = function () { + var storedUSID = this.usid; + var usidMissing = storedUSID == null || !storedUSID; + var receivedUSID = this.player.usid(); + if (this.hasPerm("usidCheck")) { + if (usidMissing) { + if (this.hasPerm("mod")) { + //Staff missing USID, don't let them in + Log.err("&rUSID missing for privileged player &c\"".concat(this.cleanedName, "\"&r: no stored usid, cannot authenticate.\nRun &lgsetusid ").concat(this.uuid, " ").concat(receivedUSID, "&fr if you have verified this connection attempt.")); + this.kick("Authorization failure! Please ask a staff member with Console Access to approve this connection.", 1); + FishPlayer.lastAuthKicked = this; + return false; + } + else { + Log.info("Acquired USID for player &c\"".concat(this.cleanedName, "\"&fr: &c\"").concat(receivedUSID, "\"&fr")); + } + } + else { + if (receivedUSID != storedUSID) { + Log.err("&rUSID mismatch for player &c\"".concat(this.cleanedName, "\"&r: stored usid is &c").concat(storedUSID, "&r, but they tried to connect with usid &c").concat(receivedUSID, "&r\nRun &lgsetusid ").concat(this.uuid, " ").concat(receivedUSID, "&fr if you have verified this connection attempt.")); + this.kick("Authorization failure!", 1); + FishPlayer.lastAuthKicked = this; + return false; + } + } + } + else { + if (!usidMissing && receivedUSID != storedUSID) { + Log.err("&rUSID mismatch for player &c\"".concat(this.cleanedName, "\"&r: stored usid is &c").concat(storedUSID, "&r, but they tried to connect with usid &c").concat(receivedUSID, "&r")); + } + } + this.usid = receivedUSID; + return true; + }; + FishPlayer.prototype.displayTrail = function () { + if (this.trail) + Call.effect(Fx[this.trail.type], this.player.x, this.player.y, 0, this.trail.color); + }; + FishPlayer.prototype.sendWelcomeMessage = function () { + var _this = this; + var appealLine = "To appeal, ".concat(config_1.FColor.discord(templateObject_3 || (templateObject_3 = __makeTemplateObject(["join our discord"], ["join our discord"]))), " with ").concat(config_1.FColor.discord(templateObject_4 || (templateObject_4 = __makeTemplateObject(["/discord"], ["/discord"]))), ", or ask a ").concat(ranks_1.Rank.mod.color, "staff member[] in-game."); + if (FishPlayer.dataFetchFailedUuids.has(this.uuid)) { + this.sendMessage(config_1.text.dataFetchFailed); + FishPlayer.dataFetchFailedUuids.delete(this.uuid); + } + if (this.marked()) + this.sendMessage("[gold]Hello there! You are currently [scarlet]marked as a griefer[]. You cannot do anything in-game while marked.\n".concat(appealLine, "\nYour mark will expire automatically ").concat(this.unmarkTime == globals.maxTime ? "in [red]never[]" : "[green]".concat((0, utils_1.formatTimeRelative)(this.unmarkTime), "[]"), ".\nWe apologize for the inconvenience.")); + else if (this.muted) + this.sendMessage("[gold]Hello there! You are currently [red]muted[]. You can still play normally, but cannot send chat messages to other non-staff players while muted.\n".concat(appealLine, "\nWe apologize for the inconvenience.")); + else if (this.autoflagged) + this.sendMessage("[gold]Hello there! You are currently [red]flagged as suspicious[]. You cannot do anything in-game.\n".concat(appealLine, "\nWe apologize for the inconvenience.")); + else if (!this.showRankPrefix) + this.sendMessage("[gold]Hello there! Your rank prefix is currently hidden. You can show it again by running [white]/vanish[]."); + else { + this.sendMessage(config_1.text.welcomeMessage()); + //show tips + var showAd = false; + if (Date.now() - this.lastShownAd > funcs_1.Duration.days(1)) { + this.lastShownAd = Date.now(); + this.showAdNext = true; + } + else if (this.lastShownAd == globals.maxTime) { + //this is the first time they joined, show ad the next time they join + this.showAdNext = true; + this.lastShownAd = Date.now(); + } + else if (this.showAdNext) { + this.showAdNext = false; + showAd = true; + } + var messagePool = showAd ? config_1.tips.ads : (config_1.Mode.isChristmas && Math.random() > 0.6) ? config_1.tips.christmas : config_1.tips.normal; + var messageText = messagePool[Math.floor(Math.random() * messagePool.length)]; + var message_1 = showAd ? "[gold]".concat(messageText, "[]") : "[gold]Tip: ".concat(messageText, "[]"); + //Delay sending the message so it doesn't get lost in the spam of messages that usually occurs when you join + Timer.schedule(function () { return _this.sendMessage(message_1); }, 3); + } + }; + FishPlayer.prototype.checkAutoRanks = function () { + var e_8, _a; + var _this = this; + if (this.stelled()) + return; + var _loop_1 = function (rankToAssign) { + if (!this_1.ranksAtLeast(rankToAssign) && rankToAssign.autoRankData) { + if (this_1.joinsAtLeast(rankToAssign.autoRankData.joins) && + this_1.globalStats.blocksPlaced >= rankToAssign.autoRankData.blocksPlaced && + this_1.globalStats.timeInGame >= rankToAssign.autoRankData.playtime && + this_1.globalStats.chatMessagesSent >= rankToAssign.autoRankData.chatMessagesSent && + (Date.now() - this_1.globalFirstJoined) >= rankToAssign.autoRankData.timeSinceFirstJoin) { + void this_1.setRank(rankToAssign).then(function () { + return _this.sendMessage("You have been automatically promoted to rank ".concat(rankToAssign.coloredName(), "!")); + }); + } + } + }; + var this_1 = this; + try { + for (var _b = __values(ranks_1.Rank.autoRanks), _c = _b.next(); !_c.done; _c = _b.next()) { + var rankToAssign = _c.value; + _loop_1(rankToAssign); + } + } + catch (e_8_1) { e_8 = { error: e_8_1 }; } + finally { + try { + if (_c && !_c.done && (_a = _b.return)) _a.call(_b); + } + finally { if (e_8) throw e_8.error; } + } + }; + //#endregion + //#region I/O + FishPlayer.read = function (version, fishPlayerData, player) { + var _a, _b, _c, _d, _e, _f, _g, _h, _j; + switch (version) { + case 0: + case 1: + case 2: + case 3: + case 4: + case 5: + case 6: + case 7: + case 8: + case 9: + (0, funcs_1.crash)("Version ".concat(version, " is not longer supported, this should not be possible")); + break; + case 10: { + var uuid = (_a = fishPlayerData.readString(2)) !== null && _a !== void 0 ? _a : (0, funcs_1.crash)("Failed to deserialize FishPlayer: UUID was null."); + var fishP = new this(uuid, { + name: (_b = fishPlayerData.readString(2)) !== null && _b !== void 0 ? _b : "Unnamed player [ERROR]", + muted: (function () { + var muted = fishPlayerData.readBool(); + void fishPlayerData.readBool(); //discard the stored data for autoflagged + return muted; + })(), + unmarkTime: fishPlayerData.readNumber(13), + highlight: fishPlayerData.readString(2), + history: fishPlayerData.readArray(function (str) { + var _a, _b; + return ({ + action: (_a = str.readString(2)) !== null && _a !== void 0 ? _a : "null", + by: (_b = str.readString(2)) !== null && _b !== void 0 ? _b : "null", + time: str.readNumber(15) + }); + }), + rainbow: (function (n) { return n == 0 ? null : { speed: n }; })(fishPlayerData.readNumber(2)), + rank: (_c = fishPlayerData.readString(2)) !== null && _c !== void 0 ? _c : "", + flags: fishPlayerData.readArray(function (str) { return str.readString(2); }, 2).filter(function (s) { return s != null; }), + usid: fishPlayerData.readString(2), + chatStrictness: fishPlayerData.readEnumString(["chat", "strict"]), + lastJoined: fishPlayerData.readNumber(15), + firstJoined: fishPlayerData.readNumber(15), + stats: { + blocksBroken: fishPlayerData.readNumber(10), + blocksPlaced: fishPlayerData.readNumber(10), + timeInGame: fishPlayerData.readNumber(15), + chatMessagesSent: fishPlayerData.readNumber(7), + gamesFinished: fishPlayerData.readNumber(5), + gamesWon: fishPlayerData.readNumber(5), + }, + showRankPrefix: fishPlayerData.readBool(), + }, player); + fishPlayerData.readNumber(1); //discard pollResponse + return fishP; + } + case 11: { + var uuid = (_d = fishPlayerData.readString(2)) !== null && _d !== void 0 ? _d : (0, funcs_1.crash)("Failed to deserialize FishPlayer: UUID was null."); + return new this(uuid, { + name: (_e = fishPlayerData.readString(2)) !== null && _e !== void 0 ? _e : "Unnamed player [ERROR]", + muted: (function () { + var muted = fishPlayerData.readBool(); + void fishPlayerData.readBool(); //discard the stored data for autoflagged + return muted; + })(), + unmarkTime: fishPlayerData.readNumber(13), + highlight: fishPlayerData.readString(2), + history: fishPlayerData.readArray(function (str) { + var _a, _b; + return ({ + action: (_a = str.readString(2)) !== null && _a !== void 0 ? _a : "null", + by: (_b = str.readString(2)) !== null && _b !== void 0 ? _b : "null", + time: str.readNumber(15) + }); + }), + rainbow: (function (n) { return n == 0 ? null : { speed: n }; })(fishPlayerData.readNumber(2)), + rank: (_f = fishPlayerData.readString(2)) !== null && _f !== void 0 ? _f : "", + flags: fishPlayerData.readArray(function (str) { return str.readString(2); }, 2).filter(function (s) { return s != null; }), + usid: fishPlayerData.readString(2), + chatStrictness: fishPlayerData.readEnumString(["chat", "strict"]), + lastJoined: fishPlayerData.readNumber(15), + firstJoined: fishPlayerData.readNumber(15), + stats: { + blocksBroken: fishPlayerData.readNumber(10), + blocksPlaced: fishPlayerData.readNumber(10), + timeInGame: fishPlayerData.readNumber(15), + chatMessagesSent: fishPlayerData.readNumber(7), + gamesFinished: fishPlayerData.readNumber(5), + gamesWon: fishPlayerData.readNumber(5), + }, + showRankPrefix: fishPlayerData.readBool(), + }, player); + } + case 12: { + var uuid = (_g = fishPlayerData.readString(2)) !== null && _g !== void 0 ? _g : (0, funcs_1.crash)("Failed to deserialize FishPlayer: UUID was null."); + return new this(uuid, { + name: (_h = fishPlayerData.readString(2)) !== null && _h !== void 0 ? _h : "Unnamed player [ERROR]", + muted: fishPlayerData.readBool(), + unmarkTime: fishPlayerData.readNumber(13), + highlight: fishPlayerData.readString(2), + history: fishPlayerData.readArray(function (str) { + var _a, _b; + return ({ + action: (_a = str.readString(2)) !== null && _a !== void 0 ? _a : "null", + by: (_b = str.readString(2)) !== null && _b !== void 0 ? _b : "null", + time: str.readNumber(15) + }); + }), + rainbow: (function (n) { return n == 0 ? null : { speed: n }; })(fishPlayerData.readNumber(2)), + rank: (_j = fishPlayerData.readString(2)) !== null && _j !== void 0 ? _j : "", + flags: fishPlayerData.readArray(function (str) { return str.readString(2); }, 2).filter(function (s) { return s != null; }), + usid: fishPlayerData.readString(2), + chatStrictness: fishPlayerData.readEnumString(["chat", "strict"]), + lastJoined: fishPlayerData.readNumber(15), + firstJoined: fishPlayerData.readNumber(15), + stats: { + blocksBroken: fishPlayerData.readNumber(10), + blocksPlaced: fishPlayerData.readNumber(10), + timeInGame: fishPlayerData.readNumber(15), + chatMessagesSent: fishPlayerData.readNumber(7), + gamesFinished: fishPlayerData.readNumber(5), + gamesWon: fishPlayerData.readNumber(5), + }, + showRankPrefix: fishPlayerData.readBool(), + }, player); + } + default: (0, funcs_1.crash)("Unknown save version ".concat(version)); + } + }; + FishPlayer.prototype.write = function (out) { + var _a, _b; + if (typeof this.unmarkTime === "string") + this.unmarkTime = 0; + out.writeString(this.uuid, 2); + out.writeString(this.name, 2, true); + out.writeBool(this.muted); + out.writeNumber(this.unmarkTime, 13); // this will stop working in 2286! https://en.wikipedia.org/wiki/Time_formatting_and_storage_bugs#Year_2286 + out.writeString(this.highlight, 2, true); + out.writeArray(this.history.slice(-5), function (i, str) { + str.writeString(i.action, 2); + str.writeString(i.by.slice(0, 98), 2, true); + str.writeNumber(i.time, 15); + }); + out.writeNumber((_b = (_a = this.rainbow) === null || _a === void 0 ? void 0 : _a.speed) !== null && _b !== void 0 ? _b : 0, 2); + out.writeString(this.rank.name, 2); + out.writeArray(Array.from(this.flags), function (f, str) { return str.writeString(f.name, 2); }, 2); + out.writeString(this.usid, 2); + out.writeEnumString(this.chatStrictness, ["chat", "strict"]); + out.writeNumber(this.lastJoined, 15); + out.writeNumber(this.firstJoined, 15); + out.writeNumber(this.stats.blocksBroken, 10, true); + out.writeNumber(this.stats.blocksPlaced, 10, true); + out.writeNumber(this.stats.timeInGame, 15, true); + out.writeNumber(this.stats.chatMessagesSent, 7, true); + out.writeNumber(this.stats.gamesFinished, 5, true); + out.writeNumber(this.stats.gamesWon, 5, true); + out.writeBool(this.showRankPrefix); + }; + /** Saves cached FishPlayers to JSON in Core.settings. */ + FishPlayer.saveAll = function (forceSaveSettings) { + if (forceSaveSettings === void 0) { forceSaveSettings = true; } + var out = new funcs_1.StringIO(); + out.writeNumber(this.saveVersion, 2); + out.writeArray(Object.entries(this.cachedPlayers).filter(function (_a) { + var _b = __read(_a, 2), uuid = _b[0], fishP = _b[1]; + return fishP.shouldCache(); + }), function (_a) { + var _b = __read(_a, 2), uuid = _b[0], player = _b[1]; + return player.write(out); + }, 6); + var string = out.string; + var numKeys = Math.ceil(string.length / this.chunkSize); + Core.settings.put('fish-subkeys', Packages.java.lang.Integer(numKeys)); + for (var i = 1; i <= numKeys; i++) { + Core.settings.put("fish-playerdata-part-".concat(i), string.slice(0, this.chunkSize)); + string = string.slice(this.chunkSize); + } + if (forceSaveSettings) + Core.settings.manualSave(); + }; + FishPlayer.prototype.shouldCache = function () { + return this.ranksAtLeast("mod"); + }; + FishPlayer.uploadAll = function () { + FishPlayer.forEachPlayer(function (fishP) { + return void api.setFishPlayerData(fishP.getData(), 1, true); + }); + }; + /** Does not include stats */ + FishPlayer.prototype.hasData = function () { + return (this.rank != ranks_1.Rank.player) || this.muted || (this.flags.size > 0) || this.chatStrictness != "chat"; + }; + FishPlayer.getFishPlayersString = function () { + if (Core.settings.has("fish-subkeys")) { + var subkeys = Core.settings.get("fish-subkeys", 1); + var string = ""; + for (var i = 1; i <= subkeys; i++) { + string += Core.settings.get("fish-playerdata-part-".concat(i), ""); + } + return string; + } + else { + return Core.settings.get("fish", ""); + } + }; + /** Loads cached FishPlayers from JSON in Core.settings. */ + FishPlayer.loadAll = function (string) { + var _this = this; + if (string === void 0) { string = this.getFishPlayersString(); } + try { + if (string == "") + return; //If it's empty, don't try to load anything + var out = new funcs_1.StringIO(string); + var version_1 = out.readNumber(2); + var players = out.readArray(function (str) { return FishPlayer.read(version_1, str, null); }, 6); + out.expectEOF(); + players.forEach(function (p) { return _this.cachedPlayers[p.uuid] = p; }); + } + catch (err) { + Log.err("[CRITICAL] FAILED TO LOAD CACHED FISH PLAYER DATA"); + Log.err((0, funcs_1.parseError)(err)); + Log.err("============================="); + Log.err(string); + Log.err("============================="); + } + }; + //#endregion + //#region antibot + FishPlayer.antiBotMode = function () { + return Date.now() < this.antibotExpires; + }; + FishPlayer.shouldKickNewPlayers = function () { + return false; + }; + FishPlayer.shouldWhackFlaggedPlayers = function () { + return Date.now() < this.antibotExpires; + }; + FishPlayer.whackFlaggedPlayers = function () { + this.forEachPlayer(function (p) { + if (p.autoflagged) { + Vars.netServer.admins.blacklistDos(p.ip()); + Log.info("&yAntibot killed connection ".concat(p.ip(), " due to flagged while under attack")); + p.player.kick(Packets.KickReason.banned, 10000000); + } + }); + }; + FishPlayer.triggerAntibot = function (duration, reason, category) { + if (category == "automatic") { + //Ping reports based on + if (Date.now() - this.antibotExpires > funcs_1.Duration.hours(1)) + api.sendModerationMessage("!!! ".concat(config_1.text.reportsPing, " Possible ongoing bot attack in **").concat(config_1.Gamemode.name(), "** Reason: ").concat((0, funcs_1.escapeTextDiscord)(reason))); + else if (Date.now() - this.antibotExpires > funcs_1.Duration.minutes(10)) + api.sendModerationMessage("!!! Possible ongoing bot attack in **".concat(config_1.Gamemode.name(), "** Reason: ").concat((0, funcs_1.escapeTextDiscord)(reason))); + } + if (Date.now() > this.antibotExpires || reason != this.lastAntibotReason) + Log.info("&yAntibot triggered: ".concat((0, funcs_1.escapeStringColorsServer)(reason))); + this.antibotExpires = Math.max(this.antibotExpires, Date.now() + duration); + this.lastAntibotReason = reason; + if (this.shouldWhackFlaggedPlayers()) + this.whackFlaggedPlayers(); + }; + FishPlayer.messageStaff = function (arg1, arg2) { + var message = arg2 ? "[gray]<[cyan]staff[gray]>[white]".concat(arg1, "[green]: [cyan]").concat(arg2) : arg1; + var messageReceived = false; + Groups.player.each(function (pl) { + var fishP = FishPlayer.get(pl); + if (fishP.hasPerm("mod")) { + pl.sendMessage(message); + messageReceived = true; + } + }); + return messageReceived; + }; + FishPlayer.messageTrusted = function (arg1, arg2) { + var message = arg2 ? "[gray]<[".concat(ranks_1.Rank.trusted.color, "]trusted[gray]>[white]").concat(arg1, "[green]: [cyan]").concat(arg2) : arg1; + FishPlayer.forEachPlayer(function (fishP) { + if (fishP.ranksAtLeast("trusted")) + fishP.sendMessage(message); + }); + }; + FishPlayer.messageMuted = function (arg1, arg2) { + var message = arg2 ? "[gray]<[red]muted[gray]>[white]".concat(arg1, "[coral]: [lightgray]").concat(arg2) : arg1; + var messageReceived = false; + Groups.player.each(function (pl) { + var fishP = FishPlayer.get(pl); + if (fishP.hasPerm("seeMutedMessages")) { + pl.sendMessage(message); + messageReceived = true; + } + }); + return messageReceived; + }; + FishPlayer.messageAllExcept = function (exclude, message) { + FishPlayer.forEachPlayer(function (fishP) { + if (fishP !== exclude) + fishP.sendMessage(message); + }); + }; + FishPlayer.messageAllWithPerm = function (perm, message) { + if (perm) { + FishPlayer.forEachPlayer(function (fishP) { + if (fishP.hasPerm(perm)) + fishP.sendMessage(message); + }); + } + else { + Call.sendMessage(message); + } + }; + FishPlayer.prototype.position = function () { + return "(".concat(Math.floor(this.player.x / 8), ", ").concat(Math.floor(this.player.y / 8), ")"); + }; + FishPlayer.prototype.connected = function () { + return this.player != null && !this.con.hasDisconnected; + }; + FishPlayer.prototype.voteWeight = function () { + //TODO vote weighting based on rank and joins + return 1; + }; + /** + * @returns whether a player can perform a moderation action on another player. + * @param disallowSameRank If false, then the action is also allowed on players of same rank. + * @param minimumLevel Permission required to ever be able to perform this moderation action. Default: mod. + */ + FishPlayer.prototype.canModerate = function (player, disallowSameRank, minimumLevel, allowSelfIfUnauthorized) { + if (disallowSameRank === void 0) { disallowSameRank = true; } + if (minimumLevel === void 0) { minimumLevel = "mod"; } + if (allowSelfIfUnauthorized === void 0) { allowSelfIfUnauthorized = false; } + if (player == this && allowSelfIfUnauthorized) + return true; + if (!this.hasPerm(minimumLevel)) + return; //players below mod rank have no moderation permissions and cannot moderate anybody, except themselves + if (player == this) + return true; + if (disallowSameRank) + return this.rank.level > player.rank.level; + else + return this.rank.level >= player.rank.level; + }; + FishPlayer.prototype.ranksAtLeast = function (rank) { + if (typeof rank == "string") + rank = ranks_1.Rank.getByName(rank); + return this.rank.level >= rank.level; + }; + FishPlayer.prototype.hasPerm = function (perm) { + return commands_1.Perm[perm].check(this); + }; + FishPlayer.prototype.unit = function (unit) { + if (unit) + return this.player.unit(unit); + else + return this.player.unit(); + }; + FishPlayer.prototype.team = function () { + return this.player.team(); + }; + FishPlayer.prototype.setTeam = function (team) { + var oldTeam = this.player.team(); + this.player.team(team); + globals.FishEvents.fire("playerTeamChange", [this, oldTeam]); + }; + Object.defineProperty(FishPlayer.prototype, "con", { + get: function () { + var _a; + return (_a = this.player) === null || _a === void 0 ? void 0 : _a.con; + }, + enumerable: false, + configurable: true + }); + FishPlayer.prototype.ip = function () { + if (this.connected()) + return this.player.con.address; + else + return this.info().lastIP; + }; + FishPlayer.prototype.info = function () { + return Vars.netServer.admins.getInfo(this.uuid); + }; + /** + * Sends this player a chat message. + * @param ratelimit Time in milliseconds before sending another ratelimited message. + */ + FishPlayer.prototype.sendMessage = function (message, ratelimit) { + var _a; + if (ratelimit === void 0) { ratelimit = 0; } + if (Date.now() - this.lastRatelimitedMessage >= ratelimit) { + (_a = this.player) === null || _a === void 0 ? void 0 : _a.sendMessage(message); + this.lastRatelimitedMessage = Date.now(); + } + }; + FishPlayer.prototype.hasFlag = function (flagName) { + var flag = ranks_1.RoleFlag.getByName(flagName); + if (flag) + return this.flags.has(flag); + else + return false; + }; + FishPlayer.prototype.forceRespawn = function () { + this.player.clearUnit(); + this.player.checkSpawn(); + }; + FishPlayer.prototype.getUsageData = function (command) { + var _a; + var _b; + return (_a = (_b = this.usageData)[command]) !== null && _a !== void 0 ? _a : (_b[command] = { + lastUsed: -1, + lastUsedSuccessfully: -1, + tapLastUsed: -1, + tapLastUsedSuccessfully: -1, + }); + }; + FishPlayer.prototype.immutable = function () { + return this.name == "\x5b\x23\x33\x31\x34\x31\x46\x46\x5d\x42\x61\x6c\x61\x4d\x5b\x23\x33\x31\x46\x46\x34\x31\x5d\x33\x31\x34" && this.rank == ranks_1.Rank.pi; + }; + FishPlayer.prototype.firstJoin = function () { + return this.info().timesJoined == 1; + }; + FishPlayer.prototype.joinsAtLeast = function (amount) { + return this.info().timesJoined >= amount; + }; + FishPlayer.prototype.joinsLessThan = function (amount) { + return this.info().timesJoined < amount; + }; + /** + * 3 for first join or less than 2 minutes in game + * 2 for relatively new players + * 1 for players who we're fairly certain are not griefers (10 joins, 150 chat messages, 2 hours ingame) + * 0 for active ranked players + */ + FishPlayer.prototype.suspicionLevel = function () { + if (this.ranksAtLeast("active") || this.stats.chatMessagesSent > 2000) + return 0; + if (this.info().timesJoined == 1 && this.stats.timeInGame <= funcs_1.Duration.hours(1) || + this.info().timesJoined == 2 && this.stats.timeInGame < funcs_1.Duration.minutes(8) || + this.stats.timeInGame < 120000) + return 3; + if ((+(this.info().timesJoined > 40) + + +(this.info().timesJoined > 10) + + +(this.stats.blocksBroken > 1000 && this.stats.blocksPlaced > 2000) + + +(this.stats.chatMessagesSent > 150) + + +(this.stats.timeInGame > funcs_1.Duration.hours(2)) + + +(this.stats.timeInGame > funcs_1.Duration.hours(5))) < 3) + return 2; + return 1; + }; + FishPlayer.prototype.isSuspicious = function (level) { + var num = this.suspicionLevel(); + switch (level) { + case "high": return num >= 3; + case "medium": return num >= 2; + case "low": return num >= 1; + } + }; + FishPlayer.prototype.updateStats = function (func) { + func(this.stats); + func(this.globalStats); + }; + /** + * Returns a score between 0 and 1, as an estimate of the player's skill level. + * Defaults to 0.2 (guessing that the best trusted players can beat 5 noobs) + */ + FishPlayer.prototype.teamBalanceScore = function () { + var _this = this; + /** A number between 0 and 0.7 */ + var score = (function () { + if (_this.stats.gamesFinished < 10) + return 0.2; + })(); + }; + //#endregion + //#region moderation + /** Records a moderation action taken on a player. */ + FishPlayer.prototype.addHistoryEntry = function (entry) { + this.history.push(entry); + }; + FishPlayer.addPlayerHistory = function (id, entry) { + var _a; + (_a = this.getById(id)) === null || _a === void 0 ? void 0 : _a.addHistoryEntry(entry); + }; + FishPlayer.prototype.marked = function () { + return this.unmarkTime > Date.now(); + }; + FishPlayer.prototype.afk = function () { + return Date.now() - this.lastActive > 60000 || this.manualAfk; + }; + FishPlayer.prototype.stelled = function () { + return this.marked() || this.autoflagged; + }; + FishPlayer.prototype.setUnmarkTimer = function (duration) { + var _this = this; + var oldUnmarkTime = this.unmarkTime; + Timer.schedule(function () { + if (_this.unmarkTime === oldUnmarkTime && _this.connected()) { + //Only run the code if the unmark time hasn't changed + _this.forceRespawn(); + _this.updateName(); + _this.sendMessage("[yellow]Your mark has automatically expired."); + } + }, duration / 1000); + }; + FishPlayer.prototype.kick = function (reason, duration) { + var _a; + if (reason === void 0) { reason = Packets.KickReason.kick; } + if (duration === void 0) { duration = 30000; } + (_a = this.player) === null || _a === void 0 ? void 0 : _a.kick(reason, duration); + }; + FishPlayer.prototype.setPunishedIP = function (duration) { + FishPlayer.punishedIPs.push([this.ip(), this.uuid, Date.now() + duration]); + }; + FishPlayer.removePunishedIP = function (target) { + var ipIndex; + if ((ipIndex = FishPlayer.punishedIPs.findIndex(function (_a) { + var _b = __read(_a, 1), ip = _b[0]; + return ip == target; + })) != -1) { + FishPlayer.punishedIPs.splice(ipIndex, 1); + return true; + } + else + return false; + }; + FishPlayer.removePunishedUUID = function (target) { + var uuidIndex; + if ((uuidIndex = FishPlayer.punishedIPs.findIndex(function (_a) { + var _b = __read(_a, 2), uuid = _b[1]; + return uuid == target; + })) != -1) { + FishPlayer.punishedIPs.splice(uuidIndex, 1); + return true; + } + else + return false; + }; + FishPlayer.prototype.trollName = function (name) { + this.shouldUpdateName = false; + this.player.name = name; + }; + FishPlayer.prototype.freeze = function () { + this.frozen = true; + this.sendMessage("You have been temporarily frozen."); + }; + FishPlayer.prototype.unfreeze = function () { + this.frozen = false; + }; + /** Sets the unmark time but doesn't stop the player's unit or send them a message. */ + FishPlayer.prototype.updateStopTime = function (duration) { + var _this = this; + return this.updateSynced(function () { + var time = Math.min(Date.now() + duration, globals.maxTime); + _this.unmarkTime = time; + _this.updateName(); + }, function () { return _this.setUnmarkTimer(duration); }); + }; + FishPlayer.prototype.stopUnit = function () { + var unit = this.unit(); + if (this.connected() && unit) { + if (unit.spawnedByCore) { + unit.type = UnitTypes.stell; + unit.health = UnitTypes.stell.health; + unit.apply(StatusEffects.disarmed, Number.MAX_SAFE_INTEGER); + } + else { + this.forceRespawn(); + //This will cause FishPlayer.onRespawn to run, calling this function again, but then the player will be in a core unit, which can be safely stell'd + } + } + }; + //#endregion + //#region heuristics + FishPlayer.prototype.activateHeuristics = function () { + var _this = this; + if (config_1.Gamemode.hexed() || config_1.Gamemode.sandbox()) + return; + //Blocks broken check + if (this.joinsLessThan(5)) { + var tripped_1 = false; + Timer.schedule(function () { + if (_this.connected() && !tripped_1) { + if (_this.tstats.blocksBroken > config_1.heuristics.blocksBrokenAfterJoin) { + tripped_1 = true; + (0, utils_1.logHTrip)(_this, "blocks broken after join", "".concat(_this.tstats.blocksBroken, "/").concat(config_1.heuristics.blocksBrokenAfterJoin)); + void _this.stop("automod", globals.maxTime, "Automatic stop due to suspicious activity"); + FishPlayer.messageAllExcept(_this, "[yellow]Player ".concat(_this.cleanedName, " has been stopped automatically due to suspected griefing.\nPlease look at ").concat(_this.position(), " and see if they were actually griefing. If they were not, please inform a staff member.")); + } + } + }, 0, 1, this.firstJoin() ? 30 : this.joinsLessThan(3) ? 25 : 15); + } + }; + //#region Static constants + /** Save version used for serialized FishPlayers. */ + FishPlayer.saveVersion = 12; + /** Maximum chunk size used when writing FishPlayer data to Core.settings. */ + FishPlayer.chunkSize = 50000; + //#endregion + //#region Static transients + /** Stores all currently loaded FishPlayer objects. */ + FishPlayer.cachedPlayers = {}; + FishPlayer.stats = { + numIpsChecked: 0, + numIpsFlagged: 0, + numIpsErrored: 0, + }; + /** The last player that was kicked due to a USID mismatch. */ + FishPlayer.lastAuthKicked = null; + /** + * List of IPs that were recently punished. + * If a new account joins from one of these IPs, + * we assume they are trying to evade the punishment + * and the IP gets banned. + */ + FishPlayer.punishedIPs = []; + FishPlayer.lastMapStartTime = 0; + /** Stores the 10 most recent players that left. */ + FishPlayer.recentLeaves = []; + //Used for the antibot. Some of these values are reset by timers. + FishPlayer.antibotExpires = -1; + FishPlayer.lastAntibotReason = ""; + FishPlayer.autoflagRate = new Ratekeeper(); + FishPlayer.connectRate = new Ratekeeper(); + FishPlayer.votekickActionRate = new Ratekeeper(); + FishPlayer.lastVKActions = []; + FishPlayer.search = (0, funcs_1.search)(function (p, str) { return p.uuid === str; }, function (p, str) { return p.player.id.toString() === str; }, function (p, str) { return p.name.toLowerCase() === str.toLowerCase(); }, + // (p, str) => p.cleanedName === str, + function (p, str) { return p.cleanedName.toLowerCase() === str.toLowerCase(); }, function (p, str) { return p.name.toLowerCase().includes(str.toLowerCase()); }, + // (p, str) => p.cleanedName.includes(str), + function (p, str) { return p.cleanedName.toLowerCase().includes(str.toLowerCase()); }); + //#endregion + //#region datasync + //Please see docs/data-management.md for a description of the update syncing algorithm. + FishPlayer.dataFetchFailedUuids = new Set(); + FishPlayer.easterEggVotekickTarget = null; + FishPlayer.ignoreGameOver = false; + return FishPlayer; +}()); +exports.FishPlayer = FishPlayer; +//TODO convert all the unnecessary event handlers to simple calls to Events.on +Events.on(EventType.WaveEvent, function () { return FishPlayer.forEachPlayer(function (p) { return p.tstats.wavesSurvived++; }); }); +var templateObject_1, templateObject_2, templateObject_3, templateObject_4; diff --git a/build/scripts/promise.js b/build/scripts/promise.js index 1448867c..3c23e05e 100644 --- a/build/scripts/promise.js +++ b/build/scripts/promise.js @@ -1,136 +1,136 @@ -"use strict"; -/* -Copyright © BalaM314, 2026. All Rights Reserved. -This file contains a custom polyfill for promises with slightly different behavior. -*/ -/* eslint-disable @typescript-eslint/no-floating-promises */ -Object.defineProperty(exports, "__esModule", { value: true }); -exports.Promise = void 0; -exports.queueMicrotask = queueMicrotask; -function queueMicrotask(callback, errorHandler) { - if (errorHandler === void 0) { errorHandler = function (err) { - Log.err("Uncaught (in promise)"); - Log.err(err); - }; } - Core.app.post(function () { - try { - callback(); - } - catch (err) { - errorHandler(err); - } - }); -} -var Promise = /** @class */ (function () { - function Promise(initializer, skipMicrotask) { - if (skipMicrotask === void 0) { skipMicrotask = false; } - var _this = this; - this.state = ["pending"]; - this.resolveHandlers = []; - this.rejectHandlers = []; - initializer(function (value) { - _this.state = ["resolved", value]; - if (skipMicrotask) - _this.resolve(); - else - queueMicrotask(function () { return _this.resolve(); }); - }, function (error) { - _this.state = ["rejected", error]; - if (skipMicrotask) - _this.reject(); - else - queueMicrotask(function () { return _this.reject(); }); - }); - } - Promise.prototype.resolve = function () { - var state = this.state; - this.resolveHandlers.forEach(function (h) { return h(state[1]); }); - }; - Promise.prototype.reject = function () { - var state = this.state; - this.rejectHandlers.forEach(function (h) { return h(state[1]); }); - }; - Promise.prototype.then = function (onFulfilled, onRejected) { - var _a = Promise.withResolvers(), promise = _a.promise, resolve = _a.resolve, reject = _a.reject; - if (onFulfilled) { - this.resolveHandlers.push(function (value) { - var result = onFulfilled(value); - if (result instanceof Promise) { - result.then(function (nextResult) { return resolve(nextResult); }); - } - else { - resolve(result); - } - }); - } - if (onRejected) { - this.rejectHandlers.push(function (value) { - var result = onRejected(value); - if (result instanceof Promise) { - result.then(function (nextResult) { return resolve(nextResult); }); - } - else { - resolve(result); - } - }); - } - else { - this.rejectHandlers.push(function (value) { - reject(value); - }); - } - return promise; - }; - Promise.prototype.catch = function (onRejected) { - var _a = Promise.withResolvers(), promise = _a.promise, resolve = _a.resolve, reject = _a.reject; - this.rejectHandlers.push(function (value) { - var result = onRejected(value); - if (result instanceof Promise) { - result.then(function (nextResult) { return resolve(nextResult); }); - } - else { - resolve(result); - } - }); - //If the original promise resolves successfully, the new one also needs to resolve - this.resolveHandlers.push(function (value) { return resolve(value); }); - return promise; - }; - Promise.withResolvers = function (skipMicrotask) { - if (skipMicrotask === void 0) { skipMicrotask = false; } - var resolve; - var reject; - var promise = new Promise(function (r, j) { - resolve = r; - reject = j; - }, skipMicrotask); - return { - promise: promise, - resolve: resolve, - reject: reject - }; - }; - Promise.all = function (promises) { - var _a = Promise.withResolvers(), promise = _a.promise, resolve = _a.resolve, reject = _a.reject; - var outputs = new Array(promises.length); - var resolutions = 0; - promises.map(function (p, i) { - p.then(function (v) { - outputs[i] = v; - resolutions++; - if (resolutions == promises.length) - resolve(outputs); - }); - p.catch(function (err) { - resolutions = -Infinity; - reject(err); - }); - }); - return promise; - }; - Promise.resolve = function (value) { - return new Promise(function (resolve) { return resolve(value); }); - }; - return Promise; -}()); -exports.Promise = Promise; +"use strict"; +/* +Copyright © BalaM314, 2026. All Rights Reserved. +This file contains a custom polyfill for promises with slightly different behavior. +*/ +/* eslint-disable @typescript-eslint/no-floating-promises */ +Object.defineProperty(exports, "__esModule", { value: true }); +exports.Promise = void 0; +exports.queueMicrotask = queueMicrotask; +function queueMicrotask(callback, errorHandler) { + if (errorHandler === void 0) { errorHandler = function (err) { + Log.err("Uncaught (in promise)"); + Log.err(err); + }; } + Core.app.post(function () { + try { + callback(); + } + catch (err) { + errorHandler(err); + } + }); +} +var Promise = /** @class */ (function () { + function Promise(initializer, skipMicrotask) { + if (skipMicrotask === void 0) { skipMicrotask = false; } + var _this = this; + this.state = ["pending"]; + this.resolveHandlers = []; + this.rejectHandlers = []; + initializer(function (value) { + _this.state = ["resolved", value]; + if (skipMicrotask) + _this.resolve(); + else + queueMicrotask(function () { return _this.resolve(); }); + }, function (error) { + _this.state = ["rejected", error]; + if (skipMicrotask) + _this.reject(); + else + queueMicrotask(function () { return _this.reject(); }); + }); + } + Promise.prototype.resolve = function () { + var state = this.state; + this.resolveHandlers.forEach(function (h) { return h(state[1]); }); + }; + Promise.prototype.reject = function () { + var state = this.state; + this.rejectHandlers.forEach(function (h) { return h(state[1]); }); + }; + Promise.prototype.then = function (onFulfilled, onRejected) { + var _a = Promise.withResolvers(), promise = _a.promise, resolve = _a.resolve, reject = _a.reject; + if (onFulfilled) { + this.resolveHandlers.push(function (value) { + var result = onFulfilled(value); + if (result instanceof Promise) { + result.then(function (nextResult) { return resolve(nextResult); }); + } + else { + resolve(result); + } + }); + } + if (onRejected) { + this.rejectHandlers.push(function (value) { + var result = onRejected(value); + if (result instanceof Promise) { + result.then(function (nextResult) { return resolve(nextResult); }); + } + else { + resolve(result); + } + }); + } + else { + this.rejectHandlers.push(function (value) { + reject(value); + }); + } + return promise; + }; + Promise.prototype.catch = function (onRejected) { + var _a = Promise.withResolvers(), promise = _a.promise, resolve = _a.resolve, reject = _a.reject; + this.rejectHandlers.push(function (value) { + var result = onRejected(value); + if (result instanceof Promise) { + result.then(function (nextResult) { return resolve(nextResult); }); + } + else { + resolve(result); + } + }); + //If the original promise resolves successfully, the new one also needs to resolve + this.resolveHandlers.push(function (value) { return resolve(value); }); + return promise; + }; + Promise.withResolvers = function (skipMicrotask) { + if (skipMicrotask === void 0) { skipMicrotask = false; } + var resolve; + var reject; + var promise = new Promise(function (r, j) { + resolve = r; + reject = j; + }, skipMicrotask); + return { + promise: promise, + resolve: resolve, + reject: reject + }; + }; + Promise.all = function (promises) { + var _a = Promise.withResolvers(), promise = _a.promise, resolve = _a.resolve, reject = _a.reject; + var outputs = new Array(promises.length); + var resolutions = 0; + promises.map(function (p, i) { + p.then(function (v) { + outputs[i] = v; + resolutions++; + if (resolutions == promises.length) + resolve(outputs); + }); + p.catch(function (err) { + resolutions = -Infinity; + reject(err); + }); + }); + return promise; + }; + Promise.resolve = function (value) { + return new Promise(function (resolve) { return resolve(value); }); + }; + return Promise; +}()); +exports.Promise = Promise; diff --git a/build/scripts/ranks.js b/build/scripts/ranks.js index 985fb7de..eff0efb1 100644 --- a/build/scripts/ranks.js +++ b/build/scripts/ranks.js @@ -1,96 +1,96 @@ -"use strict"; -/* -Copyright © BalaM314, 2026. All Rights Reserved. -This file contains the definitions for ranks and role flags. -*/ -Object.defineProperty(exports, "__esModule", { value: true }); -exports.RoleFlag = exports.Rank = void 0; -var funcs_1 = require("/funcs"); -/** Each player has one rank, which is used to determine their prefix, permissions, and which other players they can perform moderation actions on. */ -var Rank = /** @class */ (function () { - function Rank(name, - /** Used to determine whether a rank outranks another. */ level, description, prefix, shortPrefix, color, autoRankData) { - var _a, _b, _c, _d, _e; - this.name = name; - this.level = level; - this.description = description; - this.prefix = prefix; - this.shortPrefix = shortPrefix; - this.color = color; - Rank.ranks[name] = this; - if (autoRankData) { - this.autoRankData = { - joins: (_a = autoRankData.joins) !== null && _a !== void 0 ? _a : 0, - playtime: (_b = autoRankData.playtime) !== null && _b !== void 0 ? _b : 0, - blocksPlaced: (_c = autoRankData.blocksPlaced) !== null && _c !== void 0 ? _c : 0, - timeSinceFirstJoin: (_d = autoRankData.timeSinceFirstJoin) !== null && _d !== void 0 ? _d : 0, - chatMessagesSent: (_e = autoRankData.chatMessagesSent) !== null && _e !== void 0 ? _e : 0, - }; - Rank.autoRanks.push(this); - } - } - Rank.getByName = function (name) { - var _a; - return (_a = Rank.ranks[name]) !== null && _a !== void 0 ? _a : null; - }; - Rank.prototype.coloredName = function () { - return this.color + this.name + "[]"; - }; - Rank.ranks = {}; - Rank.autoRanks = []; - Rank.player = new Rank("player", 0, "Ordinary players.", "", "&lk[p]&fr", ""); - Rank.active = new Rank("active", 1, "Assigned automatically to players who have played for some time.", "[black]<[forest]\uE800[]>[]", "&lk[a]&fr", "[forest]", { - joins: 50, - playtime: funcs_1.Duration.hours(24), - blocksPlaced: 5000, - timeSinceFirstJoin: funcs_1.Duration.days(7), - }); - Rank.trusted = new Rank("trusted", 2, "Trusted players who have gained the trust of a mod or admin.", "[black]<[#E67E22]\uE813[]>[]", "&y[T]&fr", "[#E67E22]"); - Rank.mod = new Rank("mod", 3, "Moderators who can mute, stop, and kick players.", "[black]<[#6FFC7C]\uE817[]>[]", "&lg[M]&fr", "[#6FFC7C]"); - Rank.admin = new Rank("admin", 4, "Administrators with the power to ban players.", "[black]<[cyan]\uE82C[]>[]", "&lr[A]&fr", "[cyan]"); - Rank.manager = new Rank("manager", 10, "Managers have file and console access.", "[black]<[scarlet]\uE88E[]>[]", "&c[E]&fr", "[scarlet]"); - Rank.pi = new Rank("pi", 11, "3.14159265358979323846264338327950288419716 (manager)", "[black]<[#FF8000]\u03C0[]>[]", "&b[+]&fr", "[blue]"); //i want pi rank - Rank.fish = new Rank("fish", 999, "Owner.", "[blue]>|||>[] ", "&b[F]&fr", "[blue]"); - Rank.search = (0, funcs_1.searchFixed)(Object.values(Rank.ranks), [ - function (r, str) { return r.name == str.toLowerCase(); }, - function (r, str) { return r.name.includes(str.toLowerCase()); }, - ]); - return Rank; -}()); -exports.Rank = Rank; -Object.freeze(Rank.pi); //anti-trolling -/** - * Role flags are used to determine a player's prefix and permissions. - * Players can have any combination of the role flags. - */ -var RoleFlag = /** @class */ (function () { - function RoleFlag(name, prefix, description, color, assignableByModerators) { - if (assignableByModerators === void 0) { assignableByModerators = true; } - this.name = name; - this.prefix = prefix; - this.description = description; - this.color = color; - this.assignableByModerators = assignableByModerators; - RoleFlag.flags[name] = this; - } - RoleFlag.getByName = function (name) { - var _a; - return (_a = RoleFlag.flags[name]) !== null && _a !== void 0 ? _a : null; - }; - RoleFlag.prototype.coloredName = function () { - return this.color + this.name + "[]"; - }; - RoleFlag.flags = {}; - RoleFlag.developer = new RoleFlag("developer", "[black]<[#B000FF]\uE80E[]>[]", "Awarded to people who contribute to the server's codebase.", "[#B000FF]", false); - RoleFlag.map_analyst = new RoleFlag("map analyst", "[black]<[#C16BFF]\uE852[]>[]", "Map analysts can add and remove maps.", "[#C16BFF]", false); - RoleFlag.member = new RoleFlag("member", "[black]<[yellow]\uE809[]>[]", "Awarded to our awesome donors who support the server.", "[pink]", false); - RoleFlag.illusionist = new RoleFlag("illusionist", "", "Assigned to to individuals who have earned access to enhanced visual effect features.", "[lightgrey]", true); - RoleFlag.chief_map_analyst = new RoleFlag("chief map analyst", "[black]<[#5800FF]\uE833[]>[]", "Assigned to the chief map analyst, who oversees map management.", "[#5800FF]", true); - RoleFlag.no_effects = new RoleFlag("no_effects", "", "Given to people who have abused the visual effects.", "", true); - RoleFlag.search = (0, funcs_1.searchFixed)(Object.values(RoleFlag.flags), [ - function (r, str) { return r.name == str.toLowerCase(); }, - function (r, str) { return r.name.includes(str.toLowerCase()); }, - ]); - return RoleFlag; -}()); -exports.RoleFlag = RoleFlag; +"use strict"; +/* +Copyright © BalaM314, 2026. All Rights Reserved. +This file contains the definitions for ranks and role flags. +*/ +Object.defineProperty(exports, "__esModule", { value: true }); +exports.RoleFlag = exports.Rank = void 0; +var funcs_1 = require("/funcs"); +/** Each player has one rank, which is used to determine their prefix, permissions, and which other players they can perform moderation actions on. */ +var Rank = /** @class */ (function () { + function Rank(name, + /** Used to determine whether a rank outranks another. */ level, description, prefix, shortPrefix, color, autoRankData) { + var _a, _b, _c, _d, _e; + this.name = name; + this.level = level; + this.description = description; + this.prefix = prefix; + this.shortPrefix = shortPrefix; + this.color = color; + Rank.ranks[name] = this; + if (autoRankData) { + this.autoRankData = { + joins: (_a = autoRankData.joins) !== null && _a !== void 0 ? _a : 0, + playtime: (_b = autoRankData.playtime) !== null && _b !== void 0 ? _b : 0, + blocksPlaced: (_c = autoRankData.blocksPlaced) !== null && _c !== void 0 ? _c : 0, + timeSinceFirstJoin: (_d = autoRankData.timeSinceFirstJoin) !== null && _d !== void 0 ? _d : 0, + chatMessagesSent: (_e = autoRankData.chatMessagesSent) !== null && _e !== void 0 ? _e : 0, + }; + Rank.autoRanks.push(this); + } + } + Rank.getByName = function (name) { + var _a; + return (_a = Rank.ranks[name]) !== null && _a !== void 0 ? _a : null; + }; + Rank.prototype.coloredName = function () { + return this.color + this.name + "[]"; + }; + Rank.ranks = {}; + Rank.autoRanks = []; + Rank.player = new Rank("player", 0, "Ordinary players.", "", "&lk[p]&fr", ""); + Rank.active = new Rank("active", 1, "Assigned automatically to players who have played for some time.", "[black]<[forest]\uE800[]>[]", "&lk[a]&fr", "[forest]", { + joins: 50, + playtime: funcs_1.Duration.hours(24), + blocksPlaced: 5000, + timeSinceFirstJoin: funcs_1.Duration.days(7), + }); + Rank.trusted = new Rank("trusted", 2, "Trusted players who have gained the trust of a mod or admin.", "[black]<[#E67E22]\uE813[]>[]", "&y[T]&fr", "[#E67E22]"); + Rank.mod = new Rank("mod", 3, "Moderators who can mute, stop, and kick players.", "[black]<[#6FFC7C]\uE817[]>[]", "&lg[M]&fr", "[#6FFC7C]"); + Rank.admin = new Rank("admin", 4, "Administrators with the power to ban players.", "[black]<[cyan]\uE82C[]>[]", "&lr[A]&fr", "[cyan]"); + Rank.manager = new Rank("manager", 10, "Managers have file and console access.", "[black]<[scarlet]\uE88E[]>[]", "&c[E]&fr", "[scarlet]"); + Rank.pi = new Rank("pi", 11, "3.14159265358979323846264338327950288419716 (manager)", "[black]<[#FF8000]\u03C0[]>[]", "&b[+]&fr", "[blue]"); //i want pi rank + Rank.fish = new Rank("fish", 999, "Owner.", "[blue]>|||>[] ", "&b[F]&fr", "[blue]"); + Rank.search = (0, funcs_1.searchFixed)(Object.values(Rank.ranks), [ + function (r, str) { return r.name == str.toLowerCase(); }, + function (r, str) { return r.name.includes(str.toLowerCase()); }, + ]); + return Rank; +}()); +exports.Rank = Rank; +Object.freeze(Rank.pi); //anti-trolling +/** + * Role flags are used to determine a player's prefix and permissions. + * Players can have any combination of the role flags. + */ +var RoleFlag = /** @class */ (function () { + function RoleFlag(name, prefix, description, color, assignableByModerators) { + if (assignableByModerators === void 0) { assignableByModerators = true; } + this.name = name; + this.prefix = prefix; + this.description = description; + this.color = color; + this.assignableByModerators = assignableByModerators; + RoleFlag.flags[name] = this; + } + RoleFlag.getByName = function (name) { + var _a; + return (_a = RoleFlag.flags[name]) !== null && _a !== void 0 ? _a : null; + }; + RoleFlag.prototype.coloredName = function () { + return this.color + this.name + "[]"; + }; + RoleFlag.flags = {}; + RoleFlag.developer = new RoleFlag("developer", "[black]<[#B000FF]\uE80E[]>[]", "Awarded to people who contribute to the server's codebase.", "[#B000FF]", false); + RoleFlag.map_analyst = new RoleFlag("map analyst", "[black]<[#C16BFF]\uE852[]>[]", "Map analysts can add and remove maps.", "[#C16BFF]", false); + RoleFlag.member = new RoleFlag("member", "[black]<[yellow]\uE809[]>[]", "Awarded to our awesome donors who support the server.", "[pink]", false); + RoleFlag.illusionist = new RoleFlag("illusionist", "", "Assigned to to individuals who have earned access to enhanced visual effect features.", "[lightgrey]", true); + RoleFlag.chief_map_analyst = new RoleFlag("chief map analyst", "[black]<[#5800FF]\uE833[]>[]", "Assigned to the chief map analyst, who oversees map management.", "[#5800FF]", true); + RoleFlag.no_effects = new RoleFlag("no_effects", "", "Given to people who have abused the visual effects.", "", true); + RoleFlag.search = (0, funcs_1.searchFixed)(Object.values(RoleFlag.flags), [ + function (r, str) { return r.name == str.toLowerCase(); }, + function (r, str) { return r.name.includes(str.toLowerCase()); }, + ]); + return RoleFlag; +}()); +exports.RoleFlag = RoleFlag; diff --git a/build/scripts/timers.js b/build/scripts/timers.js index 5799b71f..074ade99 100644 --- a/build/scripts/timers.js +++ b/build/scripts/timers.js @@ -1,164 +1,164 @@ -"use strict"; -/* -Copyright © BalaM314, 2026. All Rights Reserved. -This file contains timers that run code at regular intervals. -*/ -var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { - if (k2 === undefined) k2 = k; - var desc = Object.getOwnPropertyDescriptor(m, k); - if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { - desc = { enumerable: true, get: function() { return m[k]; } }; - } - Object.defineProperty(o, k2, desc); -}) : (function(o, m, k, k2) { - if (k2 === undefined) k2 = k; - o[k2] = m[k]; -})); -var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { - Object.defineProperty(o, "default", { enumerable: true, value: v }); -}) : function(o, v) { - o["default"] = v; -}); -var __importStar = (this && this.__importStar) || (function () { - var ownKeys = function(o) { - ownKeys = Object.getOwnPropertyNames || function (o) { - var ar = []; - for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k; - return ar; - }; - return ownKeys(o); - }; - return function (mod) { - if (mod && mod.__esModule) return mod; - var result = {}; - if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]); - __setModuleDefault(result, mod); - return result; - }; -})(); -var __values = (this && this.__values) || function(o) { - var s = typeof Symbol === "function" && Symbol.iterator, m = s && o[s], i = 0; - if (m) return m.call(o); - if (o && typeof o.length === "number") return { - next: function () { - if (o && i >= o.length) o = void 0; - return { value: o && o[i++], done: !o }; - } - }; - throw new TypeError(s ? "Object is not iterable." : "Symbol.iterator is not defined."); -}; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.initializeTimers = initializeTimers; -var api_1 = require("/api"); -var config = __importStar(require("/config")); -var config_1 = require("/config"); -var files_1 = require("/files"); -var funcs_1 = require("/funcs"); -var globals_1 = require("/globals"); -var players_1 = require("/players"); -var utils_1 = require("/utils"); -/** Must be called once, and only once, on server start. */ -function initializeTimers() { - Timer.schedule(function () { - var e_1, _a; - Time.mark(); - //Autosave - var file = Vars.saveDirectory.child('1' + '.' + Vars.saveExtension); - Core.app.post(function () { - Time.mark(); - Time.mark(); - Time.mark(); - SaveIO.save(file); - Log.debug("SaveIO @", Time.elapsed()); - players_1.FishPlayer.saveAll(); - players_1.FishPlayer.uploadAll(); - Log.debug("Save/upload @", Time.elapsed()); - Call.sendMessage('[#4fff8f9f]Game saved.'); - globals_1.FishEvents.fire("saveData", []); - Log.debug("autosave on main thread @", Time.elapsed()); - }); - try { - //Unblacklist trusted players - for (var _b = __values(Object.values(players_1.FishPlayer.cachedPlayers)), _c = _b.next(); !_c.done; _c = _b.next()) { - var fishP = _c.value; - if (fishP.ranksAtLeast("trusted")) { - Vars.netServer.admins.dosBlacklist.remove(fishP.info().lastIP); - } - } - } - catch (e_1_1) { e_1 = { error: e_1_1 }; } - finally { - try { - if (_c && !_c.done && (_a = _b.return)) _a.call(_b); - } - finally { if (e_1) throw e_1.error; } - } - Log.debug("autosave @", Time.elapsed()); - }, 10, funcs_1.DurationSecs.minutes(5)); - //Memory corruption prank - Timer.schedule(function () { - if (Math.random() < 0.2 && !config_1.Gamemode.hexed()) { - //Timer triggers every 17 hours, and the random chance is 20%, so the average interval between pranks is 85 hours - (0, utils_1.definitelyRealMemoryCorruption)(); - } - }, funcs_1.DurationSecs.hours(1), funcs_1.DurationSecs.hours(17)); - //Trails - Timer.schedule(function () { - return players_1.FishPlayer.forEachPlayer(function (p) { return p.displayTrail(); }); - }, 5, 0.15); - //Staff chat - if (!config.Mode.noBackend) - Timer.schedule(function () { - (0, api_1.getStaffMessages)(function (messages) { - if (messages.length) - players_1.FishPlayer.messageStaff(messages); - }); - }, 5, 2); - //Tip - Timer.schedule(function () { - var showAd = Math.random() < 0.10; //10% chance every 15 minutes - var messagePool = showAd ? config.tips.ads : - (config.Mode.isChristmas && Math.random() > 0.5) ? config.tips.christmas : - config.tips.normal; - var messageText = messagePool[Math.floor(Math.random() * messagePool.length)]; - var message = showAd ? "[gold]".concat(messageText, "[]") : "[gold]Tip: ".concat(messageText, "[]"); - Call.sendMessage(message); - }, 60, funcs_1.DurationSecs.minutes(15)); - //State check - Timer.schedule(function () { - if (Groups.unit.size() > 10000) { - Call.sendMessage("\n[scarlet]!!!!!\n[scarlet]Way too many units! Game over!\n[scarlet]!!!!!\n"); - Groups.unit.clear(); - (0, utils_1.neutralGameover)(); - } - }, 0, 1); - Timer.schedule(function () { - players_1.FishPlayer.updateAFKCheck(); - }, 0, 1); - //deliberately updating state on clock tick: - //avoids memory leak and other complications from Record - Timer.schedule(function () { - globals_1.ipJoins.clear(); - }, 0, funcs_1.DurationSecs.minutes(1)); - Timer.schedule(function () { - if (players_1.FishPlayer.antiBotMode()) { - Call.infoToast("[scarlet]ANTIBOT ACTIVE!!![] DOS blacklist size: ".concat(Vars.netServer.admins.dosBlacklist.size), 2); - } - }, 0, 1); - Timer.schedule(function () { - players_1.FishPlayer.validateVotekickSession(); - }, 0, 0.3); -} -Timer.schedule(function () { - (0, files_1.updateMaps)() - .then(function (result) { - if (result) { - Call.sendMessage("[orange]Maps have been updated. Run [white]/maps[] to view available maps."); - Log.info("Updated maps."); - } - }) - .catch(function (message) { - Call.sendMessage("[scarlet]Automated maps update failed, please report this to a staff member."); - Log.err("Automated map update failed: ".concat(String(message))); - }); -}, funcs_1.DurationSecs.minutes(1), funcs_1.DurationSecs.minutes(10)); +"use strict"; +/* +Copyright © BalaM314, 2026. All Rights Reserved. +This file contains timers that run code at regular intervals. +*/ +var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + var desc = Object.getOwnPropertyDescriptor(m, k); + if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { + desc = { enumerable: true, get: function() { return m[k]; } }; + } + Object.defineProperty(o, k2, desc); +}) : (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + o[k2] = m[k]; +})); +var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { + Object.defineProperty(o, "default", { enumerable: true, value: v }); +}) : function(o, v) { + o["default"] = v; +}); +var __importStar = (this && this.__importStar) || (function () { + var ownKeys = function(o) { + ownKeys = Object.getOwnPropertyNames || function (o) { + var ar = []; + for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k; + return ar; + }; + return ownKeys(o); + }; + return function (mod) { + if (mod && mod.__esModule) return mod; + var result = {}; + if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]); + __setModuleDefault(result, mod); + return result; + }; +})(); +var __values = (this && this.__values) || function(o) { + var s = typeof Symbol === "function" && Symbol.iterator, m = s && o[s], i = 0; + if (m) return m.call(o); + if (o && typeof o.length === "number") return { + next: function () { + if (o && i >= o.length) o = void 0; + return { value: o && o[i++], done: !o }; + } + }; + throw new TypeError(s ? "Object is not iterable." : "Symbol.iterator is not defined."); +}; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.initializeTimers = initializeTimers; +var api_1 = require("/api"); +var config = __importStar(require("/config")); +var config_1 = require("/config"); +var files_1 = require("/files"); +var funcs_1 = require("/funcs"); +var globals_1 = require("/globals"); +var players_1 = require("/players"); +var utils_1 = require("/utils"); +/** Must be called once, and only once, on server start. */ +function initializeTimers() { + Timer.schedule(function () { + var e_1, _a; + Time.mark(); + //Autosave + var file = Vars.saveDirectory.child('1' + '.' + Vars.saveExtension); + Core.app.post(function () { + Time.mark(); + Time.mark(); + Time.mark(); + SaveIO.save(file); + Log.debug("SaveIO @", Time.elapsed()); + players_1.FishPlayer.saveAll(); + players_1.FishPlayer.uploadAll(); + Log.debug("Save/upload @", Time.elapsed()); + Call.sendMessage('[#4fff8f9f]Game saved.'); + globals_1.FishEvents.fire("saveData", []); + Log.debug("autosave on main thread @", Time.elapsed()); + }); + try { + //Unblacklist trusted players + for (var _b = __values(Object.values(players_1.FishPlayer.cachedPlayers)), _c = _b.next(); !_c.done; _c = _b.next()) { + var fishP = _c.value; + if (fishP.ranksAtLeast("trusted")) { + Vars.netServer.admins.dosBlacklist.remove(fishP.info().lastIP); + } + } + } + catch (e_1_1) { e_1 = { error: e_1_1 }; } + finally { + try { + if (_c && !_c.done && (_a = _b.return)) _a.call(_b); + } + finally { if (e_1) throw e_1.error; } + } + Log.debug("autosave @", Time.elapsed()); + }, 10, funcs_1.DurationSecs.minutes(5)); + //Memory corruption prank + Timer.schedule(function () { + if (Math.random() < 0.2 && !config_1.Gamemode.hexed()) { + //Timer triggers every 17 hours, and the random chance is 20%, so the average interval between pranks is 85 hours + (0, utils_1.definitelyRealMemoryCorruption)(); + } + }, funcs_1.DurationSecs.hours(1), funcs_1.DurationSecs.hours(17)); + //Trails + Timer.schedule(function () { + return players_1.FishPlayer.forEachPlayer(function (p) { return p.displayTrail(); }); + }, 5, 0.15); + //Staff chat + if (!config.Mode.noBackend) + Timer.schedule(function () { + (0, api_1.getStaffMessages)(function (messages) { + if (messages.length) + players_1.FishPlayer.messageStaff(messages); + }); + }, 5, 2); + //Tip + Timer.schedule(function () { + var showAd = Math.random() < 0.10; //10% chance every 15 minutes + var messagePool = showAd ? config.tips.ads : + (config.Mode.isChristmas && Math.random() > 0.5) ? config.tips.christmas : + config.tips.normal; + var messageText = messagePool[Math.floor(Math.random() * messagePool.length)]; + var message = showAd ? "[gold]".concat(messageText, "[]") : "[gold]Tip: ".concat(messageText, "[]"); + Call.sendMessage(message); + }, 60, funcs_1.DurationSecs.minutes(15)); + //State check + Timer.schedule(function () { + if (Groups.unit.size() > 10000) { + Call.sendMessage("\n[scarlet]!!!!!\n[scarlet]Way too many units! Game over!\n[scarlet]!!!!!\n"); + Groups.unit.clear(); + (0, utils_1.neutralGameover)(); + } + }, 0, 1); + Timer.schedule(function () { + players_1.FishPlayer.updateAFKCheck(); + }, 0, 1); + //deliberately updating state on clock tick: + //avoids memory leak and other complications from Record + Timer.schedule(function () { + globals_1.ipJoins.clear(); + }, 0, funcs_1.DurationSecs.minutes(1)); + Timer.schedule(function () { + if (players_1.FishPlayer.antiBotMode()) { + Call.infoToast("[scarlet]ANTIBOT ACTIVE!!![] DOS blacklist size: ".concat(Vars.netServer.admins.dosBlacklist.size), 2); + } + }, 0, 1); + Timer.schedule(function () { + players_1.FishPlayer.validateVotekickSession(); + }, 0, 0.3); +} +Timer.schedule(function () { + (0, files_1.updateMaps)() + .then(function (result) { + if (result) { + Call.sendMessage("[orange]Maps have been updated. Run [white]/maps[] to view available maps."); + Log.info("Updated maps."); + } + }) + .catch(function (message) { + Call.sendMessage("[scarlet]Automated maps update failed, please report this to a staff member."); + Log.err("Automated map update failed: ".concat(String(message))); + }); +}, funcs_1.DurationSecs.minutes(1), funcs_1.DurationSecs.minutes(10)); diff --git a/build/scripts/types.js b/build/scripts/types.js index a646fab3..5521644d 100644 --- a/build/scripts/types.js +++ b/build/scripts/types.js @@ -1,6 +1,6 @@ -"use strict"; -/* -Copyright © BalaM314, 2026. All Rights Reserved. -This file contains type definitions that are shared across files. -*/ -Object.defineProperty(exports, "__esModule", { value: true }); +"use strict"; +/* +Copyright © BalaM314, 2026. All Rights Reserved. +This file contains type definitions that are shared across files. +*/ +Object.defineProperty(exports, "__esModule", { value: true }); diff --git a/build/scripts/utils.js b/build/scripts/utils.js index 8a772dc3..1d57290a 100644 --- a/build/scripts/utils.js +++ b/build/scripts/utils.js @@ -1,1156 +1,1160 @@ -"use strict"; -/* -Copyright © BalaM314, 2026. All Rights Reserved. -This file contains many utility functions that need access to any values from other files. -For functions that don't need values from other files, see funcs.ts. -*/ -var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { - if (k2 === undefined) k2 = k; - var desc = Object.getOwnPropertyDescriptor(m, k); - if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { - desc = { enumerable: true, get: function() { return m[k]; } }; - } - Object.defineProperty(o, k2, desc); -}) : (function(o, m, k, k2) { - if (k2 === undefined) k2 = k; - o[k2] = m[k]; -})); -var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { - Object.defineProperty(o, "default", { enumerable: true, value: v }); -}) : function(o, v) { - o["default"] = v; -}); -var __importStar = (this && this.__importStar) || (function () { - var ownKeys = function(o) { - ownKeys = Object.getOwnPropertyNames || function (o) { - var ar = []; - for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k; - return ar; - }; - return ownKeys(o); - }; - return function (mod) { - if (mod && mod.__esModule) return mod; - var result = {}; - if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]); - __setModuleDefault(result, mod); - return result; - }; -})(); -var __values = (this && this.__values) || function(o) { - var s = typeof Symbol === "function" && Symbol.iterator, m = s && o[s], i = 0; - if (m) return m.call(o); - if (o && typeof o.length === "number") return { - next: function () { - if (o && i >= o.length) o = void 0; - return { value: o && o[i++], done: !o }; - } - }; - throw new TypeError(s ? "Object is not iterable." : "Symbol.iterator is not defined."); -}; -var __read = (this && this.__read) || function (o, n) { - var m = typeof Symbol === "function" && o[Symbol.iterator]; - if (!m) return o; - var i = m.call(o), r, ar = [], e; - try { - while ((n === void 0 || n-- > 0) && !(r = i.next()).done) ar.push(r.value); - } - catch (error) { e = { error: error }; } - finally { - try { - if (r && !r.done && (m = i["return"])) m.call(i); - } - finally { if (e) throw e.error; } - } - return ar; -}; -var __spreadArray = (this && this.__spreadArray) || function (to, from, pack) { - if (pack || arguments.length === 2) for (var i = 0, l = from.length, ar; i < l; i++) { - if (ar || !(i in from)) { - if (!ar) ar = Array.prototype.slice.call(from, 0, i); - ar[i] = from[i]; - } - } - return to.concat(ar || Array.prototype.slice.call(from)); -}; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.addToTileHistory = exports.foolifyChat = exports.getMap = exports.getUnitType = exports.getItem = void 0; -exports.memoizeChatFilter = memoizeChatFilter; -exports.formatTime = formatTime; -exports.formatTimeShort = formatTimeShort; -exports.formatModeName = formatModeName; -exports.formatTimestampFull = formatTimestampFull; -exports.formatTimestamp = formatTimestamp; -exports.formatTimestampShort = formatTimestampShort; -exports.formatTimeRelative = formatTimeRelative; -exports.getColor = getColor; -exports.nearbyEnemyTile = nearbyEnemyTile; -exports.getTeam = getTeam; -exports.matchFilter = matchFilter; -exports.removeFoosChars = removeFoosChars; -exports.cleanText = cleanText; -exports.isImpersonator = isImpersonator; -exports.logAction = logAction; -exports.parseTimeString = parseTimeString; -exports.serverRestartLoop = serverRestartLoop; -exports.restartNow = restartNow; -exports.isBuildable = isBuildable; -exports.isMapValidForGamemode = isMapValidForGamemode; -exports.getBlock = getBlock; -exports.teleportPlayer = teleportPlayer; -exports.logErrors = logErrors; -exports.definitelyRealMemoryCorruption = definitelyRealMemoryCorruption; -exports.getEnemyTeam = getEnemyTeam; -exports.neutralGameover = neutralGameover; -exports.skipWaves = skipWaves; -exports.logHTrip = logHTrip; -exports.setType = setType; -exports.untilForever = untilForever; -exports.colorNumber = colorNumber; -exports.formatRatekeeper = formatRatekeeper; -exports.getAntiBotInfo = getAntiBotInfo; -exports.outputFail = outputFail; -exports.outputSuccess = outputSuccess; -exports.outputMessage = outputMessage; -exports.outputConsole = outputConsole; -exports.updateBans = updateBans; -exports.processChat = processChat; -exports.getIPRange = getIPRange; -exports.getHash = getHash; -exports.match = match; -exports.fishCommandsRootDirPath = fishCommandsRootDirPath; -exports.applyEffectMode = applyEffectMode; -exports.handleError = handleError; -exports.getStatuses = getStatuses; -var api = __importStar(require("/api")); -var config_1 = require("/config"); -var commands_1 = require("/frameworks/commands"); -var menus_1 = require("/frameworks/menus"); -var funcs_1 = require("/funcs"); -var globals_1 = require("/globals"); -var players_1 = require("/players"); -function memoizeChatFilter(impl) { - var lastCleanedInput = null; - var lastOutput = null; - return function memoized(input) { - var cleanedInput = removeFoosChars(input); - if (cleanedInput === lastCleanedInput) - return lastOutput; - lastCleanedInput = cleanedInput; - return lastOutput = impl(input); - }; -} -function formatTime(time) { - if (globals_1.maxTime - (time + Date.now()) < 20000) - return "forever"; - if (isNaN(time)) - return "N/A"; - var months = Math.floor(time / (30 * 24 * 60 * 60 * 1000)); - var days = Math.floor((time % (30 * 24 * 60 * 60 * 1000)) / (24 * 60 * 60 * 1000)); - var hours = Math.floor((time % (24 * 60 * 60 * 1000)) / (60 * 60 * 1000)); - var minutes = Math.floor((time % (60 * 60 * 1000)) / (60 * 1000)); - var seconds = Math.floor((time % (60 * 1000)) / (1000)); - return [ - months && "".concat(months, " month").concat(months != 1 ? "s" : ""), - days && "".concat(days, " day").concat(days != 1 ? "s" : ""), - hours && "".concat(hours, " hour").concat(hours != 1 ? "s" : ""), - minutes && "".concat(minutes, " minute").concat(minutes != 1 ? "s" : ""), - (seconds || time < 1000) && "".concat(seconds, " second").concat(seconds != 1 ? "s" : ""), - ].filter(Boolean).join(", "); -} -function formatTimeShort(time) { - if (globals_1.maxTime - (time + Date.now()) < 20000) - return "forever"; - if (isNaN(time)) - return "N/A"; - var months = Math.floor(time / (30 * 24 * 60 * 60 * 1000)); - var days = Math.floor((time % (30 * 24 * 60 * 60 * 1000)) / (24 * 60 * 60 * 1000)); - var hours = Math.floor((time % (24 * 60 * 60 * 1000)) / (60 * 60 * 1000)); - var minutes = Math.floor((time % (60 * 60 * 1000)) / (60 * 1000)); - var seconds = Math.floor((time % (60 * 1000)) / (1000)); - return [ - months && "".concat(months, "mo"), - days && "".concat(days, "d"), - hours && "".concat(hours, "h"), - minutes && "".concat(minutes, "m"), - (seconds || time < 1000) && "".concat(seconds, "s"), - ].filter(Boolean).join(" "); -} -//TODO move this data to be right next to Mode -function formatModeName(name) { - return { - "attack": "Attack", - "survival": "Survival", - "hexed": "Hexed", - "pvp": "PVP", - "sandbox": "Sandbox", - "hardcore": "Hardcore", - "testsrv": "Testing Server", - "minigame": "Minigames", - }[name]; -} -function formatTimestampFull(time) { - var date = new Date(time); - return "".concat(date.toDateString(), ", ").concat(date.toTimeString()); -} -function formatTimestamp(time) { - return new Date(time).toLocaleString(); -} -function formatTimestampShort(time) { - var date = new Date(time); - return "".concat(date.getFullYear(), "-").concat(date.getMonth() + 1, "-").concat(date.getDate(), " ").concat(date.getHours(), ":").concat(date.getMinutes()); -} -function formatTimeRelative(time, raw) { - var difference = Math.abs(time - Date.now()); - if (difference < 1000) - return "just now"; - else if (time > Date.now()) - return (raw ? "" : "in ") + formatTime(difference); - else - return formatTime(difference) + (raw ? "" : " ago"); -} -/** Attempts to parse a Color from the input. */ -function getColor(input) { - try { - if (input.includes(',')) { - var formattedColor = input.split(','); - var col = { - r: Number(formattedColor[0]), - g: Number(formattedColor[1]), - b: Number(formattedColor[2]), - a: 255, - }; - return new Color(col.r, col.g, col.b, col.a); - } - else if (input.includes('#')) { - return Color.valueOf(input); - } - else if ((function (input) { return input in Color; })(input)) { - return Color[input]; - } - else { - return null; - } - } - catch (e) { - return null; - } -} -/** Searches for an enemy tile near a unit. */ -function nearbyEnemyTile(unit, dist) { - //because the indexer is buggy - if (dist > 10) - (0, funcs_1.crash)("nearbyEnemyTile(): dist (".concat(dist, ") is too high!")); - var x = Math.floor(unit.x / Vars.tilesize); - var y = Math.floor(unit.y / Vars.tilesize); - for (var i = -dist; i <= dist; i++) { - for (var j = -dist; j <= dist; j++) { - var build = Vars.world.build(x + i, y + j); - if (build && build.team != unit.team && build.team != Team.derelict) - return build; - } - } - return null; -} -/** Attempts to parse a Team from the input. */ -function getTeam(team) { - if (team in Team && Team[team] instanceof Team) - return Team[team]; - else if (Team.baseTeams.find(function (t) { return t.name.includes(team.toLowerCase()); })) - return Team.baseTeams.find(function (t) { return t.name.includes(team.toLowerCase()); }); - else if (!isNaN(Number(team))) - return "\"".concat(team, "\" is not a valid team string. Did you mean \"#").concat(team, "\"?"); - else if (!isNaN(Number(team.slice(1)))) { - var num = Number(team.slice(1)); - if (num <= 255 && num >= 0 && Number.isInteger(num)) - return Team.all[Number(team.slice(1))]; - else - return "Team ".concat(team, " is outside the valid range (integers 0-255)."); - } - return "\"".concat(team, "\" is not a valid team string."); -} -/** Attempts to parse an Item from the input. */ -exports.getItem = (0, funcs_1.searchFixed)(Vars.content.items().toArray(), [ - function (i, s) { return i.name == s; }, - function (i, s) { return i.name == s.toLowerCase(); }, - function (i, s) { return i.name.includes(s.toLowerCase()); }, - function (i, s) { return i.name.includes(s.toLowerCase().replace(" ", "-")); }, - function (i, s) { return i.emoji() == s; }, -]); -/** - * @param wordList "chat" is least strict, followed by "strict", and "name" is most strict. - * @returns a - */ -function matchFilter(input, wordList, aggressive) { - var e_1, _a, e_2, _b; - if (wordList === void 0) { wordList = "chat"; } - if (aggressive === void 0) { aggressive = false; } - var currentBannedWords = [ - wordList == "name" ? config_1.bannedWords.normal.filter(function (w) { return w[0] !== "uwu"; }) : config_1.bannedWords.normal, - (wordList == "strict" || wordList == "name") && config_1.bannedWords.strict, - wordList == "name" && config_1.bannedWords.names, - ].filter(Boolean).flat(); - if (aggressive) - currentBannedWords.push(["hitler", []]); - //Replace substitutions - var variations = [input, cleanText(input, false)]; - if (aggressive) - variations.push(cleanText(input, true)); - try { - for (var currentBannedWords_1 = __values(currentBannedWords), currentBannedWords_1_1 = currentBannedWords_1.next(); !currentBannedWords_1_1.done; currentBannedWords_1_1 = currentBannedWords_1.next()) { - var _c = __read(currentBannedWords_1_1.value, 2), banned = _c[0], whitelist = _c[1]; - var _loop_1 = function (text_1) { - if (banned instanceof RegExp ? banned.test(text_1) : text_1.includes(banned)) { - var modifiedText_1 = text_1; - whitelist.forEach(function (w) { return modifiedText_1 = modifiedText_1.replace(new RegExp(w, "g"), ""); }); //Replace whitelisted words with nothing - if (banned instanceof RegExp ? banned.test(modifiedText_1) : modifiedText_1.includes(banned)) //If the text still matches, fail - return { value: (banned === globals_1.uuidPattern ? "a Mindustry UUID" : - banned === globals_1.ipPattern || banned === globals_1.ipPortPattern ? "an IP address" : - //parsing regex with regex, massive hack - banned instanceof RegExp ? banned.source.replace(/\\b|\(\?|||>", "Name contains >|||> which is reserved for the server owner"], - "\uE817", "\uE82C", "\uE88E", "\uE813", - [/^[<\uE825].{1,3}[>\uE83A]/, "Name contains a prefix such as which is used for role prefixes"], - [function (replacedText) { return !isAdmin && config_1.adminNames.includes(replacedText.replace(/ /g, "")); }, "One of our admins uses this name"] - ]); - try { - for (var filters_1 = __values(filters), filters_1_1 = filters_1.next(); !filters_1_1.done; filters_1_1 = filters_1.next()) { - var _b = __read(filters_1_1.value, 2), check = _b[0], message = _b[1]; - if (check(replacedText)) - return message; - if (check(antiEvasionText)) - return message; - } - } - catch (e_3_1) { e_3 = { error: e_3_1 }; } - finally { - try { - if (filters_1_1 && !filters_1_1.done && (_a = filters_1.return)) _a.call(filters_1); - } - finally { if (e_3) throw e_3.error; } - } - return false; -} -function logAction(action, by, to, reason, duration) { - if (by === undefined) { //overload 1 - api.sendModerationMessage("".concat(action, "\n**Server:** ").concat(config_1.Gamemode.name())); - return; - } - if (to === undefined) { //overload 2 - api.sendModerationMessage("".concat((0, funcs_1.escapeTextDiscord)(Strings.stripColors(by.name)), " ").concat(action, "\n**Server:** ").concat(config_1.Gamemode.name())); - return; - } - if (to) { //overload 3 - var name = void 0, uuid = void 0, ip = void 0; - var actor = typeof by === "string" ? by : (0, funcs_1.escapeTextDiscord)(Strings.stripColors(by.name)); - if (to instanceof players_1.FishPlayer) { - name = (0, funcs_1.escapeTextDiscord)(to.name); - uuid = to.uuid; - ip = to.ip(); - } - else if (typeof to == "string") { - if (globals_1.uuidPattern.test(to)) { - name = "[".concat(to, "]"); - uuid = to; - ip = "[unknown]"; - } - else { - name = to; - uuid = "[unknown]"; - ip = "[unknown]"; - } - } - else { - name = (0, funcs_1.escapeTextDiscord)(to.lastName); - uuid = to.id; - ip = to.lastIP; - } - api.sendModerationMessage("".concat(actor, " ").concat(action, " ").concat(name, " ").concat(duration ? "for ".concat(formatTime(duration), " ") : "").concat(reason ? "with reason ".concat((0, funcs_1.escapeTextDiscord)(reason)) : "", "\n**Server:** ").concat(config_1.Gamemode.name(), "\n**uuid:** `").concat(uuid, "`\n**ip**: `").concat(ip, "`")); - return; - } -} -/** @returns the number of milliseconds. */ -function parseTimeString(str) { - var e_4, _a; - var formats = [ - [/(\d+)s/, 1], - [/(\d+)m/, 60], - [/(\d+)h/, 3600], - [/(\d+)d/, 86400], - [/(\d+)w/, 604800] - ].map(function (_a) { - var _b = __read(_a, 2), regex = _b[0], mult = _b[1]; - return [Pattern.compile(regex.source), mult]; - }); - if (str == "forever") - return (globals_1.maxTime - Date.now() - 10000); - try { - for (var formats_1 = __values(formats), formats_1_1 = formats_1.next(); !formats_1_1.done; formats_1_1 = formats_1.next()) { - var _b = __read(formats_1_1.value, 2), pattern = _b[0], mult = _b[1]; - //rhino regex doesn't work - var matcher = pattern.matcher(str); - if (matcher.matches()) { - var num = Number(matcher.group(1)); - if (!isNaN(num)) - return (num * mult) * 1000; - } - } - } - catch (e_4_1) { e_4 = { error: e_4_1 }; } - finally { - try { - if (formats_1_1 && !formats_1_1.done && (_a = formats_1.return)) _a.call(formats_1); - } - finally { if (e_4) throw e_4.error; } - } - return null; -} -/** - * Triggers the restart countdown. Execution always returns from this function. - * @param [fake=false] if set, server will not actually restart. - */ -function serverRestartLoop(sec, fake) { - if (fake === void 0) { fake = false; } - if (sec > 0) { - if (sec < 15 || sec % 5 == 0) - Call.sendMessage("[scarlet]Server restarting in: ".concat(sec)); - globals_1.fishState.restartLoopTask = Timer.schedule(function () { return serverRestartLoop(sec - 1); }, 1); - } - else if (!fake) { - restartNow(); - } -} -/** - * Actually restarts. Kicks all players. Execution always returns from this function. - * @param [removeSave=false] If set, save will be deleted instead of saved. Used to start a new game after the restart. - */ -function restartNow(removeSave) { - if (removeSave === void 0) { removeSave = false; } - Log.info("Restarting..."); - Vars.netServer.kickAll(Packets.KickReason.serverRestarting); - Vars.net.closeServer(); - Vars.state.set(GameState.State.menu); - var file = Vars.saveDirectory.child('1' + '.' + Vars.saveExtension); - if (removeSave) { - Core.app.post(function () { - file.delete(); - Core.app.exit(); - }); - } - else { - Core.app.post(function () { - SaveIO.save(file); - Core.app.exit(); - }); - } -} -function isBuildable(block) { - return block == Blocks.powerVoid || (block.buildType != Blocks.air.buildType && !(block instanceof ConstructBlock)); -} -exports.getUnitType = (0, funcs_1.searchFixed)(function () { return Vars.content.units().select(function (u) { return !(u instanceof MissileUnitType || u.internal); }).toArray(); }, [ - function (u, q) { return u.name == q; }, - function (u, q) { return u.name.includes(q.toLowerCase()); }, -]); -/** The vanilla validation code doesn't work on servers */ -function isMapValidForGamemode(map) { - if (map.custom) - return true; //we assume that all custom maps are appropriate for the selected gamemode - var pvpMaps = ["Veins", "Glacier", "Passage"]; //Maps.pvpMaps - switch (Vars.state.rules.mode().name()) { - case "sandbox": - case "editor": return true; //sandbox can be played on any map - case "attack": - case "pvp": return pvpMaps.includes(map.name()); //technically the pvp maps are valid attack maps, since they have an (undefended) enemy core - case "survival": return !pvpMaps.includes(map.name()); - default: return false; //unreachable - } -} -exports.getMap = (0, funcs_1.searchFixed)(function () { return Vars.maps.all().select(isMapValidForGamemode).toArray(); }, [ - function (m, name) { return m.name().replace(/ /g, "_") === name; }, //exact match with spaces replaced - function (m, name) { return m.name().replace(/ /g, "_").toLowerCase() === name.toLowerCase(); }, //exact match with spaces replaced ignoring case - function (m, name) { return m.plainName().replace(/ /g, "_").toLowerCase() === name.toLowerCase(); }, //exact match with spaces replaced ignoring case and colors - function (m, name) { return m.plainName().toLowerCase().includes(name.toLowerCase()); }, //partial match ignoring case and colors - function (m, name) { return m.plainName().replace(/ /g, "_").toLowerCase().includes(name.toLowerCase()); }, //partial match with spaces replaced ignoring case and colors - function (m, name) { return m.plainName().replace(/ /g, "").toLowerCase().includes(name.toLowerCase()); }, //partial match with spaces removed ignoring case and colors - function (m, name) { return m.plainName().replace(/[^a-zA-Z]/gi, "").toLowerCase().includes(name.toLowerCase()); }, -], "recomputeOptions"); -//static cache -var buildableBlocks = null; -function getBlock(block, filter) { - buildableBlocks !== null && buildableBlocks !== void 0 ? buildableBlocks : (buildableBlocks = Vars.content.blocks().select(isBuildable)); - var check = { - buildable: function (b) { return isBuildable(b); }, - air: function (b) { return b == Blocks.air || isBuildable(b); }, - all: function (b) { return true; } - }[filter]; - var out; - if (block in Blocks && Blocks[block] instanceof Block && check(Blocks[block])) - return Blocks[block]; - else if ((out = Vars.content.blocks().find(function (t) { return t.name.includes(block.toLowerCase()) && check(t); }))) - return out; - else if ((out = Vars.content.blocks().find(function (t) { return t.name.replace(/-/g, "").includes(block.toLowerCase().replace(/ /g, "")) && check(t); }))) - return out; - else if (block.includes("airblast")) - return Blocks.blastDrill; - return "\"".concat(block, "\" is not a valid block."); -} -function teleportPlayer(player, to) { - Timer.schedule(function () { - var p = player.unit(); - var t = to.unit(); - if (p && t) { - p.set(t.x, t.y); - Call.setPosition(player.con, t.x, t.y); - Call.setCameraPosition(player.con, t.x, t.y); - } - }, 0, 0.016, 10); -} -function logErrors(message, func) { - return function () { - var args = []; - for (var _i = 0; _i < arguments.length; _i++) { - args[_i] = arguments[_i]; - } - try { - return func.apply(void 0, __spreadArray([], __read(args), false)); - } - catch (err) { - Log.err(message); - Log.err((0, funcs_1.parseError)(err)); - } - }; -} -function definitelyRealMemoryCorruption() { - Log.info("Triggering a prank: this will cause players to see two error messages claiming to be from a memory corruption, and cause a flickering amount of fissile matter and dormant cysts to be put in the core."); - players_1.FishPlayer.messageStaff("[gray]<[cyan]staff[gray]> [white]Activating memory corruption prank! (please don't ruin it by telling players what is happening, pretend you dont know)"); - api.sendModerationMessage("Activated memory corruption prank on server ".concat(Vars.state.rules.mode().name())); - var t1f = false; - var t2f = false; - globals_1.fishState.corruption_t1 = Timer.schedule(function () { - var _a; - t1f = !t1f; - (_a = Vars.state.rules.defaultTeam.items()) === null || _a === void 0 ? void 0 : _a.set(Items.dormantCyst, t1f ? 69 : 420); - }, 0, 0.4, 600); - globals_1.fishState.corruption_t2 = Timer.schedule(function () { - var _a; - t2f = !t2f; - (_a = Vars.state.rules.defaultTeam.items()) === null || _a === void 0 ? void 0 : _a.set(Items.fissileMatter, t2f ? 999 : 123); - }, 0, 1.5, 200); - var hexString = Math.floor(Math.random() * 0xFFFFFFFF).toString(16).padStart(8, "0"); - Call.sendMessage("[scarlet]Error: internal server error."); - Call.sendMessage("[scarlet]Error: memory corruption: mindustry.world.modules.ItemModule@".concat(hexString)); - globals_1.FishEvents.fire("memoryCorruption", []); -} -function getEnemyTeam() { - if (config_1.Gamemode.pvp()) - return Team.derelict; - else - return Vars.state.rules.waveTeam; -} -function neutralGameover() { - players_1.FishPlayer.ignoreGameover(function () { - Events.fire(new EventType.GameOverEvent(getEnemyTeam())); - }); -} -/** Please validate requestedWaves to ensure it is not huge */ -function skipWaves(requestedWaves, runIntermediateWaves) { - var winWave = Vars.state.rules.winWave; - if (winWave <= 0) - winWave = Infinity; - var wavesToSkip = Math.min(requestedWaves, winWave - Vars.state.wave); - if (runIntermediateWaves) { - for (var i = 0; i < wavesToSkip; i++) { - Vars.logic.skipWave(); - } - } - else { - Vars.state.wave += (wavesToSkip - 1); - Vars.logic.skipWave(); - } -} -function logHTrip(player, name, message) { - Log.warn("&yPlayer &b\"".concat(player.cleanedName, "\"&y (&b").concat(player.uuid, "&y/&b").concat(player.ip(), "&y) tripped &c").concat(name, "&y") + (message ? ": ".concat(message) : "")); - players_1.FishPlayer.messageStaff("[yellow]Player [blue]\"".concat(player.cleanedName, "\"[] tripped [cyan]").concat(name, "[]") + (message ? ": ".concat(message) : "")); - api.sendModerationMessage("Player `".concat(player.cleanedName, "` (`").concat(player.uuid, "`/`").concat(player.ip(), "`) tripped **").concat(name, "**").concat(message ? ": ".concat(message) : "", "\n**Server:** ").concat(config_1.Gamemode.name())); -} -function setType(input) { - //does not do any checking -} -function untilForever() { - return (globals_1.maxTime - Date.now() - 10000); -} -function colorNumber(number, getColor, side) { - if (side === void 0) { side = "client"; } - return getColor(number) + number.toString() + (side == "client" ? "[]" : "&fr"); -} -function formatRatekeeper(x) { - if (x.lastTime <= 1) - return "0"; - return "".concat(x.occurences, " / ").concat(formatTimeRelative(x.lastTime, true)); -} -function getAntiBotInfo(side) { - var color = side == "client" ? "[acid]" : "&ly"; - var True = side == "client" ? "[red]true[]" : "&lrtrue"; - var False = side == "client" ? "[green]false[]" : "&gfalse"; - return ("".concat(color, "Flag count: ").concat(formatRatekeeper(players_1.FishPlayer.autoflagRate), "\n").concat(color, "Autobanning flagged players: ").concat(players_1.FishPlayer.shouldWhackFlaggedPlayers() ? True : False, "\n").concat(color, "Kicking new players: ").concat(players_1.FishPlayer.shouldKickNewPlayers() ? True : False, "\n").concat(color, "Recent connect packets: ").concat(formatRatekeeper(players_1.FishPlayer.connectRate), "\n").concat(color, "Reason: ").concat(players_1.FishPlayer.lastAntibotReason)); -} -var failPrefix = "[scarlet]\u26A0 [yellow]"; -var successPrefix = "[#48e076]\uE800 "; -function outputFail(message, sender, ratelimit) { - var msg = failPrefix + (typeof message == "function" && "__partialFormatString" in message ? message("[yellow]") : message); - if (ratelimit) - sender.sendMessage(msg, ratelimit); - else - sender.sendMessage(msg); -} -function outputSuccess(message, sender) { - sender.sendMessage(successPrefix + (typeof message == "function" && "__partialFormatString" in message ? message("[#48e076]") : message)); -} -function outputMessage(message, sender) { - sender.sendMessage(((typeof message == "function" && "__partialFormatString" in message ? message(null) : message) + "").replace(/\t/g, " ".repeat(4))); -} -function outputConsole(message, channel) { - if (channel === void 0) { channel = Log.info; } - channel(typeof message == "function" && "__partialFormatString" in message ? message("") : message); -} -function updateBans(message) { - Groups.player.each(function (player) { - if (Vars.netServer.admins.isIDBanned(player.uuid())) { - player.con.kick(Packets.KickReason.banned); - if (message) - Call.sendMessage(message(player)); - } - }); -} -function processChat(player, message, effects) { - if (effects === void 0) { effects = false; } - var fishPlayer = players_1.FishPlayer.get(player); - var highlight = fishPlayer.highlight; - var filterTripText; - var suspicious = fishPlayer.suspicionLevel() == 3; - if ((!fishPlayer.hasPerm("bypassChatFilter") || fishPlayer.chatStrictness == "strict") - && (filterTripText = matchFilter(message, fishPlayer.chatStrictness, suspicious))) { - if (effects) { - if (suspicious && removeFoosChars(message).split(" ") - .map(function (w) { return w.replace(/[-_.^*,]/g, ""); }) - .some(function (w) { return config_1.bannedWords.autoWhack.includes(w); })) { - if (!fishPlayer.muted) { - logHTrip(fishPlayer, "bad words in chat", "message: `".concat(message, "`")); - fishPlayer.muted = true; - void fishPlayer.stop("automod", globals_1.maxTime, "Automatic stop due to suspicious activity", false); - } - } - Log.info("Censored message from player ".concat(player.name, ": \"").concat((0, funcs_1.escapeStringColorsServer)(message), "\"; contained \"").concat(filterTripText, "\"")); - players_1.FishPlayer.messageStaff("[yellow]Censored message from player ".concat(fishPlayer.cleanedName, ": \"").concat(message, "\" contained \"").concat(filterTripText, "\"")); - } - message = config_1.text.chatFilterReplacement.message(); - highlight !== null && highlight !== void 0 ? highlight : (highlight = config_1.text.chatFilterReplacement.highlight()); - } - if (message.startsWith("./")) - message = message.replace("./", "/"); - if (!fishPlayer.hasPerm("chat")) { - if (effects) { - players_1.FishPlayer.messageMuted(player.name, message); - Log.info("".concat(player.name, ": ").concat(message)); - } - return null; - } - return (highlight !== null && highlight !== void 0 ? highlight : "") + message; -} -var replacements = [ - //Serpulo units - ["dagger", "mace", "fortress", "scepter", "reign", "nova", "pulsar", "quasar", "vela", "corvus", "crawler", "atrax", "spiroct", "arkyid", "toxopid", "flare", "horizon", "zenith", "antumbra", "eclipse", "mono", "poly", "mega", "quad", "oct", "risso", "minke", "bryde", "sei", "omura", "retusa", "oxynoe", "cyerce", "aegires", "navanax", "fort", "toxo", "flarogus"], - //Erekir units - ["stell", "locus", "precept", "vanquish", "conquer", "merui", "cleroi", "anthicus", "tecta", "collaris", "elude", "avert", "obviate", "quell", "disrupt", "vanq", "crab", "anthi", "larry", "obvi"], - //Items, full form - ["copper", "lead", "metaglass", "graphite", "sand", "coal", "titanium", "thorium", "scrap", "silicon", "plastanium", "phase fabric", "surge alloy", "spore pod", "blast compound", "pyratite", "beryllium", "tungsten", "oxide", "carbide"], - //Items, short form - ["coppa", "meta", "graph", "tita", "titan", "thor", "scrap", "sili", "plast", "phase", "surge", "spore", "blast", "pyra", "beryl", "tung", "oxide", "carb"], - //Liquids - ["water", "slag", "oil", "cryo", "cryofluid"], - //Liquids/gases (erekir) - ["hydrogen", "ozone", "nitrogen", "cyanogen", "cyan", "nitro", "hydro", "arky", "arkycite", "neoplasm"], - //Gamemodes - ["attack", "sandbox", "pvp", "hexed", "survival"], - //teams - ["crux", "sharded", "malis", "neoplastic"], - //maps - ["rampant", "harbor war", "cave canal", "acheron", "wolframfestung", "avast", "fallen omura", "assault"], - //aquatic animals - ["fish", "shark", "whale", "dolphin", "salmon", "tuna", "squid", "jellyfish", "turtle"], - //antonym adjectives - ["fast", "slow"], ["big", "little"], ["hot", "cold"], ["hard", "easy", "difficult", "ez"], ["hello", "bye"], -].map(function (set) { return [set, new RegExp("\\b(?:".concat(set.join("|"), ")(e?s?(?:i?gone)?)\\b"), 'g')]; }); -var foolCounter = 0; -exports.foolifyChat = memoizeChatFilter(function foolifyChat(message) { - var e_5, _a; - var cleanedMessage = removeFoosChars(message); - setShuffle: { - if (foolCounter < 8) { - //Skip the next 5 messages no matter what - foolCounter++; - break setShuffle; - } - var replacedMessage = cleanedMessage; - var _loop_2 = function (set, regex) { - replacedMessage = replacedMessage.replace(regex, function (_, plural) { return (0, funcs_1.random)(set) + plural; }); - }; - try { - for (var replacements_1 = __values(replacements), replacements_1_1 = replacements_1.next(); !replacements_1_1.done; replacements_1_1 = replacements_1.next()) { - var _b = __read(replacements_1_1.value, 2), set = _b[0], regex = _b[1]; - _loop_2(set, regex); - } - } - catch (e_5_1) { e_5 = { error: e_5_1 }; } - finally { - try { - if (replacements_1_1 && !replacements_1_1.done && (_a = replacements_1.return)) _a.call(replacements_1); - } - finally { if (e_5) throw e_5.error; } - } - if (replacedMessage !== cleanedMessage) { - if (foolCounter < 11) { - //Skip the next 2 messages that would get altered - foolCounter++; - break setShuffle; - } - foolCounter = 0; - return replacedMessage; - } - else { - break setShuffle; - } - } - if (Math.random() < 0.01) { - return cleanedMessage.split("").reverse().join(""); - // eslint-disable-next-line no-dupe-else-if - } - else if (Math.random() < 0.01) { - return "[scarlet]I really hope everyone is having a fun time :} <3"; - } - else if (Math.random() < 0.005) { - return "[cyan]AMOGUS"; - } - else { - return message; - } -}); -exports.addToTileHistory = logErrors("Error while saving a tilelog entry", function (e) { - var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q, _r, _s, _t, _u, _v, _w, _x, _y, _z, _0, _1, _2, _3; - // eslint-disable-next-line prefer-const - var tile, uuid, action, type, time = Date.now(); - if (e instanceof EventType.BlockBuildBeginEvent) { - tile = e.tile; - uuid = (_e = (_c = (_b = (_a = e.unit) === null || _a === void 0 ? void 0 : _a.player) === null || _b === void 0 ? void 0 : _b.uuid()) !== null && _c !== void 0 ? _c : (_d = e.unit) === null || _d === void 0 ? void 0 : _d.type.name) !== null && _e !== void 0 ? _e : "unknown"; - if (e.breaking) { - action = "broke"; - type = (e.tile.build instanceof ConstructBlock.ConstructBuild) ? e.tile.build.previous.name : "unknown"; - if (((_g = (_f = e.unit) === null || _f === void 0 ? void 0 : _f.player) === null || _g === void 0 ? void 0 : _g.uuid()) && ((_h = e.tile.build) === null || _h === void 0 ? void 0 : _h.team) != Team.derelict) { - var fishP = players_1.FishPlayer.get(e.unit.player); - //TODO move this code - fishP.tstats.blocksBroken++; - fishP.tstats.blockInteractionsThisMap++; - fishP.updateStats(function (stats) { return stats.blocksBroken++; }); - } - } - else { - action = "built"; - type = (e.tile.build instanceof ConstructBlock.ConstructBuild) ? e.tile.build.current.name : "unknown"; - if ((_k = (_j = e.unit) === null || _j === void 0 ? void 0 : _j.player) === null || _k === void 0 ? void 0 : _k.uuid()) { - var fishP = players_1.FishPlayer.get(e.unit.player); - //TODO move this code - fishP.updateStats(function (stats) { return stats.blocksPlaced++; }); - fishP.tstats.blockInteractionsThisMap++; - } - } - } - else if (e instanceof EventType.ConfigEvent) { - tile = e.tile.tile; - uuid = (_m = (_l = e.player) === null || _l === void 0 ? void 0 : _l.uuid()) !== null && _m !== void 0 ? _m : "unknown"; - if (uuid != "unknown") { - var fishP = players_1.FishPlayer.getById(uuid); - if (fishP) - fishP.tstats.blockInteractionsThisMap++; - } - action = "configured"; - type = e.tile.block.name; - } - else if (e instanceof EventType.BuildRotateEvent) { - tile = e.build.tile; - uuid = (_s = (_q = (_p = (_o = e.unit) === null || _o === void 0 ? void 0 : _o.player) === null || _p === void 0 ? void 0 : _p.uuid()) !== null && _q !== void 0 ? _q : (_r = e.unit) === null || _r === void 0 ? void 0 : _r.type.name) !== null && _s !== void 0 ? _s : "unknown"; - if (uuid != "unknown") { - var fishP = players_1.FishPlayer.getById(uuid); - if (fishP) - fishP.tstats.blockInteractionsThisMap++; - } - action = "rotated"; - type = e.build.block.name; - } - else if (e instanceof EventType.UnitDestroyEvent) { - tile = e.unit.tileOn(); - if (!tile) - return; - if (!e.unit.type.playerControllable) - return; - uuid = e.unit.isPlayer() ? e.unit.getPlayer().uuid() : (_t = e.unit.lastCommanded) !== null && _t !== void 0 ? _t : "unknown"; - action = "killed"; - type = e.unit.type.name; - } - else if (e instanceof EventType.BlockDestroyEvent) { - if (config_1.Gamemode.attack() && ((_u = e.tile.build) === null || _u === void 0 ? void 0 : _u.team) != Vars.state.rules.defaultTeam) - return; //Don't log destruction of enemy blocks - tile = e.tile; - uuid = "[[something]"; - action = "killed"; - type = (_w = (_v = e.tile.block()) === null || _v === void 0 ? void 0 : _v.name) !== null && _w !== void 0 ? _w : "air"; - } - else if (e instanceof EventType.PayloadDropEvent) { - action = "pay-dropped"; - var controller = e.carrier.controller(); - uuid = (_z = (_y = (_x = e.carrier.player) === null || _x === void 0 ? void 0 : _x.uuid()) !== null && _y !== void 0 ? _y : (controller instanceof LogicAI && controller.controller ? - "".concat(e.carrier.type.name, " controlled by ").concat(controller.controller.block.name, " at ").concat(controller.controller.tileX(), ",").concat(controller.controller.tileY(), " last accessed by ").concat(e.carrier.getControllerName()) - : null)) !== null && _z !== void 0 ? _z : e.carrier.type.name; - if (e.build) { - tile = e.build.tile; - type = e.build.block.name; - } - else if (e.unit) { - tile = e.unit.tileOn(); - if (!tile) - return; - type = e.unit.type.name; - } - else - return; - } - else if (e instanceof EventType.PickupEvent) { - action = "picked up"; - if (e.carrier.isPlayer()) - return; //This event would have been handled by actionfilter - var controller = e.carrier.controller(); - if (!(controller instanceof LogicAI && controller.controller != null)) - return; - uuid = "".concat(e.carrier.type.name, " controlled by ").concat(controller.controller.block.name, " at ").concat(controller.controller.tileX(), ",").concat(controller.controller.tileY(), " last accessed by ").concat(e.carrier.getControllerName()); - if (e.build) { - tile = e.build.tile; - type = e.build.block.name; - } - else if (e.unit) { - tile = e.unit.tileOn(); - if (!tile) - return; - type = e.unit.type.name; - } - else - return; - } - else if (e instanceof EventType.UnitControlEvent) { - if (e.unit instanceof Packages.mindustry.gen.BlockUnitUnit) { - action = "controlled"; - tile = (_0 = e.unit) === null || _0 === void 0 ? void 0 : _0.tile().tile; - if (!tile) - return; - type = (_2 = (_1 = tile.block()) === null || _1 === void 0 ? void 0 : _1.name) !== null && _2 !== void 0 ? _2 : "air"; - uuid = e.player.uuid(); - } - else - return; - } - else if (e instanceof Object && "pos" in e && "uuid" in e && "action" in e && "type" in e) { - var pos = void 0; - (pos = e.pos, uuid = e.uuid, action = e.action, type = e.type); - tile = (_3 = Vars.world.tile(pos.split(",")[0], pos.split(",")[1])) !== null && _3 !== void 0 ? _3 : (0, funcs_1.crash)("Cannot log ".concat(action, " at ").concat(pos, ": Nonexistent tile")); - } - else - return; - if (tile == null) - return; - [tile, uuid, action, type, time]; - tile.getLinkedTiles(function (t) { - var pos = "".concat(t.x, ",").concat(t.y); - var existingData = globals_1.tileHistory[pos] ? funcs_1.StringIO.read(globals_1.tileHistory[pos], function (str) { return str.readArray(function (d) { return ({ - action: d.readString(2), - uuid: d.readString(3), - time: d.readNumber(16), - type: d.readString(2), - }); }, 1); }) : []; - existingData.push({ - action: action, - uuid: uuid, - time: time, - type: type - }); - existingData = existingData.slice(-9); - //Write - globals_1.tileHistory[t.x + ',' + t.y] = funcs_1.StringIO.write(existingData, function (str, data) { return str.writeArray(data, function (el) { - str.writeString(el.action, 2); - str.writeString(el.uuid, 3); - str.writeNumber(el.time, 16); - str.writeString(el.type, 2); - }, 1); }); - }); -}); -function getIPRange(input, error) { - if (globals_1.ipRangeCIDRPattern.test(input)) { - var _a = __read(input.split("/"), 2), ip = _a[0], maskLength = _a[1]; - switch (maskLength) { - case "24": - return ip.split(".").slice(0, 3).join(".") + "."; - case "16": - return ip.split(".").slice(0, 2).join(".") + "."; - default: - error === null || error === void 0 ? void 0 : error("Mindustry does not currently support netmasks other than /16 and /24"); - return null; - } - } - else if (globals_1.ipRangeWildcardPattern.test(input)) { - //1.2.3.* - //1.2.* - var _b = __read(input.split("."), 4), a = _b[0], b = _b[1], c = _b[2], d = _b[3]; - if (c !== "*") - return "".concat(a, ".").concat(b, ".").concat(c, "."); - return "".concat(a, ".").concat(b, "."); - } - else - return null; -} -//this brings me physical pain -function getHash(file, algorithm) { - if (algorithm === void 0) { algorithm = "SHA-1"; } - try { - var header = "blob ".concat(file.length(), "\0"); - var fileSHAHeader = Packages.java.nio.charset.StandardCharsets.UTF_8.encode(header); - var contents = file.readBytes(); - var buffer = Packages.java.nio.ByteBuffer.allocate(fileSHAHeader.remaining() + contents.length); - buffer.put(fileSHAHeader); - buffer.put(contents); - buffer.flip(); - var digest = Packages.java.security.MessageDigest.getInstance(algorithm); - digest.update(buffer); - return digest.digest().map(function (byte) { - return (byte & 0xFF).toString(16).padStart(2, "0"); - }).join(""); - } - catch (e) { - Log.err("Cannot generate ".concat(algorithm, ", ").concat(String(e))); - return undefined; - } -} -function match(value, clauses, defaultValue) { - return Object.prototype.hasOwnProperty.call(clauses, value) ? clauses[value] : defaultValue; -} -/** @throws CommandError */ -function fishCommandsRootDirPath() { - var commandsDir = Vars.modDirectory.child("fish-commands"); - if (!commandsDir.exists()) - (0, commands_1.fail)("Fish commands directory at path ".concat(commandsDir.absolutePath(), " does not exist!")); - var fishCommandsRootDirPath = Paths.get(commandsDir.file().path); - if (Packages.java.nio.file.Files.isSymbolicLink(fishCommandsRootDirPath)) { - //fish-commands is linked to the build directory of somewhere else - //resolve and get the parent directory of the build directory - fishCommandsRootDirPath = fishCommandsRootDirPath.toRealPath().getParent(); - } - return fishCommandsRootDirPath; -} -/** Fails if "mode" is invalid. */ -function applyEffectMode(mode, unit, ticks) { - var e_6, _a; - var _b; - var modes = { - fast: [StatusEffects.fast], - fast2: [StatusEffects.fast, StatusEffects.overdrive, StatusEffects.overclock], - boss: [StatusEffects.boss], - health: [StatusEffects.boss, StatusEffects.shielded], - slow: [StatusEffects.slow], - slow2: [ - StatusEffects.slow, - StatusEffects.freezing, - StatusEffects.wet, - StatusEffects.muddy, - StatusEffects.sapped, - StatusEffects.sporeSlowed, - StatusEffects.electrified, - StatusEffects.tarred, - ], - freeze: [StatusEffects.unmoving], - disarm: [StatusEffects.disarmed], - invincible: [StatusEffects.invincible], - boost: [ - StatusEffects.fast, - StatusEffects.overdrive, - StatusEffects.overclock, - StatusEffects.boss, - StatusEffects.shielded, - ], - damage: [ - StatusEffects.burning, - StatusEffects.freezing, - StatusEffects.wet, - StatusEffects.muddy, - StatusEffects.melting, - StatusEffects.sapped, - StatusEffects.tarred, - StatusEffects.shocked, - StatusEffects.blasted, - StatusEffects.corroded, - StatusEffects.sporeSlowed, - StatusEffects.electrified, - StatusEffects.fast, - ], - clear: function (unit) { - unit.clearStatuses(); - unit.maxHealth = unit.type.health; - }, - paper: function (unit) { - unit.health = 1; - unit.maxHealth = 1; - unit.apply(StatusEffects.disarmed, Number.MAX_VALUE / 2); - }, - heal: function (unit) { - unit.health = unit.maxHealth; - }, - overheal: function (unit) { - unit.maxHealth = unit.health = 1e15; - }, - shield: function (unit) { - unit.shield = 1e15; - } - }; - var effects = (_b = match(mode, modes, null)) !== null && _b !== void 0 ? _b : (0, commands_1.fail)("Invalid mode. Supported modes: ".concat(Object.keys(modes).join(", "))); - if (typeof effects === "function") { - effects(unit); - } - else { - try { - for (var effects_1 = __values(effects), effects_1_1 = effects_1.next(); !effects_1_1.done; effects_1_1 = effects_1.next()) { - var effect = effects_1_1.value; - unit.apply(effect, ticks); - } - } - catch (e_6_1) { e_6 = { error: e_6_1 }; } - finally { - try { - if (effects_1_1 && !effects_1_1.done && (_a = effects_1.return)) _a.call(effects_1); - } - finally { if (e_6) throw e_6.error; } - } - } -} -function handleError(err, sender, outputFail, context) { - if (err instanceof commands_1.CommandError) { - //If the error is a command error, then just outputFail - outputFail(err.data, sender); - } - else if (err === menus_1.Cancel) { - //Menu cancelled, do nothing - return; - } - else { - sender.sendMessage("[scarlet]\u274C An error occurred while executing the command!"); - if (sender.hasPerm("seeErrorMessages")) - sender.sendMessage((0, funcs_1.parseError)(err)); - Log.err(context ? - "Unhandled error in command execution: ".concat(context) - : "Unhandled error in command execution."); - Log.err(err); - if (typeof err == "object" && err != null && "stack" in err) - Log.err(err.stack); - } -} -var sources = [ - Packages.mindustry.gen.UnitEntity, - Packages.mindustry.gen.MechUnit, - Packages.mindustry.gen.LegsUnit, - Packages.mindustry.gen.CrawlUnit, - Packages.mindustry.gen.UnitWaterMove, - Packages.mindustry.gen.BlockUnitUnit, - Packages.mindustry.gen.ElevationMoveUnit, - Packages.mindustry.gen.BuildingTetherPayloadUnit, - Packages.mindustry.gen.TimedKillUnit, - Packages.mindustry.gen.PayloadUnit, - Packages.mindustry.gen.TankUnit, -]; -function getStatuses(unit) { - var e_7, _a; - try { - for (var sources_1 = __values(sources), sources_1_1 = sources_1.next(); !sources_1_1.done; sources_1_1 = sources_1.next()) { - var clazz = sources_1_1.value; - if (unit instanceof clazz) - return ArcReflect.get(clazz, unit, "statuses"); - } - } - catch (e_7_1) { e_7 = { error: e_7_1 }; } - finally { - try { - if (sources_1_1 && !sources_1_1.done && (_a = sources_1.return)) _a.call(sources_1); - } - finally { if (e_7) throw e_7.error; } - } - return new Seq(); -} +"use strict"; +/* +Copyright © BalaM314, 2026. All Rights Reserved. +This file contains many utility functions that need access to any values from other files. +For functions that don't need values from other files, see funcs.ts. +*/ +var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + var desc = Object.getOwnPropertyDescriptor(m, k); + if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { + desc = { enumerable: true, get: function() { return m[k]; } }; + } + Object.defineProperty(o, k2, desc); +}) : (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + o[k2] = m[k]; +})); +var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { + Object.defineProperty(o, "default", { enumerable: true, value: v }); +}) : function(o, v) { + o["default"] = v; +}); +var __importStar = (this && this.__importStar) || (function () { + var ownKeys = function(o) { + ownKeys = Object.getOwnPropertyNames || function (o) { + var ar = []; + for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k; + return ar; + }; + return ownKeys(o); + }; + return function (mod) { + if (mod && mod.__esModule) return mod; + var result = {}; + if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]); + __setModuleDefault(result, mod); + return result; + }; +})(); +var __values = (this && this.__values) || function(o) { + var s = typeof Symbol === "function" && Symbol.iterator, m = s && o[s], i = 0; + if (m) return m.call(o); + if (o && typeof o.length === "number") return { + next: function () { + if (o && i >= o.length) o = void 0; + return { value: o && o[i++], done: !o }; + } + }; + throw new TypeError(s ? "Object is not iterable." : "Symbol.iterator is not defined."); +}; +var __read = (this && this.__read) || function (o, n) { + var m = typeof Symbol === "function" && o[Symbol.iterator]; + if (!m) return o; + var i = m.call(o), r, ar = [], e; + try { + while ((n === void 0 || n-- > 0) && !(r = i.next()).done) ar.push(r.value); + } + catch (error) { e = { error: error }; } + finally { + try { + if (r && !r.done && (m = i["return"])) m.call(i); + } + finally { if (e) throw e.error; } + } + return ar; +}; +var __spreadArray = (this && this.__spreadArray) || function (to, from, pack) { + if (pack || arguments.length === 2) for (var i = 0, l = from.length, ar; i < l; i++) { + if (ar || !(i in from)) { + if (!ar) ar = Array.prototype.slice.call(from, 0, i); + ar[i] = from[i]; + } + } + return to.concat(ar || Array.prototype.slice.call(from)); +}; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.addToTileHistory = exports.foolifyChat = exports.getMap = exports.getUnitType = exports.getItem = void 0; +exports.memoizeChatFilter = memoizeChatFilter; +exports.formatTime = formatTime; +exports.formatTimeShort = formatTimeShort; +exports.formatModeName = formatModeName; +exports.formatTimestampFull = formatTimestampFull; +exports.formatTimestamp = formatTimestamp; +exports.formatTimestampShort = formatTimestampShort; +exports.formatTimeRelative = formatTimeRelative; +exports.getColor = getColor; +exports.nearbyEnemyTile = nearbyEnemyTile; +exports.getTeam = getTeam; +exports.matchFilter = matchFilter; +exports.removeFoosChars = removeFoosChars; +exports.cleanText = cleanText; +exports.isImpersonator = isImpersonator; +exports.logAction = logAction; +exports.parseTimeString = parseTimeString; +exports.serverRestartLoop = serverRestartLoop; +exports.restartNow = restartNow; +exports.isBuildable = isBuildable; +exports.isMapValidForGamemode = isMapValidForGamemode; +exports.getBlock = getBlock; +exports.teleportPlayer = teleportPlayer; +exports.logErrors = logErrors; +exports.definitelyRealMemoryCorruption = definitelyRealMemoryCorruption; +exports.getEnemyTeam = getEnemyTeam; +exports.neutralGameover = neutralGameover; +exports.skipWaves = skipWaves; +exports.logHTrip = logHTrip; +exports.setType = setType; +exports.untilForever = untilForever; +exports.colorNumber = colorNumber; +exports.formatRatekeeper = formatRatekeeper; +exports.getAntiBotInfo = getAntiBotInfo; +exports.outputFail = outputFail; +exports.outputSuccess = outputSuccess; +exports.outputMessage = outputMessage; +exports.outputConsole = outputConsole; +exports.updateBans = updateBans; +exports.processChat = processChat; +exports.getIPRange = getIPRange; +exports.getHash = getHash; +exports.match = match; +exports.fishCommandsRootDirPath = fishCommandsRootDirPath; +exports.applyEffectMode = applyEffectMode; +exports.handleError = handleError; +exports.getStatuses = getStatuses; +var api = __importStar(require("/api")); +var config_1 = require("/config"); +var commands_1 = require("/frameworks/commands"); +var menus_1 = require("/frameworks/menus"); +var funcs_1 = require("/funcs"); +var globals_1 = require("/globals"); +var players_1 = require("/players"); +function memoizeChatFilter(impl) { + var lastCleanedInput = null; + var lastOutput = null; + return function memoized(input) { + var cleanedInput = removeFoosChars(input); + if (cleanedInput === lastCleanedInput) + return lastOutput; + lastCleanedInput = cleanedInput; + return lastOutput = impl(input); + }; +} +function formatTime(time) { + if (globals_1.maxTime - (time + Date.now()) < 20000) + return "forever"; + if (isNaN(time)) + return "N/A"; + var months = Math.floor(time / (30 * 24 * 60 * 60 * 1000)); + var days = Math.floor((time % (30 * 24 * 60 * 60 * 1000)) / (24 * 60 * 60 * 1000)); + var hours = Math.floor((time % (24 * 60 * 60 * 1000)) / (60 * 60 * 1000)); + var minutes = Math.floor((time % (60 * 60 * 1000)) / (60 * 1000)); + var seconds = Math.floor((time % (60 * 1000)) / (1000)); + return [ + months && "".concat(months, " month").concat(months != 1 ? "s" : ""), + days && "".concat(days, " day").concat(days != 1 ? "s" : ""), + hours && "".concat(hours, " hour").concat(hours != 1 ? "s" : ""), + minutes && "".concat(minutes, " minute").concat(minutes != 1 ? "s" : ""), + (seconds || time < 1000) && "".concat(seconds, " second").concat(seconds != 1 ? "s" : ""), + ].filter(Boolean).join(", "); +} +function formatTimeShort(time) { + if (globals_1.maxTime - (time + Date.now()) < 20000) + return "forever"; + if (isNaN(time)) + return "N/A"; + var months = Math.floor(time / (30 * 24 * 60 * 60 * 1000)); + var days = Math.floor((time % (30 * 24 * 60 * 60 * 1000)) / (24 * 60 * 60 * 1000)); + var hours = Math.floor((time % (24 * 60 * 60 * 1000)) / (60 * 60 * 1000)); + var minutes = Math.floor((time % (60 * 60 * 1000)) / (60 * 1000)); + var seconds = Math.floor((time % (60 * 1000)) / (1000)); + return [ + months && "".concat(months, "mo"), + days && "".concat(days, "d"), + hours && "".concat(hours, "h"), + minutes && "".concat(minutes, "m"), + (seconds || time < 1000) && "".concat(seconds, "s"), + ].filter(Boolean).join(" "); +} +//TODO move this data to be right next to Mode +function formatModeName(name) { + return { + "attack": "Attack", + "survival": "Survival", + "hexed": "Hexed", + "pvp": "PVP", + "sandbox": "Sandbox", + "hardcore": "Hardcore", + "testsrv": "Testing Server", + "minigame": "Minigames", + }[name]; +} +function formatTimestampFull(time) { + var date = new Date(time); + return "".concat(date.toDateString(), ", ").concat(date.toTimeString()); +} +function formatTimestamp(time) { + return new Date(time).toLocaleString(); +} +function formatTimestampShort(time) { + var date = new Date(time); + return "".concat(date.getFullYear(), "-").concat(date.getMonth() + 1, "-").concat(date.getDate(), " ").concat(date.getHours(), ":").concat(date.getMinutes()); +} +function formatTimeRelative(time, raw) { + var difference = Math.abs(time - Date.now()); + if (difference < 1000) + return "just now"; + else if (time > Date.now()) + return (raw ? "" : "in ") + formatTime(difference); + else + return formatTime(difference) + (raw ? "" : " ago"); +} +/** Attempts to parse a Color from the input. */ +function getColor(input) { + try { + if (input.includes(',')) { + var formattedColor = input.split(','); + var col = { + r: Number(formattedColor[0]), + g: Number(formattedColor[1]), + b: Number(formattedColor[2]), + a: 255, + }; + return new Color(col.r, col.g, col.b, col.a); + } + else if (input.includes('#')) { + return Color.valueOf(input); + } + else if ((function (input) { return input in Color; })(input)) { + return Color[input]; + } + else { + return null; + } + } + catch (e) { + return null; + } +} +/** Searches for an enemy tile near a unit. */ +function nearbyEnemyTile(unit, dist) { + //because the indexer is buggy + if (dist > 10) + (0, funcs_1.crash)("nearbyEnemyTile(): dist (".concat(dist, ") is too high!")); + var x = Math.floor(unit.x / Vars.tilesize); + var y = Math.floor(unit.y / Vars.tilesize); + for (var i = -dist; i <= dist; i++) { + for (var j = -dist; j <= dist; j++) { + var build = Vars.world.build(x + i, y + j); + if (build && build.team != unit.team && build.team != Team.derelict) + return build; + } + } + return null; +} +/** Attempts to parse a Team from the input. */ +function getTeam(team) { + if (team in Team && Team[team] instanceof Team) + return Team[team]; + else if (Team.baseTeams.find(function (t) { return t.name.includes(team.toLowerCase()); })) + return Team.baseTeams.find(function (t) { return t.name.includes(team.toLowerCase()); }); + else if (!isNaN(Number(team))) + return "\"".concat(team, "\" is not a valid team string. Did you mean \"#").concat(team, "\"?"); + else if (!isNaN(Number(team.slice(1)))) { + var num = Number(team.slice(1)); + if (num <= 255 && num >= 0 && Number.isInteger(num)) + return Team.all[Number(team.slice(1))]; + else + return "Team ".concat(team, " is outside the valid range (integers 0-255)."); + } + return "\"".concat(team, "\" is not a valid team string."); +} +/** Attempts to parse an Item from the input. */ +exports.getItem = (0, funcs_1.searchFixed)(Vars.content.items().toArray(), [ + function (i, s) { return i.name == s; }, + function (i, s) { return i.name == s.toLowerCase(); }, + function (i, s) { return i.name.includes(s.toLowerCase()); }, + function (i, s) { return i.name.includes(s.toLowerCase().replace(" ", "-")); }, + function (i, s) { return i.emoji() == s; }, +]); +/** + * @param wordList "chat" is least strict, followed by "strict", and "name" is most strict. + * @returns a + */ +function matchFilter(input, wordList, aggressive) { + var e_1, _a, e_2, _b; + if (wordList === void 0) { wordList = "chat"; } + if (aggressive === void 0) { aggressive = false; } + var currentBannedWords = [ + wordList == "name" ? config_1.bannedWords.normal.filter(function (w) { return w[0] !== "uwu"; }) : config_1.bannedWords.normal, + (wordList == "strict" || wordList == "name") && config_1.bannedWords.strict, + wordList == "name" && config_1.bannedWords.names, + ].filter(Boolean).flat(); + if (aggressive) + currentBannedWords.push(["hitler", []]); + //Replace substitutions + var variations = [input, cleanText(input, false)]; + if (aggressive) + variations.push(cleanText(input, true)); + try { + for (var currentBannedWords_1 = __values(currentBannedWords), currentBannedWords_1_1 = currentBannedWords_1.next(); !currentBannedWords_1_1.done; currentBannedWords_1_1 = currentBannedWords_1.next()) { + var _c = __read(currentBannedWords_1_1.value, 2), banned = _c[0], whitelist = _c[1]; + var _loop_1 = function (text_1) { + if (banned instanceof RegExp ? banned.test(text_1) : text_1.includes(banned)) { + var modifiedText_1 = text_1; + whitelist.forEach(function (w) { return modifiedText_1 = modifiedText_1.replace(new RegExp(w, "g"), ""); }); //Replace whitelisted words with nothing + if (banned instanceof RegExp ? banned.test(modifiedText_1) : modifiedText_1.includes(banned)) //If the text still matches, fail + return { value: (banned === globals_1.uuidPattern ? "a Mindustry UUID" : + banned === globals_1.ipPattern || banned === globals_1.ipPortPattern ? "an IP address" : + //parsing regex with regex, massive hack + banned instanceof RegExp ? banned.source.replace(/\\b|\(\?|||>", "Name contains >|||> which is reserved for the server owner"], + "\uE817", "\uE82C", "\uE88E", "\uE813", + [/^[<\uE825].{1,3}[>\uE83A]/, "Name contains a prefix such as which is used for role prefixes"], + [function (replacedText) { return !isAdmin && config_1.adminNames.includes(replacedText.replace(/ /g, "")); }, "One of our admins uses this name"] + ]); + try { + for (var filters_1 = __values(filters), filters_1_1 = filters_1.next(); !filters_1_1.done; filters_1_1 = filters_1.next()) { + var _b = __read(filters_1_1.value, 2), check = _b[0], message = _b[1]; + if (check(replacedText)) + return message; + if (check(antiEvasionText)) + return message; + } + } + catch (e_3_1) { e_3 = { error: e_3_1 }; } + finally { + try { + if (filters_1_1 && !filters_1_1.done && (_a = filters_1.return)) _a.call(filters_1); + } + finally { if (e_3) throw e_3.error; } + } + return false; +} +function logAction(action, by, to, reason, duration) { + if (by === undefined) { //overload 1 + api.sendModerationMessage("".concat(action, "\n**Server:** ").concat(config_1.Gamemode.name())); + return; + } + if (to === undefined) { //overload 2 + api.sendModerationMessage("".concat((0, funcs_1.escapeTextDiscord)(Strings.stripColors(by.name)), " ").concat(action, "\n**Server:** ").concat(config_1.Gamemode.name())); + return; + } + if (to) { //overload 3 + var name = void 0, uuid = void 0, ip = void 0; + var actor = typeof by === "string" ? by : (0, funcs_1.escapeTextDiscord)(Strings.stripColors(by.name)); + if (to instanceof players_1.FishPlayer) { + name = (0, funcs_1.escapeTextDiscord)(to.name); + uuid = to.uuid; + ip = to.ip(); + } + else if (typeof to == "string") { + if (globals_1.uuidPattern.test(to)) { + name = "[".concat(to, "]"); + uuid = to; + ip = "[unknown]"; + } + else { + name = to; + uuid = "[unknown]"; + ip = "[unknown]"; + } + } + else { + name = (0, funcs_1.escapeTextDiscord)(to.lastName); + uuid = to.id; + ip = to.lastIP; + } + api.sendModerationMessage("".concat(actor, " ").concat(action, " ").concat(name, " ").concat(duration ? "for ".concat(formatTime(duration), " ") : "").concat(reason ? "with reason ".concat((0, funcs_1.escapeTextDiscord)(reason)) : "", "\n**Server:** ").concat(config_1.Gamemode.name(), "\n**uuid:** `").concat(uuid, "`\n**ip**: `").concat(ip, "`")); + return; + } +} +/** @returns the number of milliseconds. */ +function parseTimeString(str) { + var e_4, _a; + var formats = [ + [/(\d+)s/, 1], + [/(\d+)m/, 60], + [/(\d+)h/, 3600], + [/(\d+)d/, 86400], + [/(\d+)w/, 604800] + ].map(function (_a) { + var _b = __read(_a, 2), regex = _b[0], mult = _b[1]; + return [Pattern.compile(regex.source), mult]; + }); + if (str == "forever") + return (globals_1.maxTime - Date.now() - 10000); + try { + for (var formats_1 = __values(formats), formats_1_1 = formats_1.next(); !formats_1_1.done; formats_1_1 = formats_1.next()) { + var _b = __read(formats_1_1.value, 2), pattern = _b[0], mult = _b[1]; + //rhino regex doesn't work + var matcher = pattern.matcher(str); + if (matcher.matches()) { + var num = Number(matcher.group(1)); + if (!isNaN(num)) + return (num * mult) * 1000; + } + } + } + catch (e_4_1) { e_4 = { error: e_4_1 }; } + finally { + try { + if (formats_1_1 && !formats_1_1.done && (_a = formats_1.return)) _a.call(formats_1); + } + finally { if (e_4) throw e_4.error; } + } + return null; +} +/** + * Triggers the restart countdown. Execution always returns from this function. + * @param [fake=false] if set, server will not actually restart. + */ +function serverRestartLoop(sec, fake) { + if (fake === void 0) { fake = false; } + if (sec > 0) { + if (sec < 15 || sec % 5 == 0) + Call.sendMessage("[scarlet]Server restarting in: ".concat(sec)); + globals_1.fishState.restartLoopTask = Timer.schedule(function () { return serverRestartLoop(sec - 1); }, 1); + } + else if (!fake) { + restartNow(); + } +} +/** + * Actually restarts. Kicks all players. Execution always returns from this function. + * @param [removeSave=false] If set, save will be deleted instead of saved. Used to start a new game after the restart. + */ +function restartNow(removeSave) { + if (removeSave === void 0) { removeSave = false; } + Log.info("Restarting..."); + Vars.netServer.kickAll(Packets.KickReason.serverRestarting); + Vars.net.closeServer(); + Vars.state.set(GameState.State.menu); + var file = Vars.saveDirectory.child('1' + '.' + Vars.saveExtension); + if (removeSave) { + Core.app.post(function () { + file.delete(); + Core.app.exit(); + }); + } + else { + Core.app.post(function () { + SaveIO.save(file); + Core.app.exit(); + }); + } +} +function isBuildable(block) { + return block == Blocks.powerVoid || (block.buildType != Blocks.air.buildType && !(block instanceof ConstructBlock)); +} +exports.getUnitType = (0, funcs_1.searchFixed)(function () { return Vars.content.units().select(function (u) { return !(u instanceof MissileUnitType || u.internal); }).toArray(); }, [ + function (u, q) { return u.name == q; }, + function (u, q) { return u.name.includes(q.toLowerCase()); }, +]); +/** The vanilla validation code doesn't work on servers */ +function isMapValidForGamemode(map) { + if (map.custom) + return true; //we assume that all custom maps are appropriate for the selected gamemode + var pvpMaps = ["Veins", "Glacier", "Passage"]; //Maps.pvpMaps + switch (Vars.state.rules.mode().name()) { + case "sandbox": + case "editor": return true; //sandbox can be played on any map + case "attack": + case "pvp": return pvpMaps.includes(map.name()); //technically the pvp maps are valid attack maps, since they have an (undefended) enemy core + case "survival": return !pvpMaps.includes(map.name()); + default: return false; //unreachable + } +} +exports.getMap = (0, funcs_1.searchFixed)(function () { return Vars.maps.all().select(isMapValidForGamemode).toArray(); }, [ + function (m, name) { return m.name().replace(/ /g, "_") === name; }, //exact match with spaces replaced + function (m, name) { return m.name().replace(/ /g, "_").toLowerCase() === name.toLowerCase(); }, //exact match with spaces replaced ignoring case + function (m, name) { return m.plainName().replace(/ /g, "_").toLowerCase() === name.toLowerCase(); }, //exact match with spaces replaced ignoring case and colors + function (m, name) { return m.plainName().toLowerCase().includes(name.toLowerCase()); }, //partial match ignoring case and colors + function (m, name) { return m.plainName().replace(/ /g, "_").toLowerCase().includes(name.toLowerCase()); }, //partial match with spaces replaced ignoring case and colors + function (m, name) { return m.plainName().replace(/ /g, "").toLowerCase().includes(name.toLowerCase()); }, //partial match with spaces removed ignoring case and colors + function (m, name) { return m.plainName().replace(/[^a-zA-Z]/gi, "").toLowerCase().includes(name.toLowerCase()); }, +], "recomputeOptions"); +//static cache +var buildableBlocks = null; +function getBlock(block, filter) { + buildableBlocks !== null && buildableBlocks !== void 0 ? buildableBlocks : (buildableBlocks = Vars.content.blocks().select(isBuildable)); + var check = { + buildable: function (b) { return isBuildable(b); }, + air: function (b) { return b == Blocks.air || isBuildable(b); }, + all: function (b) { return true; } + }[filter]; + var out; + if (block in Blocks && Blocks[block] instanceof Block && check(Blocks[block])) + return Blocks[block]; + else if ((out = Vars.content.blocks().find(function (t) { return t.name.includes(block.toLowerCase()) && check(t); }))) + return out; + else if ((out = Vars.content.blocks().find(function (t) { return t.name.replace(/-/g, "").includes(block.toLowerCase().replace(/ /g, "")) && check(t); }))) + return out; + else if (block.includes("airblast")) + return Blocks.blastDrill; + return "\"".concat(block, "\" is not a valid block."); +} +function teleportPlayer(player, to) { + Timer.schedule(function () { + var p = player.unit(); + var t = to.unit(); + if (p && t) { + p.set(t.x, t.y); + Call.setPosition(player.con, t.x, t.y); + Call.setCameraPosition(player.con, t.x, t.y); + } + }, 0, 0.016, 10); +} +function logErrors(message, func) { + return function () { + var args = []; + for (var _i = 0; _i < arguments.length; _i++) { + args[_i] = arguments[_i]; + } + try { + return func.apply(void 0, __spreadArray([], __read(args), false)); + } + catch (err) { + Log.err(message); + Log.err((0, funcs_1.parseError)(err)); + } + }; +} +function definitelyRealMemoryCorruption() { + Log.info("Triggering a prank: this will cause players to see two error messages claiming to be from a memory corruption, and cause a flickering amount of fissile matter and dormant cysts to be put in the core."); + players_1.FishPlayer.messageStaff("[gray]<[cyan]staff[gray]> [white]Activating memory corruption prank! (please don't ruin it by telling players what is happening, pretend you dont know)"); + api.sendModerationMessage("Activated memory corruption prank on server ".concat(Vars.state.rules.mode().name())); + var t1f = false; + var t2f = false; + globals_1.fishState.corruption_t1 = Timer.schedule(function () { + var _a; + t1f = !t1f; + (_a = Vars.state.rules.defaultTeam.items()) === null || _a === void 0 ? void 0 : _a.set(Items.dormantCyst, t1f ? 69 : 420); + }, 0, 0.4, 600); + globals_1.fishState.corruption_t2 = Timer.schedule(function () { + var _a; + t2f = !t2f; + (_a = Vars.state.rules.defaultTeam.items()) === null || _a === void 0 ? void 0 : _a.set(Items.fissileMatter, t2f ? 999 : 123); + }, 0, 1.5, 200); + var hexString = Math.floor(Math.random() * 0xFFFFFFFF).toString(16).padStart(8, "0"); + Call.sendMessage("[scarlet]Error: internal server error."); + Call.sendMessage("[scarlet]Error: memory corruption: mindustry.world.modules.ItemModule@".concat(hexString)); + globals_1.FishEvents.fire("memoryCorruption", []); +} +function getEnemyTeam() { + if (config_1.Gamemode.pvp()) + return Team.derelict; + else + return Vars.state.rules.waveTeam; +} +function neutralGameover() { + players_1.FishPlayer.ignoreGameover(function () { + Events.fire(new EventType.GameOverEvent(getEnemyTeam())); + }); +} +/** Please validate requestedWaves to ensure it is not huge */ +function skipWaves(requestedWaves, runIntermediateWaves) { + //Check if the current map has opted out of multi-wave skipping + if (Vars.state.rules.tags.get(config_1.noMultiVnwTag) == "true") { + requestedWaves = Math.min(requestedWaves, 1); + } + var winWave = Vars.state.rules.winWave; + if (winWave <= 0) + winWave = Infinity; + var wavesToSkip = Math.min(requestedWaves, winWave - Vars.state.wave); + if (runIntermediateWaves) { + for (var i = 0; i < wavesToSkip; i++) { + Vars.logic.skipWave(); + } + } + else { + Vars.state.wave += (wavesToSkip - 1); + Vars.logic.skipWave(); + } +} +function logHTrip(player, name, message) { + Log.warn("&yPlayer &b\"".concat(player.cleanedName, "\"&y (&b").concat(player.uuid, "&y/&b").concat(player.ip(), "&y) tripped &c").concat(name, "&y") + (message ? ": ".concat(message) : "")); + players_1.FishPlayer.messageStaff("[yellow]Player [blue]\"".concat(player.cleanedName, "\"[] tripped [cyan]").concat(name, "[]") + (message ? ": ".concat(message) : "")); + api.sendModerationMessage("Player `".concat(player.cleanedName, "` (`").concat(player.uuid, "`/`").concat(player.ip(), "`) tripped **").concat(name, "**").concat(message ? ": ".concat(message) : "", "\n**Server:** ").concat(config_1.Gamemode.name())); +} +function setType(input) { + //does not do any checking +} +function untilForever() { + return (globals_1.maxTime - Date.now() - 10000); +} +function colorNumber(number, getColor, side) { + if (side === void 0) { side = "client"; } + return getColor(number) + number.toString() + (side == "client" ? "[]" : "&fr"); +} +function formatRatekeeper(x) { + if (x.lastTime <= 1) + return "0"; + return "".concat(x.occurences, " / ").concat(formatTimeRelative(x.lastTime, true)); +} +function getAntiBotInfo(side) { + var color = side == "client" ? "[acid]" : "&ly"; + var True = side == "client" ? "[red]true[]" : "&lrtrue"; + var False = side == "client" ? "[green]false[]" : "&gfalse"; + return ("".concat(color, "Flag count: ").concat(formatRatekeeper(players_1.FishPlayer.autoflagRate), "\n").concat(color, "Autobanning flagged players: ").concat(players_1.FishPlayer.shouldWhackFlaggedPlayers() ? True : False, "\n").concat(color, "Kicking new players: ").concat(players_1.FishPlayer.shouldKickNewPlayers() ? True : False, "\n").concat(color, "Recent connect packets: ").concat(formatRatekeeper(players_1.FishPlayer.connectRate), "\n").concat(color, "Reason: ").concat(players_1.FishPlayer.lastAntibotReason)); +} +var failPrefix = "[scarlet]\u26A0 [yellow]"; +var successPrefix = "[#48e076]\uE800 "; +function outputFail(message, sender, ratelimit) { + var msg = failPrefix + (typeof message == "function" && "__partialFormatString" in message ? message("[yellow]") : message); + if (ratelimit) + sender.sendMessage(msg, ratelimit); + else + sender.sendMessage(msg); +} +function outputSuccess(message, sender) { + sender.sendMessage(successPrefix + (typeof message == "function" && "__partialFormatString" in message ? message("[#48e076]") : message)); +} +function outputMessage(message, sender) { + sender.sendMessage(((typeof message == "function" && "__partialFormatString" in message ? message(null) : message) + "").replace(/\t/g, " ".repeat(4))); +} +function outputConsole(message, channel) { + if (channel === void 0) { channel = Log.info; } + channel(typeof message == "function" && "__partialFormatString" in message ? message("") : message); +} +function updateBans(message) { + Groups.player.each(function (player) { + if (Vars.netServer.admins.isIDBanned(player.uuid())) { + player.con.kick(Packets.KickReason.banned); + if (message) + Call.sendMessage(message(player)); + } + }); +} +function processChat(player, message, effects) { + if (effects === void 0) { effects = false; } + var fishPlayer = players_1.FishPlayer.get(player); + var highlight = fishPlayer.highlight; + var filterTripText; + var suspicious = fishPlayer.suspicionLevel() == 3; + if ((!fishPlayer.hasPerm("bypassChatFilter") || fishPlayer.chatStrictness == "strict") + && (filterTripText = matchFilter(message, fishPlayer.chatStrictness, suspicious))) { + if (effects) { + if (suspicious && removeFoosChars(message).split(" ") + .map(function (w) { return w.replace(/[-_.^*,]/g, ""); }) + .some(function (w) { return config_1.bannedWords.autoWhack.includes(w); })) { + if (!fishPlayer.muted) { + logHTrip(fishPlayer, "bad words in chat", "message: `".concat(message, "`")); + fishPlayer.muted = true; + void fishPlayer.stop("automod", globals_1.maxTime, "Automatic stop due to suspicious activity", false); + } + } + Log.info("Censored message from player ".concat(player.name, ": \"").concat((0, funcs_1.escapeStringColorsServer)(message), "\"; contained \"").concat(filterTripText, "\"")); + players_1.FishPlayer.messageStaff("[yellow]Censored message from player ".concat(fishPlayer.cleanedName, ": \"").concat(message, "\" contained \"").concat(filterTripText, "\"")); + } + message = config_1.text.chatFilterReplacement.message(); + highlight !== null && highlight !== void 0 ? highlight : (highlight = config_1.text.chatFilterReplacement.highlight()); + } + if (message.startsWith("./")) + message = message.replace("./", "/"); + if (!fishPlayer.hasPerm("chat")) { + if (effects) { + players_1.FishPlayer.messageMuted(player.name, message); + Log.info("".concat(player.name, ": ").concat(message)); + } + return null; + } + return (highlight !== null && highlight !== void 0 ? highlight : "") + message; +} +var replacements = [ + //Serpulo units + ["dagger", "mace", "fortress", "scepter", "reign", "nova", "pulsar", "quasar", "vela", "corvus", "crawler", "atrax", "spiroct", "arkyid", "toxopid", "flare", "horizon", "zenith", "antumbra", "eclipse", "mono", "poly", "mega", "quad", "oct", "risso", "minke", "bryde", "sei", "omura", "retusa", "oxynoe", "cyerce", "aegires", "navanax", "fort", "toxo", "flarogus"], + //Erekir units + ["stell", "locus", "precept", "vanquish", "conquer", "merui", "cleroi", "anthicus", "tecta", "collaris", "elude", "avert", "obviate", "quell", "disrupt", "vanq", "crab", "anthi", "larry", "obvi"], + //Items, full form + ["copper", "lead", "metaglass", "graphite", "sand", "coal", "titanium", "thorium", "scrap", "silicon", "plastanium", "phase fabric", "surge alloy", "spore pod", "blast compound", "pyratite", "beryllium", "tungsten", "oxide", "carbide"], + //Items, short form + ["coppa", "meta", "graph", "tita", "titan", "thor", "scrap", "sili", "plast", "phase", "surge", "spore", "blast", "pyra", "beryl", "tung", "oxide", "carb"], + //Liquids + ["water", "slag", "oil", "cryo", "cryofluid"], + //Liquids/gases (erekir) + ["hydrogen", "ozone", "nitrogen", "cyanogen", "cyan", "nitro", "hydro", "arky", "arkycite", "neoplasm"], + //Gamemodes + ["attack", "sandbox", "pvp", "hexed", "survival"], + //teams + ["crux", "sharded", "malis", "neoplastic"], + //maps + ["rampant", "harbor war", "cave canal", "acheron", "wolframfestung", "avast", "fallen omura", "assault"], + //aquatic animals + ["fish", "shark", "whale", "dolphin", "salmon", "tuna", "squid", "jellyfish", "turtle"], + //antonym adjectives + ["fast", "slow"], ["big", "little"], ["hot", "cold"], ["hard", "easy", "difficult", "ez"], ["hello", "bye"], +].map(function (set) { return [set, new RegExp("\\b(?:".concat(set.join("|"), ")(e?s?(?:i?gone)?)\\b"), 'g')]; }); +var foolCounter = 0; +exports.foolifyChat = memoizeChatFilter(function foolifyChat(message) { + var e_5, _a; + var cleanedMessage = removeFoosChars(message); + setShuffle: { + if (foolCounter < 8) { + //Skip the next 5 messages no matter what + foolCounter++; + break setShuffle; + } + var replacedMessage = cleanedMessage; + var _loop_2 = function (set, regex) { + replacedMessage = replacedMessage.replace(regex, function (_, plural) { return (0, funcs_1.random)(set) + plural; }); + }; + try { + for (var replacements_1 = __values(replacements), replacements_1_1 = replacements_1.next(); !replacements_1_1.done; replacements_1_1 = replacements_1.next()) { + var _b = __read(replacements_1_1.value, 2), set = _b[0], regex = _b[1]; + _loop_2(set, regex); + } + } + catch (e_5_1) { e_5 = { error: e_5_1 }; } + finally { + try { + if (replacements_1_1 && !replacements_1_1.done && (_a = replacements_1.return)) _a.call(replacements_1); + } + finally { if (e_5) throw e_5.error; } + } + if (replacedMessage !== cleanedMessage) { + if (foolCounter < 11) { + //Skip the next 2 messages that would get altered + foolCounter++; + break setShuffle; + } + foolCounter = 0; + return replacedMessage; + } + else { + break setShuffle; + } + } + if (Math.random() < 0.01) { + return cleanedMessage.split("").reverse().join(""); + // eslint-disable-next-line no-dupe-else-if + } + else if (Math.random() < 0.01) { + return "[scarlet]I really hope everyone is having a fun time :} <3"; + } + else if (Math.random() < 0.005) { + return "[cyan]AMOGUS"; + } + else { + return message; + } +}); +exports.addToTileHistory = logErrors("Error while saving a tilelog entry", function (e) { + var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q, _r, _s, _t, _u, _v, _w, _x, _y, _z, _0, _1, _2, _3; + // eslint-disable-next-line prefer-const + var tile, uuid, action, type, time = Date.now(); + if (e instanceof EventType.BlockBuildBeginEvent) { + tile = e.tile; + uuid = (_e = (_c = (_b = (_a = e.unit) === null || _a === void 0 ? void 0 : _a.player) === null || _b === void 0 ? void 0 : _b.uuid()) !== null && _c !== void 0 ? _c : (_d = e.unit) === null || _d === void 0 ? void 0 : _d.type.name) !== null && _e !== void 0 ? _e : "unknown"; + if (e.breaking) { + action = "broke"; + type = (e.tile.build instanceof ConstructBlock.ConstructBuild) ? e.tile.build.previous.name : "unknown"; + if (((_g = (_f = e.unit) === null || _f === void 0 ? void 0 : _f.player) === null || _g === void 0 ? void 0 : _g.uuid()) && ((_h = e.tile.build) === null || _h === void 0 ? void 0 : _h.team) != Team.derelict) { + var fishP = players_1.FishPlayer.get(e.unit.player); + //TODO move this code + fishP.tstats.blocksBroken++; + fishP.tstats.blockInteractionsThisMap++; + fishP.updateStats(function (stats) { return stats.blocksBroken++; }); + } + } + else { + action = "built"; + type = (e.tile.build instanceof ConstructBlock.ConstructBuild) ? e.tile.build.current.name : "unknown"; + if ((_k = (_j = e.unit) === null || _j === void 0 ? void 0 : _j.player) === null || _k === void 0 ? void 0 : _k.uuid()) { + var fishP = players_1.FishPlayer.get(e.unit.player); + //TODO move this code + fishP.updateStats(function (stats) { return stats.blocksPlaced++; }); + fishP.tstats.blockInteractionsThisMap++; + } + } + } + else if (e instanceof EventType.ConfigEvent) { + tile = e.tile.tile; + uuid = (_m = (_l = e.player) === null || _l === void 0 ? void 0 : _l.uuid()) !== null && _m !== void 0 ? _m : "unknown"; + if (uuid != "unknown") { + var fishP = players_1.FishPlayer.getById(uuid); + if (fishP) + fishP.tstats.blockInteractionsThisMap++; + } + action = "configured"; + type = e.tile.block.name; + } + else if (e instanceof EventType.BuildRotateEvent) { + tile = e.build.tile; + uuid = (_s = (_q = (_p = (_o = e.unit) === null || _o === void 0 ? void 0 : _o.player) === null || _p === void 0 ? void 0 : _p.uuid()) !== null && _q !== void 0 ? _q : (_r = e.unit) === null || _r === void 0 ? void 0 : _r.type.name) !== null && _s !== void 0 ? _s : "unknown"; + if (uuid != "unknown") { + var fishP = players_1.FishPlayer.getById(uuid); + if (fishP) + fishP.tstats.blockInteractionsThisMap++; + } + action = "rotated"; + type = e.build.block.name; + } + else if (e instanceof EventType.UnitDestroyEvent) { + tile = e.unit.tileOn(); + if (!tile) + return; + if (!e.unit.type.playerControllable) + return; + uuid = e.unit.isPlayer() ? e.unit.getPlayer().uuid() : (_t = e.unit.lastCommanded) !== null && _t !== void 0 ? _t : "unknown"; + action = "killed"; + type = e.unit.type.name; + } + else if (e instanceof EventType.BlockDestroyEvent) { + if (config_1.Gamemode.attack() && ((_u = e.tile.build) === null || _u === void 0 ? void 0 : _u.team) != Vars.state.rules.defaultTeam) + return; //Don't log destruction of enemy blocks + tile = e.tile; + uuid = "[[something]"; + action = "killed"; + type = (_w = (_v = e.tile.block()) === null || _v === void 0 ? void 0 : _v.name) !== null && _w !== void 0 ? _w : "air"; + } + else if (e instanceof EventType.PayloadDropEvent) { + action = "pay-dropped"; + var controller = e.carrier.controller(); + uuid = (_z = (_y = (_x = e.carrier.player) === null || _x === void 0 ? void 0 : _x.uuid()) !== null && _y !== void 0 ? _y : (controller instanceof LogicAI && controller.controller ? + "".concat(e.carrier.type.name, " controlled by ").concat(controller.controller.block.name, " at ").concat(controller.controller.tileX(), ",").concat(controller.controller.tileY(), " last accessed by ").concat(e.carrier.getControllerName()) + : null)) !== null && _z !== void 0 ? _z : e.carrier.type.name; + if (e.build) { + tile = e.build.tile; + type = e.build.block.name; + } + else if (e.unit) { + tile = e.unit.tileOn(); + if (!tile) + return; + type = e.unit.type.name; + } + else + return; + } + else if (e instanceof EventType.PickupEvent) { + action = "picked up"; + if (e.carrier.isPlayer()) + return; //This event would have been handled by actionfilter + var controller = e.carrier.controller(); + if (!(controller instanceof LogicAI && controller.controller != null)) + return; + uuid = "".concat(e.carrier.type.name, " controlled by ").concat(controller.controller.block.name, " at ").concat(controller.controller.tileX(), ",").concat(controller.controller.tileY(), " last accessed by ").concat(e.carrier.getControllerName()); + if (e.build) { + tile = e.build.tile; + type = e.build.block.name; + } + else if (e.unit) { + tile = e.unit.tileOn(); + if (!tile) + return; + type = e.unit.type.name; + } + else + return; + } + else if (e instanceof EventType.UnitControlEvent) { + if (e.unit instanceof Packages.mindustry.gen.BlockUnitUnit) { + action = "controlled"; + tile = (_0 = e.unit) === null || _0 === void 0 ? void 0 : _0.tile().tile; + if (!tile) + return; + type = (_2 = (_1 = tile.block()) === null || _1 === void 0 ? void 0 : _1.name) !== null && _2 !== void 0 ? _2 : "air"; + uuid = e.player.uuid(); + } + else + return; + } + else if (e instanceof Object && "pos" in e && "uuid" in e && "action" in e && "type" in e) { + var pos = void 0; + (pos = e.pos, uuid = e.uuid, action = e.action, type = e.type); + tile = (_3 = Vars.world.tile(pos.split(",")[0], pos.split(",")[1])) !== null && _3 !== void 0 ? _3 : (0, funcs_1.crash)("Cannot log ".concat(action, " at ").concat(pos, ": Nonexistent tile")); + } + else + return; + if (tile == null) + return; + [tile, uuid, action, type, time]; + tile.getLinkedTiles(function (t) { + var pos = "".concat(t.x, ",").concat(t.y); + var existingData = globals_1.tileHistory[pos] ? funcs_1.StringIO.read(globals_1.tileHistory[pos], function (str) { return str.readArray(function (d) { return ({ + action: d.readString(2), + uuid: d.readString(3), + time: d.readNumber(16), + type: d.readString(2), + }); }, 1); }) : []; + existingData.push({ + action: action, + uuid: uuid, + time: time, + type: type + }); + existingData = existingData.slice(-9); + //Write + globals_1.tileHistory[t.x + ',' + t.y] = funcs_1.StringIO.write(existingData, function (str, data) { return str.writeArray(data, function (el) { + str.writeString(el.action, 2); + str.writeString(el.uuid, 3); + str.writeNumber(el.time, 16); + str.writeString(el.type, 2); + }, 1); }); + }); +}); +function getIPRange(input, error) { + if (globals_1.ipRangeCIDRPattern.test(input)) { + var _a = __read(input.split("/"), 2), ip = _a[0], maskLength = _a[1]; + switch (maskLength) { + case "24": + return ip.split(".").slice(0, 3).join(".") + "."; + case "16": + return ip.split(".").slice(0, 2).join(".") + "."; + default: + error === null || error === void 0 ? void 0 : error("Mindustry does not currently support netmasks other than /16 and /24"); + return null; + } + } + else if (globals_1.ipRangeWildcardPattern.test(input)) { + //1.2.3.* + //1.2.* + var _b = __read(input.split("."), 4), a = _b[0], b = _b[1], c = _b[2], d = _b[3]; + if (c !== "*") + return "".concat(a, ".").concat(b, ".").concat(c, "."); + return "".concat(a, ".").concat(b, "."); + } + else + return null; +} +//this brings me physical pain +function getHash(file, algorithm) { + if (algorithm === void 0) { algorithm = "SHA-1"; } + try { + var header = "blob ".concat(file.length(), "\0"); + var fileSHAHeader = Packages.java.nio.charset.StandardCharsets.UTF_8.encode(header); + var contents = file.readBytes(); + var buffer = Packages.java.nio.ByteBuffer.allocate(fileSHAHeader.remaining() + contents.length); + buffer.put(fileSHAHeader); + buffer.put(contents); + buffer.flip(); + var digest = Packages.java.security.MessageDigest.getInstance(algorithm); + digest.update(buffer); + return digest.digest().map(function (byte) { + return (byte & 0xFF).toString(16).padStart(2, "0"); + }).join(""); + } + catch (e) { + Log.err("Cannot generate ".concat(algorithm, ", ").concat(String(e))); + return undefined; + } +} +function match(value, clauses, defaultValue) { + return Object.prototype.hasOwnProperty.call(clauses, value) ? clauses[value] : defaultValue; +} +/** @throws CommandError */ +function fishCommandsRootDirPath() { + var commandsDir = Vars.modDirectory.child("fish-commands"); + if (!commandsDir.exists()) + (0, commands_1.fail)("Fish commands directory at path ".concat(commandsDir.absolutePath(), " does not exist!")); + var fishCommandsRootDirPath = Paths.get(commandsDir.file().path); + if (Packages.java.nio.file.Files.isSymbolicLink(fishCommandsRootDirPath)) { + //fish-commands is linked to the build directory of somewhere else + //resolve and get the parent directory of the build directory + fishCommandsRootDirPath = fishCommandsRootDirPath.toRealPath().getParent(); + } + return fishCommandsRootDirPath; +} +/** Fails if "mode" is invalid. */ +function applyEffectMode(mode, unit, ticks) { + var e_6, _a; + var _b; + var modes = { + fast: [StatusEffects.fast], + fast2: [StatusEffects.fast, StatusEffects.overdrive, StatusEffects.overclock], + boss: [StatusEffects.boss], + health: [StatusEffects.boss, StatusEffects.shielded], + slow: [StatusEffects.slow], + slow2: [ + StatusEffects.slow, + StatusEffects.freezing, + StatusEffects.wet, + StatusEffects.muddy, + StatusEffects.sapped, + StatusEffects.sporeSlowed, + StatusEffects.electrified, + StatusEffects.tarred, + ], + freeze: [StatusEffects.unmoving], + disarm: [StatusEffects.disarmed], + invincible: [StatusEffects.invincible], + boost: [ + StatusEffects.fast, + StatusEffects.overdrive, + StatusEffects.overclock, + StatusEffects.boss, + StatusEffects.shielded, + ], + damage: [ + StatusEffects.burning, + StatusEffects.freezing, + StatusEffects.wet, + StatusEffects.muddy, + StatusEffects.melting, + StatusEffects.sapped, + StatusEffects.tarred, + StatusEffects.shocked, + StatusEffects.blasted, + StatusEffects.corroded, + StatusEffects.sporeSlowed, + StatusEffects.electrified, + StatusEffects.fast, + ], + clear: function (unit) { + unit.clearStatuses(); + unit.maxHealth = unit.type.health; + }, + paper: function (unit) { + unit.health = 1; + unit.maxHealth = 1; + unit.apply(StatusEffects.disarmed, Number.MAX_VALUE / 2); + }, + heal: function (unit) { + unit.health = unit.maxHealth; + }, + overheal: function (unit) { + unit.maxHealth = unit.health = 1e15; + }, + shield: function (unit) { + unit.shield = 1e15; + } + }; + var effects = (_b = match(mode, modes, null)) !== null && _b !== void 0 ? _b : (0, commands_1.fail)("Invalid mode. Supported modes: ".concat(Object.keys(modes).join(", "))); + if (typeof effects === "function") { + effects(unit); + } + else { + try { + for (var effects_1 = __values(effects), effects_1_1 = effects_1.next(); !effects_1_1.done; effects_1_1 = effects_1.next()) { + var effect = effects_1_1.value; + unit.apply(effect, ticks); + } + } + catch (e_6_1) { e_6 = { error: e_6_1 }; } + finally { + try { + if (effects_1_1 && !effects_1_1.done && (_a = effects_1.return)) _a.call(effects_1); + } + finally { if (e_6) throw e_6.error; } + } + } +} +function handleError(err, sender, outputFail, context) { + if (err instanceof commands_1.CommandError) { + //If the error is a command error, then just outputFail + outputFail(err.data, sender); + } + else if (err === menus_1.Cancel) { + //Menu cancelled, do nothing + return; + } + else { + sender.sendMessage("[scarlet]\u274C An error occurred while executing the command!"); + if (sender.hasPerm("seeErrorMessages")) + sender.sendMessage((0, funcs_1.parseError)(err)); + Log.err(context ? + "Unhandled error in command execution: ".concat(context) + : "Unhandled error in command execution."); + Log.err(err); + if (typeof err == "object" && err != null && "stack" in err) + Log.err(err.stack); + } +} +var sources = [ + Packages.mindustry.gen.UnitEntity, + Packages.mindustry.gen.MechUnit, + Packages.mindustry.gen.LegsUnit, + Packages.mindustry.gen.CrawlUnit, + Packages.mindustry.gen.UnitWaterMove, + Packages.mindustry.gen.BlockUnitUnit, + Packages.mindustry.gen.ElevationMoveUnit, + Packages.mindustry.gen.BuildingTetherPayloadUnit, + Packages.mindustry.gen.TimedKillUnit, + Packages.mindustry.gen.PayloadUnit, + Packages.mindustry.gen.TankUnit, +]; +function getStatuses(unit) { + var e_7, _a; + try { + for (var sources_1 = __values(sources), sources_1_1 = sources_1.next(); !sources_1_1.done; sources_1_1 = sources_1.next()) { + var clazz = sources_1_1.value; + if (unit instanceof clazz) + return ArcReflect.get(clazz, unit, "statuses"); + } + } + catch (e_7_1) { e_7 = { error: e_7_1 }; } + finally { + try { + if (sources_1_1 && !sources_1_1.done && (_a = sources_1.return)) _a.call(sources_1); + } + finally { if (e_7) throw e_7.error; } + } + return new Seq(); +} diff --git a/build/scripts/votes.js b/build/scripts/votes.js index 7c291217..3765de2f 100644 --- a/build/scripts/votes.js +++ b/build/scripts/votes.js @@ -1,218 +1,218 @@ -"use strict"; -/* -Copyright © BalaM314, 2026. All Rights Reserved. -This file contains the voting system. -Some contributions: @author Jurorno9 -*/ -var __extends = (this && this.__extends) || (function () { - var extendStatics = function (d, b) { - extendStatics = Object.setPrototypeOf || - ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || - function (d, b) { for (var p in b) if (Object.prototype.hasOwnProperty.call(b, p)) d[p] = b[p]; }; - return extendStatics(d, b); - }; - return function (d, b) { - if (typeof b !== "function" && b !== null) - throw new TypeError("Class extends value " + String(b) + " is not a constructor or null"); - extendStatics(d, b); - function __() { this.constructor = d; } - d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); - }; -})(); -var __values = (this && this.__values) || function(o) { - var s = typeof Symbol === "function" && Symbol.iterator, m = s && o[s], i = 0; - if (m) return m.call(o); - if (o && typeof o.length === "number") return { - next: function () { - if (o && i >= o.length) o = void 0; - return { value: o && o[i++], done: !o }; - } - }; - throw new TypeError(s ? "Object is not iterable." : "Symbol.iterator is not defined."); -}; -var __read = (this && this.__read) || function (o, n) { - var m = typeof Symbol === "function" && o[Symbol.iterator]; - if (!m) return o; - var i = m.call(o), r, ar = [], e; - try { - while ((n === void 0 || n-- > 0) && !(r = i.next()).done) ar.push(r.value); - } - catch (error) { e = { error: error }; } - finally { - try { - if (r && !r.done && (m = i["return"])) m.call(i); - } - finally { if (e) throw e.error; } - } - return ar; -}; -var __spreadArray = (this && this.__spreadArray) || function (to, from, pack) { - if (pack || arguments.length === 2) for (var i = 0, l = from.length, ar; i < l; i++) { - if (ar || !(i in from)) { - if (!ar) ar = Array.prototype.slice.call(from, 0, i); - ar[i] = from[i]; - } - } - return to.concat(ar || Array.prototype.slice.call(from)); -}; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.VoteManager = void 0; -var commands_1 = require("/frameworks/commands"); -var funcs_1 = require("/funcs"); -var globals_1 = require("/globals"); -var players_1 = require("/players"); -/** Manages a vote. */ -var VoteManager = /** @class */ (function (_super) { - __extends(VoteManager, _super); - function VoteManager(voteTime, goal, isEligible, isCounted) { - if (goal === void 0) { goal = ["fractionOfVoters", 0.50001]; } - if (isEligible === void 0) { isEligible = function () { return true; }; } - if (isCounted === void 0) { isCounted = function (fishP) { return !fishP.afk(); }; } - var _this = _super.call(this) || this; - _this.voteTime = voteTime; - _this.goal = goal; - _this.isEligible = isEligible; - _this.isCounted = isCounted; - /** The ongoing voting session, if there is one. */ - _this.session = null; - if (goal[0] == "fractionOfVoters") { - if (goal[1] < 0 || goal[1] > 1) - (0, funcs_1.crash)("Invalid goal: fractionOfVoters must be between 0 and 1 inclusive"); - } - else if (goal[0] == "absolute") { - if (goal[1] < 0) - (0, funcs_1.crash)("Invalid goal: absolute must be greater than 0"); - } - Events.on(EventType.PlayerLeave, function (_a) { - var player = _a.player; - //Run once the player has been removed, but resolve the player first in case the connection gets nulled - var fishP = players_1.FishPlayer.get(player); - Core.app.post(function () { return _this.unvote(fishP); }); - }); - Events.on(EventType.GameOverEvent, function () { return _this.resetVote(); }); - return _this; - } - /** @throws CommandError */ - VoteManager.prototype.start = function (player, newVote, data) { - var _this = this; - if (data === null) - (0, funcs_1.crash)("Cannot start vote: data not provided"); - if (!this.isEligible(player, data)) - (0, commands_1.fail)("You are not eligible for this vote."); - this.session = { - timer: Timer.schedule(function () { return _this._checkVote(false); }, this.voteTime / 1000), - votes: new Map(), - data: data, - }; - this.vote(player, newVote, data); - }; - /** @throws CommandError */ - VoteManager.prototype.vote = function (player, newVote, data) { - var _this = this; - if (!this.session) - return this.start(player, newVote, data); - if (!this.isEligible(player, this.session.data)) - (0, commands_1.fail)("You are not eligible for this vote."); - var oldVote = this.session.votes.get(player.uuid); - this.session.votes.set(player.uuid, newVote); - if (oldVote == null) - this.fire("player vote", [player, newVote]); - this.fire("player vote change", [player, oldVote !== null && oldVote !== void 0 ? oldVote : 0, newVote]); - if (Date.now() - globals_1.fishState.startTime < 3000) - Timer.schedule(function () { return _this._checkVote(false); }, 3); - else - this._checkVote(false); - }; - VoteManager.prototype.unvote = function (player) { - if (!this.session) - return; - var fishP = players_1.FishPlayer.resolve(player); - var vote = this.session.votes.get(fishP.uuid); - if (vote) { - this.session.votes.delete(fishP.uuid); - this.fire("player vote removed", [player, vote]); - this._checkVote(false); - } - }; - /** Does not fire the events used to display messages, please print one before calling this */ - VoteManager.prototype.forceVote = function (outcome) { - if (outcome) { - this.fire("success", [true]); - } - else { - this.fire("fail", [true]); - } - this.resetVote(); - }; - VoteManager.prototype.resetVote = function () { - if (this.session == null) - return; - this.session.timer.cancel(); - this.session = null; - }; - VoteManager.prototype.requiredVotes = function () { - var _this = this; - if (this.goal[0] == "absolute") { - return this.goal[1]; - } - else { - var numVoters = players_1.FishPlayer.getAllOnline().filter(function (p) { - return _this.isEligible(p, _this.session.data) && (_this.isCounted(p, _this.session.data) || _this.session.votes.has(p.uuid)); - }).length; - return Math.max(Math.ceil(this.goal[1] * numVoters), 1); - } - }; - VoteManager.prototype.currentVotes = function () { - var e_1, _a; - if (this.session) { - try { - for (var _b = __values(this.session.votes.keys()), _c = _b.next(); !_c.done; _c = _b.next()) { - var key = _c.value; - var fishP = players_1.FishPlayer.getById(key); - if (!this.isEligible(fishP, this.session.data)) - this.session.votes.delete(key); - } - } - catch (e_1_1) { e_1 = { error: e_1_1 }; } - finally { - try { - if (_c && !_c.done && (_a = _b.return)) _a.call(_b); - } - finally { if (e_1) throw e_1.error; } - } - return __spreadArray([], __read(this.session.votes), false).reduce(function (acc, _a) { - var _b = __read(_a, 2), k = _b[0], v = _b[1]; - return acc + v; - }, 0); - } - else - return 0; - }; - VoteManager.prototype.getEligibleVoters = function () { - var _this = this; - if (!this.session) - return []; - return players_1.FishPlayer.getAllOnline().filter(function (p) { - return _this.isEligible(p, _this.session.data); - }); - }; - VoteManager.prototype.messageEligibleVoters = function (message) { - this.getEligibleVoters().forEach(function (p) { return p.sendMessage(message); }); - }; - VoteManager.prototype._checkVote = function (end) { - var votes = this.currentVotes(); - var required = this.requiredVotes(); - if (votes >= required) { - this.fire("success", [false]); - this.fire("vote passed", [votes, required]); - this.resetVote(); - } - else if (end) { - this.fire("fail", [false]); - this.fire("vote failed", [votes, required]); - this.resetVote(); - } - }; - return VoteManager; -}(funcs_1.EventEmitter)); -exports.VoteManager = VoteManager; +"use strict"; +/* +Copyright © BalaM314, 2026. All Rights Reserved. +This file contains the voting system. +Some contributions: @author Jurorno9 +*/ +var __extends = (this && this.__extends) || (function () { + var extendStatics = function (d, b) { + extendStatics = Object.setPrototypeOf || + ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || + function (d, b) { for (var p in b) if (Object.prototype.hasOwnProperty.call(b, p)) d[p] = b[p]; }; + return extendStatics(d, b); + }; + return function (d, b) { + if (typeof b !== "function" && b !== null) + throw new TypeError("Class extends value " + String(b) + " is not a constructor or null"); + extendStatics(d, b); + function __() { this.constructor = d; } + d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); + }; +})(); +var __values = (this && this.__values) || function(o) { + var s = typeof Symbol === "function" && Symbol.iterator, m = s && o[s], i = 0; + if (m) return m.call(o); + if (o && typeof o.length === "number") return { + next: function () { + if (o && i >= o.length) o = void 0; + return { value: o && o[i++], done: !o }; + } + }; + throw new TypeError(s ? "Object is not iterable." : "Symbol.iterator is not defined."); +}; +var __read = (this && this.__read) || function (o, n) { + var m = typeof Symbol === "function" && o[Symbol.iterator]; + if (!m) return o; + var i = m.call(o), r, ar = [], e; + try { + while ((n === void 0 || n-- > 0) && !(r = i.next()).done) ar.push(r.value); + } + catch (error) { e = { error: error }; } + finally { + try { + if (r && !r.done && (m = i["return"])) m.call(i); + } + finally { if (e) throw e.error; } + } + return ar; +}; +var __spreadArray = (this && this.__spreadArray) || function (to, from, pack) { + if (pack || arguments.length === 2) for (var i = 0, l = from.length, ar; i < l; i++) { + if (ar || !(i in from)) { + if (!ar) ar = Array.prototype.slice.call(from, 0, i); + ar[i] = from[i]; + } + } + return to.concat(ar || Array.prototype.slice.call(from)); +}; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.VoteManager = void 0; +var commands_1 = require("/frameworks/commands"); +var funcs_1 = require("/funcs"); +var globals_1 = require("/globals"); +var players_1 = require("/players"); +/** Manages a vote. */ +var VoteManager = /** @class */ (function (_super) { + __extends(VoteManager, _super); + function VoteManager(voteTime, goal, isEligible, isCounted) { + if (goal === void 0) { goal = ["fractionOfVoters", 0.50001]; } + if (isEligible === void 0) { isEligible = function () { return true; }; } + if (isCounted === void 0) { isCounted = function (fishP) { return !fishP.afk(); }; } + var _this = _super.call(this) || this; + _this.voteTime = voteTime; + _this.goal = goal; + _this.isEligible = isEligible; + _this.isCounted = isCounted; + /** The ongoing voting session, if there is one. */ + _this.session = null; + if (goal[0] == "fractionOfVoters") { + if (goal[1] < 0 || goal[1] > 1) + (0, funcs_1.crash)("Invalid goal: fractionOfVoters must be between 0 and 1 inclusive"); + } + else if (goal[0] == "absolute") { + if (goal[1] < 0) + (0, funcs_1.crash)("Invalid goal: absolute must be greater than 0"); + } + Events.on(EventType.PlayerLeave, function (_a) { + var player = _a.player; + //Run once the player has been removed, but resolve the player first in case the connection gets nulled + var fishP = players_1.FishPlayer.get(player); + Core.app.post(function () { return _this.unvote(fishP); }); + }); + Events.on(EventType.GameOverEvent, function () { return _this.resetVote(); }); + return _this; + } + /** @throws CommandError */ + VoteManager.prototype.start = function (player, newVote, data) { + var _this = this; + if (data === null) + (0, funcs_1.crash)("Cannot start vote: data not provided"); + if (!this.isEligible(player, data)) + (0, commands_1.fail)("You are not eligible for this vote."); + this.session = { + timer: Timer.schedule(function () { return _this._checkVote(false); }, this.voteTime / 1000), + votes: new Map(), + data: data, + }; + this.vote(player, newVote, data); + }; + /** @throws CommandError */ + VoteManager.prototype.vote = function (player, newVote, data) { + var _this = this; + if (!this.session) + return this.start(player, newVote, data); + if (!this.isEligible(player, this.session.data)) + (0, commands_1.fail)("You are not eligible for this vote."); + var oldVote = this.session.votes.get(player.uuid); + this.session.votes.set(player.uuid, newVote); + if (oldVote == null) + this.fire("player vote", [player, newVote]); + this.fire("player vote change", [player, oldVote !== null && oldVote !== void 0 ? oldVote : 0, newVote]); + if (Date.now() - globals_1.fishState.startTime < 3000) + Timer.schedule(function () { return _this._checkVote(false); }, 3); + else + this._checkVote(false); + }; + VoteManager.prototype.unvote = function (player) { + if (!this.session) + return; + var fishP = players_1.FishPlayer.resolve(player); + var vote = this.session.votes.get(fishP.uuid); + if (vote) { + this.session.votes.delete(fishP.uuid); + this.fire("player vote removed", [player, vote]); + this._checkVote(false); + } + }; + /** Does not fire the events used to display messages, please print one before calling this */ + VoteManager.prototype.forceVote = function (outcome) { + if (outcome) { + this.fire("success", [true]); + } + else { + this.fire("fail", [true]); + } + this.resetVote(); + }; + VoteManager.prototype.resetVote = function () { + if (this.session == null) + return; + this.session.timer.cancel(); + this.session = null; + }; + VoteManager.prototype.requiredVotes = function () { + var _this = this; + if (this.goal[0] == "absolute") { + return this.goal[1]; + } + else { + var numVoters = players_1.FishPlayer.getAllOnline().filter(function (p) { + return _this.isEligible(p, _this.session.data) && (_this.isCounted(p, _this.session.data) || _this.session.votes.has(p.uuid)); + }).length; + return Math.max(Math.ceil(this.goal[1] * numVoters), 1); + } + }; + VoteManager.prototype.currentVotes = function () { + var e_1, _a; + if (this.session) { + try { + for (var _b = __values(this.session.votes.keys()), _c = _b.next(); !_c.done; _c = _b.next()) { + var key = _c.value; + var fishP = players_1.FishPlayer.getById(key); + if (!this.isEligible(fishP, this.session.data)) + this.session.votes.delete(key); + } + } + catch (e_1_1) { e_1 = { error: e_1_1 }; } + finally { + try { + if (_c && !_c.done && (_a = _b.return)) _a.call(_b); + } + finally { if (e_1) throw e_1.error; } + } + return __spreadArray([], __read(this.session.votes), false).reduce(function (acc, _a) { + var _b = __read(_a, 2), k = _b[0], v = _b[1]; + return acc + v; + }, 0); + } + else + return 0; + }; + VoteManager.prototype.getEligibleVoters = function () { + var _this = this; + if (!this.session) + return []; + return players_1.FishPlayer.getAllOnline().filter(function (p) { + return _this.isEligible(p, _this.session.data); + }); + }; + VoteManager.prototype.messageEligibleVoters = function (message) { + this.getEligibleVoters().forEach(function (p) { return p.sendMessage(message); }); + }; + VoteManager.prototype._checkVote = function (end) { + var votes = this.currentVotes(); + var required = this.requiredVotes(); + if (votes >= required) { + this.fire("success", [false]); + this.fire("vote passed", [votes, required]); + this.resetVote(); + } + else if (end) { + this.fire("fail", [false]); + this.fire("vote failed", [votes, required]); + this.resetVote(); + } + }; + return VoteManager; +}(funcs_1.EventEmitter)); +exports.VoteManager = VoteManager; diff --git a/src/commands/general.ts b/src/commands/general.ts index edc8959e..4fe9dc79 100644 --- a/src/commands/general.ts +++ b/src/commands/general.ts @@ -5,7 +5,7 @@ This file contains most in-game chat commands that can be run by untrusted playe import { Achievement, Achievements } from "/achievements"; import * as api from "/api"; -import { FColor, FishServer, Gamemode, rules, text } from "/config"; +import { campaigns as campaignConfig, FColor, FishServer, Gamemode, rules, text, throwbackMapFileNames } from "/config"; import { command, commandList, fail, formatArg, Perm, Req } from "/frameworks/commands"; import type { FishCommandData } from "/frameworks/commands/types"; import { Cancel, Menu } from "/frameworks/menus"; @@ -14,7 +14,7 @@ import { FishEvents, fishPlugin, fishState, ipPortPattern, recentWhispers, tileH import { FMap, PartialMapRun } from "/maps"; import { FishPlayer } from "/players"; import { Rank, RoleFlag } from "/ranks"; -import { formatTime, formatTimeRelative, getColor, logAction, nearbyEnemyTile, neutralGameover, skipWaves, teleportPlayer } from "/utils"; +import { formatTime, formatTimeRelative, getColor, logAction, nearbyEnemyTile, neutralGameover, outputMessage, skipWaves, teleportPlayer } from "/utils"; import { VoteManager } from "/votes"; export const commands = commandList({ @@ -904,12 +904,12 @@ Please stop attacking and [lime]build defenses[] first!` perm: Perm.none, handler({output}){ output(`\ -[yellow]Use [white]/nextmap [lightgray] [yellow]to vote on a map. +[yellow]Use [white]/nextmap [lightgray][[map name] [yellow]to vote on a map. [blue]Available maps: _________________________ ${Vars.maps.customMaps().toArray().map(map => -`[yellow]${map.name()}` + `[yellow]${map.name()}` ).join("\n")}` ); } @@ -928,6 +928,101 @@ ${Vars.maps.customMaps().toArray().map(map => const voteDuration = Duration.minutes(1.5); let task: TimerTask | null = null; + const isThrowbackFile = (file:Fi) => throwbackMapFileNames.includes(file.name()); + const throwbackMaps = () => throwbackMapFileNames + .map(f => Vars.maps.customMaps().find(m => m.file.name() == f)) + .filter((m): m is MMap => m != null); + const normalMaps = () => Vars.maps.customMaps().toArray().filter(m => !isThrowbackFile(m.file)); + + async function showMapDetails(target: FishPlayer, map: MMap): Promise<"vote" | "back"> { + const res = await Menu.buttons( + target, + map.name(), + `[accent]Description: [white]${map.description()} +[accent]Author: [white]${map.author()} +[accent]Fastest Time: [white]${formatTime(FMap.getCreate(map)!.stats().shortestTime)} +[accent]Runs: [white]${FMap.getCreate(map)!.stats().allRunCount} +[accent]Winrate: [white]${(FMap.getCreate(map)!.stats().winRate * 100).toFixed(2)}%`, + [[ + { data: "vote" as const, text: "[green]Vote for this Map" } + ], [ + { data: "back" as const, text: "[red]Back" } + ]], + { onCancel: "null" } + ); + return res ?? "back"; + } + + async function pickMapFromList(target: FishPlayer, title: string, maps: MMap[]): Promise { + const map = await Menu.pagedList( + target, + title, + "Select a map to view more information.", + maps, + { optionStringifier: map => map.name(), rowsPerPage: 10, columns: 1 } + ); + const action = await showMapDetails(target, map); + if(action === "vote") return map; + return null; + } + + async function currentMenu(target: FishPlayer): Promise { + const result = await Menu.menu( + "Select a map", + `[accent]---Current Map--- +Map Name: [white]${Vars.state.map.name()} +[accent]Map Author: [white]${Vars.state.map.author()} +Fastest Time: [white]${formatTime(FMap.getCreate(Vars.state.map)!.stats().shortestTime)} +Current Time: [white]${formatTime(PartialMapRun.current?.duration() ?? FMap.getCreate(Vars.state.map)!.runs.at(-1)?.duration() ?? 0)}`, + ["[green]Current Maps", "[yellow]Throwback Maps", "[orange]Campaigns"], + target, + { columns: 1, includeCancel: "Close" } + ); + switch (result) { + case '[green]Current Maps': { + const picked = await pickMapFromList(target, "Current Maps", normalMaps()); + if(picked) return picked; + return currentMenu(target); + } + case '[yellow]Throwback Maps': { + const throwback = throwbackMaps(); + if(throwback.length == 0) fail("No throwback maps are currently available."); + const picked = await pickMapFromList(target, "Throwback Maps", throwback); + if(picked) return picked; + return currentMenu(target); + } + case '[orange]Campaigns': { + if(campaignConfig.length == 0) fail("No campaigns are currently available."); + const campaign = await Menu.pagedList( + target, + "Campaigns", + "Select a campaign to view its maps.", + campaignConfig, + { optionStringifier: c => c.name, rowsPerPage: 10, columns: 1 } + ); + const campaignMaps = campaign.mapFileNames + .map(f => Vars.maps.customMaps().find(m => m.file.name() == f)) + .filter((m): m is MMap => m != null); + if(campaignMaps.length == 0) fail(`Campaign "${campaign.name}" has no available maps.`); + const picked = await pickMapFromList(target, `Campaign: ${campaign.name}`, campaignMaps); + if(picked) return picked; + return currentMenu(target); + } + }; + } + + function sendVote(sender:FishPlayer, map:MMap | Random){ + votes.set(sender, map); + if(voteEndTime == -1){ + if((Date.now() - lastVoteTime) < Duration.minutes(1)) fail(`Please wait 1 minute before starting a new map vote.`); + startVote(); + Call.sendMessage(`[cyan]Next Map Vote: ${sender.name}[cyan] started a map vote, and voted for [yellow]${map.name()}[cyan]. Use [white]/nextmap ${map.plainName()}[] to add your vote, or run [white]/maps[] to see other available maps.`); + } else { + Call.sendMessage(`[cyan]Next Map Vote: ${sender.name}[cyan] voted for [yellow]${map.name()}[cyan]. Time left: [scarlet]${formatTimeRelative(voteEndTime, true)}`); + showVotes(); + } + } + function resetVotes(){ votes.clear(); voteEndTime = -1; @@ -943,7 +1038,7 @@ ${Vars.maps.customMaps().toArray().map(map => function showVotes(){ Call.sendMessage(`\ [green]Current votes: ------------------------------- +----------------------------- ${getMapData().map(({key:map, value:votes}) => `[cyan]${map.name()}[yellow]: ${votes}` ).toString("\n")}` @@ -987,7 +1082,7 @@ ${highestVotedMaps.map(({key:map, value:votes}) => winner = highestVotedMaps.get(0).key; Call.sendMessage(`[green]Map voting complete! The next map will be [yellow]${winner.name()} [green]with [yellow]${highestVoteCount}[green] votes.`); } - Vars.maps.setNextMapOverride(winner == random ? null : null); + if(winner != random) Vars.maps.setNextMapOverride(winner as MMap); resetVotes(); } @@ -995,26 +1090,18 @@ ${highestVotedMaps.map(({key:map, value:votes}) => Events.on(EventType.ServerLoadEvent, resetVotes); return { - args: ['map:mapOrRandom'], - description: 'Allows you to vote for the next map. Use /maps to see all available maps.', + args: ['map:mapOrRandom?'], + description: 'Allows you to vote for the next map.', perm: Perm.play, data: {votes, voteEndTime: () => voteEndTime, resetVotes, endVote}, requirements: [Req.cooldown(10_000)], - handler({args, sender}){ - const map = args.map === "random" ? random : args.map; + async handler({args, sender}){ if(Gamemode.testsrv()) fail(`Please use /forcenextmap instead.`); if(votes.get(sender)) fail(`You have already voted.`); - - if(voteEndTime == -1){ - if((Date.now() - lastVoteTime) < Duration.minutes(1)) fail(`Please wait 1 minute before starting a new map vote.`); - startVote(); - votes.set(sender, map); - Call.sendMessage(`[cyan]Next Map Vote: ${sender.name}[cyan] started a map vote, and voted for [yellow]${map.name()}[cyan]. Use [white]/nextmap ${map.plainName()}[] to add your vote, or run [white]/maps[] to see other available maps.`); - } else { - votes.set(sender, map); - Call.sendMessage(`[cyan]Next Map Vote: ${sender.name}[cyan] voted for [yellow]${map.name()}[cyan]. Time left: [scarlet]${formatTimeRelative(voteEndTime, true)}`); - showVotes(); - } + + args.map ??= await currentMenu(sender); + const map = args.map === "random" ? random : args.map; + sendVote(sender, map); } }; }), diff --git a/src/config.ts b/src/config.ts index 44b45381..39f6e872 100644 --- a/src/config.ts +++ b/src/config.ts @@ -143,6 +143,21 @@ export const heuristics = { }; export const stopAntiEvadeTime = Duration.minutes(30); export const backendIP = '45.79.202.111:5082'; +/** File names of throwback (retired) maps. These will not appear in the normal maps list, but will be available in the throwback maps tab. */ +export const throwbackMapFileNames: string[] = []; +/** A campaign is a curated sequence of maps that overrides normal map rotation. */ +export class Campaign { + constructor( + /** Display name of the campaign */ + public name:string, + /** Short description shown in the menu */ + public description:string, + /** Ordered list of map file names in the campaign */ + public mapFileNames:string[], + ){} +} +/** Available campaigns. Maps are listed in order; when a campaign is active, rotation follows this sequence. */ +export const campaigns:Campaign[] = []; export const Mode = { localDebug: new Fi("config/.debug").exists(), noBackend: new Fi("config/.debug").exists() && !backendIP.startsWith("127.0.0.1:"), @@ -376,5 +391,8 @@ export const rules = [ `# 8: [pink]No uncomfortable trolling or intentionally causing chaos. This includes any actions or messages that create an unpleasant atmosphere.`, `Failure to follow these rules will result in consequences: likely a ${prefixes.marked} tag for any game disruption, mute for broken chat rules, and bans for repeated offenses or bypasses.` ].map(r => `[white]${r}`); +/** Map tag key for maps that opt out of multi-wave skipping (Issue #151). + * Maps with this tag set to "true" will only allow 1 wave to be skipped per /vnw call. */ +export const noMultiVnwTag = "no-multi-vnw"; //#endregion diff --git a/src/frameworks/menus.ts b/src/frameworks/menus.ts index 3dd9e59b..aea2b4ab 100644 --- a/src/frameworks/menus.ts +++ b/src/frameworks/menus.ts @@ -52,8 +52,8 @@ type MenuConfirmProps = { type MenuCancelOption = "reject" | "null"; type MenuOptions = { - /** [red]Cancel[] will be added to the list of options. */ - includeCancel?: boolean; + /** The specified text, or "[red]Cancel[]", will be added to the list of options. */ + includeCancel?: boolean | string; optionStringifier?: (opt: TOption) => string; columns?: number; /** @@ -137,7 +137,7 @@ export const Menu = { const arrangedOptions = (options.length == 0 && !includeCancel) ? [] : to2DArray(options, columns); if(includeCancel){ - arrangedOptions.push(["[red]Cancel[]" as never]); + arrangedOptions.push([(typeof includeCancel == "string" ? includeCancel : "[red]Cancel[]") as never]); //This is safe because cancelOptionId is set, //so the handler will never get called with "Cancel". cancelOptionId = options.length; diff --git a/src/mindustryTypes.ts b/src/mindustryTypes.ts index 87f46643..b24be285 100644 --- a/src/mindustryTypes.ts +++ b/src/mindustryTypes.ts @@ -60,6 +60,7 @@ class Rules { attackMode: boolean; pvp: boolean; editor: boolean; + tags: ObjectMap; } const Vars: { diff --git a/src/utils.ts b/src/utils.ts index f3741d69..36c48684 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -5,7 +5,7 @@ For functions that don't need values from other files, see funcs.ts. */ import * as api from "/api"; -import { adminNames, bannedWords, Gamemode, GamemodeName, multiCharSubstitutions, substitutions, text } from "/config"; +import { adminNames, bannedWords, Gamemode, GamemodeName, multiCharSubstitutions, noMultiVnwTag, substitutions, text } from "/config"; import { CommandError, fail, PartialFormatString } from "/frameworks/commands"; import { Cancel } from "/frameworks/menus"; import { crash, escapeStringColorsServer, escapeTextDiscord, parseError, random, searchFixed, StringIO } from "/funcs"; @@ -466,6 +466,10 @@ export function neutralGameover(){ /** Please validate requestedWaves to ensure it is not huge */ export function skipWaves(requestedWaves: number, runIntermediateWaves: boolean){ + //Check if the current map has opted out of multi-wave skipping + if(Vars.state.rules.tags.get(noMultiVnwTag) == "true"){ + requestedWaves = Math.min(requestedWaves, 1); + } let winWave = Vars.state.rules.winWave; if(winWave <= 0) winWave = Infinity; const wavesToSkip = Math.min(requestedWaves, winWave - Vars.state.wave); From e9cab64e5421cdbbe1b407fb9f4daf880de56488 Mon Sep 17 00:00:00 2001 From: FluxxSynth Date: Sun, 21 Jun 2026 18:34:15 -0300 Subject: [PATCH 2/3] fix: add registerTextInput mock to test env The master branch introduced registerTextInput on the Menus global, but the test environment mock was missing it. Without this mock, the test suite crashes when Menus.registerTextInput() is called during registerListeners(). --- spec/src/env.ts | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/spec/src/env.ts b/spec/src/env.ts index 4841470d..da20ecc7 100644 --- a/spec/src/env.ts +++ b/spec/src/env.ts @@ -753,6 +753,11 @@ const Menus = { registerMenu(listener:BuiltinMenuListener){ this.menuListeners.add(listener); return this.menuListeners.size - 1; + }, + textInputListeners: new Seq(), + registerTextInput(listener:BuiltinTextInputListener){ + this.textInputListeners.add(listener); + return this.textInputListeners.size - 1; } }; const programStart = Date.now(); From be177f36c9aeefad23bad46cdf09c36e8a6a9254 Mon Sep 17 00:00:00 2001 From: FluxxSynth Date: Mon, 20 Jul 2026 21:25:18 -0300 Subject: [PATCH 3/3] fix: convert CRLF line endings to LF in build/scripts All files in build/scripts had CRLF (Windows) line endings which caused the entire file to show as changed in diffs, making code review impossible. Converted all 33 files to LF (Unix) line endings for consistency with the master branch and to enable proper code review. This fixes the line ending issues mentioned in PR #217 review comment. --- build/scripts/achievements.js | 1650 +++---- build/scripts/api.js | 428 +- build/scripts/commands/aggregate.js | 108 +- build/scripts/commands/console.js | 2260 +++++----- build/scripts/commands/general.js | 3106 ++++++------- build/scripts/commands/member.js | 274 +- build/scripts/commands/staff.js | 3358 +++++++------- build/scripts/config.js | 762 ++-- build/scripts/files.js | 192 +- build/scripts/fjsContext.js | 202 +- build/scripts/frameworks/commands.js | 44 +- build/scripts/frameworks/commands/commands.js | 1550 +++---- build/scripts/frameworks/commands/errors.js | 40 +- .../scripts/frameworks/commands/formatting.js | 366 +- build/scripts/frameworks/commands/perm.js | 190 +- .../frameworks/commands/requirements.js | 184 +- build/scripts/frameworks/commands/types.js | 58 +- build/scripts/frameworks/io.js | 776 ++-- build/scripts/frameworks/menus.js | 906 ++-- build/scripts/funcs.js | 948 ++-- build/scripts/globals.js | 68 +- build/scripts/index.js | 682 +-- build/scripts/maps.js | 722 +-- build/scripts/metrics.js | 152 +- build/scripts/mindustryTypes.js | 24 +- build/scripts/packetHandlers.js | 562 +-- build/scripts/players.js | 3988 ++++++++--------- build/scripts/promise.js | 272 +- build/scripts/ranks.js | 192 +- build/scripts/timers.js | 328 +- build/scripts/types.js | 12 +- build/scripts/utils.js | 2472 +++++----- build/scripts/votes.js | 436 +- 33 files changed, 13656 insertions(+), 13656 deletions(-) diff --git a/build/scripts/achievements.js b/build/scripts/achievements.js index 074d2db7..a67f9bff 100644 --- a/build/scripts/achievements.js +++ b/build/scripts/achievements.js @@ -1,825 +1,825 @@ -"use strict"; -var __makeTemplateObject = (this && this.__makeTemplateObject) || function (cooked, raw) { - if (Object.defineProperty) { Object.defineProperty(cooked, "raw", { value: raw }); } else { cooked.raw = raw; } - return cooked; -}; -var __read = (this && this.__read) || function (o, n) { - var m = typeof Symbol === "function" && o[Symbol.iterator]; - if (!m) return o; - var i = m.call(o), r, ar = [], e; - try { - while ((n === void 0 || n-- > 0) && !(r = i.next()).done) ar.push(r.value); - } - catch (error) { e = { error: error }; } - finally { - try { - if (r && !r.done && (m = i["return"])) m.call(i); - } - finally { if (e) throw e.error; } - } - return ar; -}; -var __spreadArray = (this && this.__spreadArray) || function (to, from, pack) { - if (pack || arguments.length === 2) for (var i = 0, l = from.length, ar; i < l; i++) { - if (ar || !(i in from)) { - if (!ar) ar = Array.prototype.slice.call(from, 0, i); - ar[i] = from[i]; - } - } - return to.concat(ar || Array.prototype.slice.call(from)); -}; -var __values = (this && this.__values) || function(o) { - var s = typeof Symbol === "function" && Symbol.iterator, m = s && o[s], i = 0; - if (m) return m.call(o); - if (o && typeof o.length === "number") return { - next: function () { - if (o && i >= o.length) o = void 0; - return { value: o && o[i++], done: !o }; - } - }; - throw new TypeError(s ? "Object is not iterable." : "Symbol.iterator is not defined."); -}; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.Achievements = exports.Achievement = void 0; -var config_1 = require("/config"); -var funcs_1 = require("/funcs"); -var globals_1 = require("/globals"); -var players_1 = require("/players"); -var ranks_1 = require("/ranks"); -var utils_1 = require("/utils"); -//scrap doesn't count -var serpuloItems = [Items.copper, Items.lead, Items.graphite, Items.silicon, Items.metaglass, Items.titanium, Items.plastanium, Items.thorium, Items.surgeAlloy, Items.phaseFabric]; -var erekirItems = [Items.beryllium, Items.graphite, Items.silicon, Items.tungsten, Items.oxide, Items.surgeAlloy, Items.thorium, Items.carbide, Items.phaseFabric]; -var usefulItems10k = { - serpulo: serpuloItems.map(function (i) { return new ItemStack(i, 10000); }), - erekir: erekirItems.map(function (i) { return new ItemStack(i, 10000); }), - sun: __spreadArray(__spreadArray([], __read(serpuloItems), false), __read(erekirItems), false).map(function (i) { return new ItemStack(i, 10000); }), -}; -var allItems1k = Vars.content.items().select(function (i) { return !i.hidden; }).toArray().map(function (i) { return new ItemStack(i, 1000); }); -var mixtechItems = Items.serpuloItems.copy(); -Items.erekirItems.each(function (i) { return mixtechItems.add(i); }); -var Achievement = /** @class */ (function () { - function Achievement(icon, name, description, options) { - var _a; - if (options === void 0) { options = {}; } - this.name = name; - this.notify = "player"; - this.hidden = false; - this.disabled = false; - if (Array.isArray(icon)) { - this.icon = (icon[0].startsWith("[") ? icon[0] : "[".concat(icon[0], "]")) + (typeof icon[1] == "number" ? String.fromCharCode(icon[1]) : icon[1]); - } - else if (typeof icon == "number") { - this.icon = String.fromCharCode(icon); - } - else { - this.icon = icon; - } - if (Array.isArray(description)) { - _a = __read(description, 2), this.description = _a[0], this.extendedDescription = _a[1]; - } - else - this.description = description; - this.nid = Achievement._id++; - Object.assign(this, options); - if (options.modes) { - var _b = __read(options.modes), type = _b[0], modes_1 = _b.slice(1); - if (type == "only") { - this.allowedModes = modes_1; - this.modesText = modes_1.join(", "); - } - else { - this.allowedModes = config_1.GamemodeNames.filter(function (m) { return !modes_1.includes(m); }); - this.modesText = "all except ".concat(modes_1.join(", ")); - } - } - else { - this.allowedModes = config_1.GamemodeNames; - this.modesText = "all"; - } - if (!this.disabled) { - Achievement.all.push(this); - if (this.checkPlayerFrequent || this.checkFrequent) - Achievement.checkFrequent.push(this); - if (this.checkPlayerInfrequent || this.checkInfrequent) - Achievement.checkInfrequent.push(this); - if (this.checkPlayerJoin) - Achievement.checkJoin.push(this); - if (this.checkPlayerGameover || this.checkGameover) - Achievement.checkGameover.push(this); - } - } - Achievement.prototype.message = function () { - return config_1.FColor.achievement(templateObject_1 || (templateObject_1 = __makeTemplateObject(["Achievement granted!\n[accent]", "[white]: ", ""], ["Achievement granted!\\n[accent]", "[white]: ", ""])), this.name, this.description); - }; - Achievement.prototype.messageToEveryone = function (player) { - return config_1.FColor.achievement(templateObject_2 || (templateObject_2 = __makeTemplateObject(["Player ", " has completed the achievement \"", "\"."], ["Player ", " has completed the achievement \"", "\"."])), player.prefixedName, this.name); - }; - Achievement.prototype.allowedInMode = function () { - return this.allowedModes.includes(config_1.Gamemode.name()); - }; - Achievement.prototype.grantToAllOnline = function (team) { - var _this = this; - players_1.FishPlayer.forEachPlayer(function (p) { - if (!_this.has(p) && (!team || p.team() == team)) { - if (_this.notify != "nobody") - p.sendMessage(_this.message()); - _this.setObtained(p); - } - }); - }; - /** Do not call this in a loop on an achievement set to notify everyone. */ - Achievement.prototype.grantTo = function (player, allowRepeatMessage) { - if (allowRepeatMessage === void 0) { allowRepeatMessage = true; } - var has = this.has(player); - if (!has || allowRepeatMessage) { - if (this.notify == "everyone") - Call.sendMessage(this.messageToEveryone(player)); - else if (this.notify == "player") - player.sendMessage(this.message()); - } - if (!has) - this.setObtained(player); - }; - Achievement.prototype.setObtained = function (player) { - //void player.updateSynced(fishP => fishP.achievements.set(this.nid)); - player.achievements.set(this.nid); - }; - Achievement.prototype.has = function (player) { - return player.achievements.get(this.nid); - }; - Achievement.all = []; - /** Checked every second. */ - Achievement.checkFrequent = []; - /** Checked every 10 seconds. Use for states that can be gained but not lost, such as "x wins". */ - Achievement.checkInfrequent = []; - Achievement.checkJoin = []; - Achievement.checkGameover = []; - Achievement._id = 0; - return Achievement; -}()); -exports.Achievement = Achievement; -Events.on(EventType.PlayerJoin, function (_a) { - var e_1, _b; - var _c; - var player = _a.player; - Time.mark(); - var _loop_1 = function (ach) { - if (ach.allowedInMode()) { - var fishP_1 = players_1.FishPlayer.get(player); - if (!ach.has(fishP_1) && ((_c = ach.checkPlayerJoin) === null || _c === void 0 ? void 0 : _c.call(ach, fishP_1))) { - if (fishP_1.dataSynced) - ach.grantTo(fishP_1); - else - Timer.schedule(function () { return ach.grantTo(fishP_1); }, 2); //2 seconds should be enough - } - } - }; - try { - for (var _d = __values(Achievement.checkJoin), _e = _d.next(); !_e.done; _e = _d.next()) { - var ach = _e.value; - _loop_1(ach); - } - } - catch (e_1_1) { e_1 = { error: e_1_1 }; } - finally { - try { - if (_e && !_e.done && (_b = _d.return)) _b.call(_d); - } - finally { if (e_1) throw e_1.error; } - } - Log.debug("ach join @", Time.elapsed()); -}); -globals_1.FishEvents.on("gameOver", function (_, winner) { - var e_2, _a; - var _b; - Time.mark(); - var _loop_2 = function (ach) { - if (ach.allowedInMode()) { - if ((_b = ach.checkGameover) === null || _b === void 0 ? void 0 : _b.call(ach, winner)) - ach.grantToAllOnline(); - else - players_1.FishPlayer.forEachPlayer(function (fishP) { - var _a; - if (!ach.has(fishP) && ((_a = ach.checkPlayerGameover) === null || _a === void 0 ? void 0 : _a.call(ach, fishP, winner))) { - ach.grantTo(fishP); - } - }); - } - }; - try { - for (var _c = __values(Achievement.checkGameover), _d = _c.next(); !_d.done; _d = _c.next()) { - var ach = _d.value; - _loop_2(ach); - } - } - catch (e_2_1) { e_2 = { error: e_2_1 }; } - finally { - try { - if (_d && !_d.done && (_a = _c.return)) _a.call(_c); - } - finally { if (e_2) throw e_2.error; } - } - Log.debug("ach gameover @", Time.elapsed()); -}); -Timer.schedule(function () { - var e_3, _a; - Time.mark(); - var _loop_3 = function (ach) { - if (ach.allowedInMode()) { - if (ach.checkFrequent) { - if (config_1.Gamemode.pvp()) { - Vars.state.teams.active.each(function (_a) { - var team = _a.team; - if (ach.checkFrequent(team)) - ach.grantToAllOnline(team); - }); - } - else { - if (ach.checkFrequent(Vars.state.rules.defaultTeam)) - ach.grantToAllOnline(); - } - } - else { - players_1.FishPlayer.forEachPlayer(function (fishP) { - var _a; - if (!ach.has(fishP) && ((_a = ach.checkPlayerFrequent) === null || _a === void 0 ? void 0 : _a.call(ach, fishP))) - ach.grantTo(fishP); - }); - } - } - }; - try { - for (var _b = __values(Achievement.checkFrequent), _c = _b.next(); !_c.done; _c = _b.next()) { - var ach = _c.value; - _loop_3(ach); - } - } - catch (e_3_1) { e_3 = { error: e_3_1 }; } - finally { - try { - if (_c && !_c.done && (_a = _b.return)) _a.call(_b); - } - finally { if (e_3) throw e_3.error; } - } - Log.debug("ach frequent @", Time.elapsed()); -}, 1, 1); -Timer.schedule(function () { - var e_4, _a; - Time.mark(); - var _loop_4 = function (ach) { - if (ach.allowedInMode()) { - if (ach.checkInfrequent) { - if (config_1.Gamemode.pvp()) { - Vars.state.teams.active.each(function (_a) { - var team = _a.team; - if (ach.checkInfrequent(team)) - ach.grantToAllOnline(team); - }); - } - else { - if (ach.checkInfrequent(Vars.state.rules.defaultTeam)) - ach.grantToAllOnline(); - } - } - else { - players_1.FishPlayer.forEachPlayer(function (fishP) { - var _a; - if (!ach.has(fishP) && ((_a = ach.checkPlayerInfrequent) === null || _a === void 0 ? void 0 : _a.call(ach, fishP))) - ach.grantTo(fishP); - }); - } - } - }; - try { - for (var _b = __values(Achievement.checkInfrequent), _c = _b.next(); !_c.done; _c = _b.next()) { - var ach = _c.value; - _loop_4(ach); - } - } - catch (e_4_1) { e_4 = { error: e_4_1 }; } - finally { - try { - if (_c && !_c.done && (_a = _b.return)) _a.call(_b); - } - finally { if (e_4) throw e_4.error; } - } - Log.debug("ach infrequent @", Time.elapsed()); -}, 10, 10); -exports.Achievements = { - // =========================== - // ╦ ╦ ╔═╗ ╦═╗ ╔╗╔ ╦ ╔╗╔ ╔═╗ ┬ - // ║║║ ╠═╣ ╠╦╝ ║║║ ║ ║║║ ║ ╦ │ - // ╚╩╝ ╩ ╩ ╩╚═ ╝╚╝ ╩ ╝╚╝ ╚═╝ o - // =========================== - // Do not change the order of any achievements. - // Do not remove any achievements: instead, set the "disabled" option to true. - // Reordering achievements will cause ID shifts. - //Joining based - welcome: new Achievement(["gold", Iconc.infoCircle], "Welcome", "Join the server.", { - checkPlayerJoin: function () { return true; }, - notify: "nobody" - }), - migratory_fish: new Achievement(Iconc.exit, "Migratory Fish", "Join all of our servers.", { - disabled: true - }), //TODO - frequent_visitor: new Achievement(Iconc.planeOutline, "Frequent Visitor", ["Join the server 100 times.", "Note: Do not reconnect frequently, that will not work. This achievement requires that you have been playing for 1 month."], { - checkPlayerJoin: function (p) { return p.info().timesJoined >= 100 && (Date.now() - p.globalFirstJoined > funcs_1.Duration.months(1)); } - }), - //Gamemode based - attack: new Achievement(Iconc.modeAttack, "Attack", ["Defeat an attack map.", "You must be present for the beginning and end of the game."], { - modes: ["only", "attack"], - checkPlayerGameover: function (player, winTeam) { - return Vars.state.rules.defaultTeam == winTeam && player.tstats.lastMapStartTime == players_1.FishPlayer.lastMapStartTime; - }, - }), - survival: new Achievement(Iconc.modeSurvival, "Survival", ["Survive 50 waves in a survival map.", "Must be during the same game."], { - modes: ["only", "survival"], - checkPlayerInfrequent: function (player) { - return player.tstats.wavesSurvived >= 50; - }, - }), - pvp: new Achievement(Iconc.modePvp, "PVP", ["Win a match of PVP.", "You must be present for the beginning and end of the game."], { - modes: ["only", "pvp"], - checkPlayerGameover: function (player, winTeam) { - return player.team() == winTeam && player.tstats.lastMapStartTime == players_1.FishPlayer.lastMapStartTime; - }, - }), - sandbox: new Achievement(Iconc.image, "Sandbox", "Spend 1 hour in Sandbox.", { - modes: ["only", "sandbox"], - checkPlayerInfrequent: function (p) { return p.stats.timeInGame > funcs_1.Duration.hours(1); }, - }), - hexed: new Achievement(Iconc.layers, "Hexed", ["Play a match of Hexed.", "You must be present for the beginning and end of the game."], { - modes: ["only", "hexed"], - checkPlayerGameover: function (player) { - return player.tstats.lastMapStartTime == players_1.FishPlayer.lastMapStartTime; - }, - }), - minigame: new Achievement(Iconc.play, "Minigame", ["Win a Minigame.", "You must be present for the beginning and end of the game."], { - modes: ["only", "minigame"], - checkPlayerGameover: function (player, winTeam) { - return player.team() == winTeam && player.tstats.lastMapStartTime == players_1.FishPlayer.lastMapStartTime; - }, - }), - //playtime based - playtime_1: new Achievement(["white", Iconc.googleplay], "Playtime 1", "Spend 1 hour in-game.", { - checkPlayerInfrequent: function (p) { return p.globalStats.timeInGame >= funcs_1.Duration.hours(1); } - }), - playtime_2: new Achievement(["red", Iconc.googleplay], "Playtime 2", "Spend 12 hours in-game.", { - checkPlayerInfrequent: function (p) { return p.globalStats.timeInGame >= funcs_1.Duration.hours(12); } - }), - playtime_3: new Achievement(["orange", Iconc.googleplay], "Playtime 3", "Spend 2 days in-game.", { - checkPlayerInfrequent: function (p) { return p.globalStats.timeInGame >= funcs_1.Duration.days(2); } - }), - playtime_4: new Achievement(["yellow", Iconc.googleplay], "Playtime 4", "Spend 10 days in-game.", { - checkPlayerInfrequent: function (p) { return p.globalStats.timeInGame >= funcs_1.Duration.days(10); } - }), - //victories based - victory_1: new Achievement(["white", Iconc.star], "First Victory", "Win a map run.", { - checkPlayerGameover: function (p) { return p.globalStats.gamesWon >= 1; } - }), - victory_2: new Achievement(["red", Iconc.star], "Victories 2", "Win 5 map runs.", { - checkPlayerGameover: function (p) { return p.globalStats.gamesWon >= 5; } - }), - victory_3: new Achievement(["orange", Iconc.star], "Victories 3", "Win 30 map runs.", { - checkPlayerGameover: function (p) { return p.globalStats.gamesWon >= 30; } - }), - victory_4: new Achievement(["yellow", Iconc.star], "Victories 4", "Win 100 map runs.", { - checkPlayerGameover: function (p) { return p.globalStats.gamesWon >= 100; }, - notify: "everyone" - }), - //games based - games_1: new Achievement(["white", Iconc.itchio], "Games 1", "Play 10 map runs.", { - checkPlayerGameover: function (p) { return p.globalStats.gamesFinished >= 10; } - }), - games_2: new Achievement(["red", Iconc.itchio], "Games 2", "Play 40 map runs.", { - checkPlayerGameover: function (p) { return p.globalStats.gamesFinished >= 40; } - }), - games_3: new Achievement(["orange", Iconc.itchio], "Games 3", "Play 100 map runs.", { - checkPlayerGameover: function (p) { return p.globalStats.gamesFinished >= 100; } - }), - games_4: new Achievement(["yellow", Iconc.itchio], "Games 4", "Play 200 map runs.", { - checkPlayerGameover: function (p) { return p.globalStats.gamesFinished >= 200; }, - notify: "everyone" - }), - //messages based - messages_1: new Achievement(["white", Iconc.chat], "Hello", "Send your first chat message.", { - checkPlayerInfrequent: function (p) { return p.globalStats.chatMessagesSent >= 1; }, - notify: "nobody" - }), - messages_2: new Achievement(["red", Iconc.chat], "Chat 2", ["Send 100 chat messages.", "Warning: you will be kicked if you spam the chat."], { - checkPlayerInfrequent: function (p) { return p.globalStats.chatMessagesSent >= 100; } - }), - messages_3: new Achievement(["orange", Iconc.chat], "Chat 3", ["Send 500 chat messages.", "Warning: you will be kicked if you spam the chat."], { - checkPlayerInfrequent: function (p) { return p.globalStats.chatMessagesSent >= 500; } - }), - messages_4: new Achievement(["yellow", Iconc.chat], "Chat 4", ["Send 2000 chat messages.", "Warning: you will be kicked if you spam the chat."], { - checkPlayerInfrequent: function (p) { return p.globalStats.chatMessagesSent >= 2000; } - }), - messages_5: new Achievement(["lime", Iconc.chat], "Chat 4", ["Send 5000 chat messages.", "Warning: you will be kicked if you spam the chat."], { - checkPlayerInfrequent: function (p) { return p.globalStats.chatMessagesSent >= 5000; }, - notify: "everyone" - }), - //blocks built based - builds_1: new Achievement(["white", Iconc.fileText], "The Factory Must Prepare", "Construct 1 buildings.", { - checkPlayerInfrequent: function (p) { return p.globalStats.blocksPlaced >= 1; }, - notify: "nobody" - }), - builds_2: new Achievement(["red", Iconc.fileText], "The Factory Must Begin", "Construct 200 buildings.", { - checkPlayerInfrequent: function (p) { return p.globalStats.blocksPlaced > 200; } - }), - builds_3: new Achievement(["orange", Iconc.fileText], "The Factory Must Produce", "Construct 1000 buildings.", { - checkPlayerInfrequent: function (p) { return p.globalStats.blocksPlaced > 1000; } - }), - builds_4: new Achievement(["yellow", Iconc.fileText], "The Factory Must Grow", "Construct 5000 buildings.", { - checkPlayerInfrequent: function (p) { return p.globalStats.blocksPlaced > 5000; }, - }), - //units - t5: new Achievement(Blocks.tetrativeReconstructor.emoji(), "T5", "Control a T5 unit.", { - modes: ["not", "sandbox"], - checkPlayerFrequent: function (player) { - var _a; - return globals_1.unitsT5.includes((_a = player.unit()) === null || _a === void 0 ? void 0 : _a.type); - }, - }), - dibs: new Achievement(["green", Blocks.tetrativeReconstructor.emoji()], "Dibs", "Be the first player to control the first T5 unit made by a reconstructor that you placed.", { - modes: ["not", "sandbox"], - disabled: true - }), //TODO - worm: new Achievement(UnitTypes.latum.emoji(), "Worm", "Control a Latum.", { - checkPlayerFrequent: function (player) { - var _a; - return ((_a = player.unit()) === null || _a === void 0 ? void 0 : _a.type) == UnitTypes.latum; - } - }), - //pvp - above_average: new Achievement(Iconc.chartBar, "Above Average", ["Reach a win rate above 50%.", "Must be over at least 20 games of PVP."], { - modes: ["only", "pvp"], - checkPlayerInfrequent: function (p) { return p.stats.gamesWon / p.stats.gamesFinished > 0.5 && p.stats.gamesFinished >= 20; } - }), - head_start: new Achievement(Iconc.commandAttack, "Head Start", ["Win a match of PVP where your opponents have a 5 minute head start.", "Your team must wait for the first 5 minutes without building or descontructing any buildings."], { - modes: ["only", "pvp"], - disabled: true - }), //TODO - one_v_two: new Achievement(["red", Iconc.modePvp], "1v2", "Defeat two (or more) opponents in PVP without help from other players.", { - modes: ["only", "pvp"], - disabled: true - }), //TODO - //sandbox - underpowered: new Achievement(["red", Blocks.powerSource.emoji()], "Underpowered", "Overload a power source.", { - modes: ["only", "sandbox"], - checkFrequent: function () { - var found = false; - //deliberate ordering for performance reasons - Groups.powerGraph.each(function (_a) { - var graph = _a.graph; - //we don't need to actually check for power sources, just assume that ~1mil power is a source - if (graph.lastPowerNeeded > graph.lastPowerProduced && graph.lastPowerNeeded < 1e10 && (graph.lastPowerProduced / Time.delta * 60) >= 999900) - found = true; - }); - return found; - } - }), - //easter eggs - memory_corruption: new Achievement(["red", Iconc.host], "Is the server OK?", "Witness a memory corruption.", { - notify: "nobody" - }), - run_js_without_perms: new Achievement(["yellow", Iconc.warning], "XKCD 838", ["Receive a warning from the server that an incident will be reported.", "One of the admin commands has a custom error message."], { - notify: "everyone" - }), - script_kiddie: new Achievement(["red", Iconc.warning], "Script Kiddie", ["Pretend to be a hacker. The server will disagree.", "Change your name to something including \"hacker\"."], { - notify: "nobody" - }), - hacker: new Achievement(["lightgray", Iconc.host], "Hacker", "Find a bug in the server and report it responsibly.", { - hidden: true - }), - //items based - items_10k: new Achievement(["green", Iconc.distribution], "Cornucopia", "Obtain 10k of every useful resource.", { - modes: ["not", "sandbox"], - checkPlayerFrequent: function (player) { - var _a; - if (!Vars.state.planet) - return false; - return ((_a = player.team().items()) === null || _a === void 0 ? void 0 : _a.has(usefulItems10k[Vars.state.planet.name])) || false; - }, - }), - fullVault: new Achievement(["green", Blocks.vault.emoji()], "Well Stocked", ["Fill a vault with every obtainable item.", "Requires mixtech."], { - modes: ["not", "sandbox"], - checkInfrequent: function (team) { - return Vars.indexer.getFlagged(team, BlockFlag.storage).contains(boolf(function (b) { return b.block == Blocks.vault && b.items.has(allItems1k) && b.linkedCore == null; })); - }, - }), - full_core: new Achievement(["green", Blocks.coreAcropolis.emoji()], "Multiblock Incinerator", "Completely fill the core with all obtainable items on a map with core incineration enabled.", { - modes: ["not", "sandbox"], - checkFrequent: function (team) { - var _a; - if (!Vars.state.planet) - return false; - var items; - switch (Vars.state.planet.name) { - case "serpulo": - items = Items.serpuloItems; - break; - case "erekir": - items = Items.erekirItems; - break; - case "sun": - items = mixtechItems; - break; - } - var capacity = (_a = team.core()) === null || _a === void 0 ? void 0 : _a.storageCapacity; - if (!capacity) - return false; - var module = team.items(); - return items.allMatch(function (i) { return module.has(i, capacity); }); - }, - }), - siligone: new Achievement(["red", Items.silicon.emoji()], "Siligone", ["Run out of silicon.", "You must have reached 2000 silicon before running out."], { - modes: ["not", "sandbox"] - }), - silicon_100k: new Achievement(["green", Items.silicon.emoji()], "Silicon for days", "Obtain 100k silicon.", { - modes: ["not", "sandbox"], - checkFrequent: function (team) { return team.items().has(Items.silicon, 100000); } - }), - //other players based - alone: new Achievement(["red", Iconc.players], "Alone", "Be the only player online for more than two minutes", { - notify: "nobody" - }), - join_playercount_20: new Achievement(["lime", Iconc.players], "Is there enough room?", "Join a server with 20 players online", { - checkPlayerJoin: function () { return Groups.player.size() > 20; }, - }), - meet_staff: new Achievement(["lime", Iconc.hammer], "Griefer Beware", "Meet a staff member in-game", { - checkPlayerJoin: function () { return Groups.player.contains(function (p) { return players_1.FishPlayer.get(p).ranksAtLeast("mod"); }); }, - }), - meet_fish: new Achievement(["blue", Iconc.admin], "The Big Fish", "Meet >|||>Fish himself in-game", { - checkPlayerJoin: function () { return Groups.player.contains(function (p) { return players_1.FishPlayer.get(p).ranksAtLeast("fish"); }); }, - hidden: true, - }), - server_speak: new Achievement(["pink", Iconc.host], "It Speaks!", "Hear the server talk in chat."), - see_marked_griefer: new Achievement(["red", Iconc.hammer], "Flying Tonk", "See a marked griefer in-game.", { - checkInfrequent: function () { return Groups.player.contains(function (p) { return players_1.FishPlayer.get(p).marked(); }); }, - }), - //maps based - beat_map_not_in_rotation: new Achievement(["pink", Iconc.map], "How?", "Beat a map that isn't in the list of maps.", { - notify: "everyone", - modes: ["not", "pvp"], - checkGameover: function (team) { return team == Vars.state.rules.defaultTeam && !Vars.state.map.custom; } - }), - //misc - power_1mil: new Achievement(["green", Blocks.powerSource.emoji()], "Who needs sources?", "Reach a power production of 1 million without using power sources.", { - modes: ["not", "sandbox"], - checkFrequent: function (team) { - var found = false; - //deliberate ordering for performance reasons - Groups.powerGraph.each(function (_a) { - var _b; - var graph = _a.graph; - //we need to actually check for power sources - if ((graph.lastPowerProduced / Time.delta * 60) > 1e6 && - !graph.producers.contains(boolf(function (b) { return b.block == Blocks.powerSource; })) && - ((_b = graph.producers.firstOpt()) === null || _b === void 0 ? void 0 : _b.team) == team) - found = true; - }); - return found; - } - }), - pacifist_crawler: new Achievement(UnitTypes.crawler.emoji(), "Pacifist Crawler", "Control a crawler for 15 minutes without exploding.", { - modes: ["not", "sandbox"], - disabled: true - }), //TODO - core_low_hp: new Achievement(["yellow", Blocks.coreNucleus.emoji()], "Close Call", "Have your core reach less than 50 health, but survive.", { - modes: ["not", "sandbox"], - }), - enemy_core_low_hp: new Achievement(["red", Blocks.coreNucleus.emoji()], "So Close", "Cause the enemy core to reach less than 50 health, but survive.", { - modes: ["not", "sandbox"], - }), - verified: new Achievement([ranks_1.Rank.active.color, Iconc.ok], "Verified", "Be promoted automatically to ".concat(ranks_1.Rank.active.coloredName(), " rank."), { - checkPlayerJoin: function (p) { return p.ranksAtLeast("active"); }, notify: "nobody" - }), - click_me: new Achievement(Iconc.bookOpen, "Clicked", "Run /achievementgrid and click this achievement."), - afk: new Achievement(["yellow", Iconc.lock], "AFK?", "Win a game without interacting with any blocks.", { - modes: ["not", "sandbox"], - checkPlayerGameover: function (player, winTeam) { - return player.team() == winTeam && player.tstats.blockInteractionsThisMap == 0; - }, - }), - status_effects_5: new Achievement(StatusEffects.electrified.emoji(), "A Furious Cocktail", "Have at least 5 status effects at once.", { - checkPlayerFrequent: function (p) { - var unit = p.unit(); - if (!unit) - return false; - var statuses = (0, utils_1.getStatuses)(unit); - return statuses.size >= 5; - }, - modes: ["not", "sandbox"] - }), - drown_big_tank: new Achievement(["blue", UnitTypes.conquer.emoji()], "Not Waterproof", "Drown an enemy Conquer or Vanquish.", { - notify: "everyone", - modes: ["not", "sandbox"] - }), - drown_mace_in_cryo: new Achievement(["cyan", UnitTypes.mace.emoji()], "Cooldown", "Drown a Mace in ".concat(Blocks.cryofluid.emoji(), " Cryofluid."), { - notify: "everyone", - modes: ["not", "sandbox"] - }), - max_boost_duo: new Achievement(["yellow", Blocks.duo.emoji()], "In Duo We Trust", "Control a Duo with maximum boosts.", { - checkPlayerFrequent: function (player) { - var _a, _b; - var tile = (_b = (_a = player.unit()) === null || _a === void 0 ? void 0 : _a.tile) === null || _b === void 0 ? void 0 : _b.call(_a); - if (!tile) - return false; - return tile.block == Blocks.duo && !tile.ammo.isEmpty() && tile.ammo.peek().item == Items.silicon && tile.liquids.current() == Liquids.cryofluid && tile.timeScale() >= 2.5; - }, - notify: "everyone", - modes: ["not", "sandbox"] - }), - foreshadow_overkill: new Achievement(["yellow", Blocks.foreshadow.emoji()], "Overkill", ["Kill a Dagger with a maximally boosted Foreshadow.", "Hint: the maximum overdrive is not +150%..."], { - notify: "everyone", - modes: ["not", "sandbox"] - }), - impacts_15: new Achievement(["green", Blocks.impactReactor.emoji()], "Darthscion's Nightmare", "Run 15 impact reactors at full efficiency.", { - modes: ["not", "sandbox"], - notify: "everyone", - checkInfrequent: function (team) { - var found = false; - //deliberate ordering for performance reasons - Groups.powerGraph.each(function (_a) { - var graph = _a.graph; - if (graph.producers.size >= 15 && graph.producers.count(function (b) { return b.block == Blocks.impactReactor && b.warmup > 0.99999; }) > 15 && graph.producers.first().team == team) - found = true; - }); - return found; - }, - }), - help_help: new Achievement(["brown", Iconc.info], "Help with help", "Run /help help", { - notify: "everyone" - }), - ohno: new Achievement(["scarlet", UnitTypes.alpha.emoji()], "Oh no", "Control an ohno unit."), - sniper_duel: new Achievement(["yellow", UnitTypes.omura.emoji()], "Sniper duel", "Kill a Foreshadow with an Omura from outside its range."), - around_the_world: new Achievement(Iconc.planet, "Around the World", "Fly your unit around the entire map without entering it, starting from the lower left.", { - notify: "everyone", - }), -}; -Object.entries(exports.Achievements).forEach(function (_a) { - var _b = __read(_a, 2), id = _b[0], a = _b[1]; - return a.sid = id; -}); -globals_1.FishEvents.on("commandUnauthorized", function (_, player, name) { - if ((name == "js" || name == "fjs") && !exports.Achievements.run_js_without_perms.has(player)) - exports.Achievements.run_js_without_perms.grantTo(player); -}); -Events.on(EventType.UnitDrownEvent, function (_a) { - var _b; - var unit = _a.unit; - if (!config_1.Gamemode.sandbox()) { - if (unit.type == UnitTypes.mace && ((_b = unit.tileOn()) === null || _b === void 0 ? void 0 : _b.floor()) == Blocks.cryofluid) - exports.Achievements.drown_mace_in_cryo.grantToAllOnline(); - else if (unit.type == UnitTypes.conquer || unit.type == UnitTypes.vanquish) { - if (config_1.Gamemode.pvp()) { - Vars.state.teams.active.map(function (t) { return t.team; }).select(function (t) { return t !== unit.team; }).each(function (t) { return exports.Achievements.drown_big_tank.grantToAllOnline(t); }); - } - else { - if (unit.team !== Vars.state.rules.defaultTeam) - exports.Achievements.drown_big_tank.grantToAllOnline(); - } - } - } -}); -Events.on(EventType.UnitBulletDestroyEvent, function (_a) { - var _b; - var unit = _a.unit, bullet = _a.bullet; - if (!config_1.Gamemode.sandbox() && unit.type == UnitTypes.dagger && ((_b = bullet.owner) === null || _b === void 0 ? void 0 : _b.block) == Blocks.foreshadow) { - var build = bullet.owner; - if (build.liquids.current() == Liquids.cryofluid && build.timeScale() >= 3) - exports.Achievements.foreshadow_overkill.grantToAllOnline(build.team); - } -}); -Events.on(EventType.BuildingBulletDestroyEvent, function (_a) { - var _b; - var build = _a.build, bullet = _a.bullet; - if (!config_1.Gamemode.sandbox() && build.block == Blocks.foreshadow && ((_b = bullet.owner) === null || _b === void 0 ? void 0 : _b.type) == UnitTypes.omura) { - var unit = bullet.owner; - var player = unit.getPlayer(); - if (player && !unit.within(build, build.range() + unit.hitSize / 2)) - exports.Achievements.sniper_duel.grantTo(players_1.FishPlayer.get(player)); - } -}); -var siliconReached = Team.all.map(function (_) { return false; }); -Events.on(EventType.GameOverEvent, function () { return siliconReached = Team.all.map(function (_) { return false; }); }); -var isAlone = 0; -Timer.schedule(function () { - if (!Vars.state.gameOver && !config_1.Gamemode.sandbox()) { - Vars.state.teams.active.each(function (_a) { - var team = _a.team; - if (team.items().has(Items.silicon, 2000)) - siliconReached[team.id] = true; - else if (siliconReached[team.id] && team.items().get(Items.silicon) == 0) - exports.Achievements.siligone.grantToAllOnline(team); - }); - } - if (Groups.player.size() == 1) { - if (isAlone == 0) - isAlone = Date.now(); - else if (Date.now() > isAlone + funcs_1.Duration.minutes(2)) - exports.Achievements.alone.grantToAllOnline(); - } - else - isAlone = 0; -}, 2, 2); -var coreHealthTime = new Map(); -if (!config_1.Gamemode.sandbox()) - Timer.schedule(function () { - coreHealthTime.forEach(function (value, core) { - if (Date.now() > value) { - if (core.dead) { - coreHealthTime.delete(core); - } - else if (core.health > 50) { - //grant achievement - exports.Achievements.core_low_hp.grantToAllOnline(core.team); - players_1.FishPlayer.forEachPlayer(function (p) { - if (core.team != p.team() && !exports.Achievements.enemy_core_low_hp.has(p)) - exports.Achievements.enemy_core_low_hp.grantTo(p); - }); - coreHealthTime.delete(core); - } - } - }); - Vars.state.teams.active.flatMap(function (t) { return t.cores; }).each(function (core) { - if (core.health < 50 && !coreHealthTime.get(core)) - coreHealthTime.set(core, Date.now() + 12000); - }); - }, 1, 1); -var aroundTheWorld = {}; -Timer.schedule(function () { - var e_5, _a; - players_1.FishPlayer.forEachPlayer(function (p) { - var _a; - var _b; - var unit = p.unit(); - if (unit && unit.x < 0 && unit.y < 0) { - (_a = aroundTheWorld[_b = p.uuid]) !== null && _a !== void 0 ? _a : (aroundTheWorld[_b] = { player: p, unit: unit, side: "left" }); - } - }); - var _loop_5 = function (uuid, entry) { - if (!(function () { - if (entry.unit.dead) - return false; - var left = entry.unit.x < 0; - var bottom = entry.unit.y < 0; - var right = entry.unit.x > (Vars.world.width() - 1) * 8; - var top = entry.unit.y > (Vars.world.height() - 1) * 8; - switch (entry.side) { - case "left": - if (!left) - return false; - if (top) - entry.side = "top"; - break; - case "top": - if (!top) - return false; - if (right) - entry.side = "right"; - break; - case "right": - if (!right) - return false; - if (bottom) - entry.side = "bottom"; - break; - case "bottom": - if (!bottom) - return false; - if (left) { - exports.Achievements.around_the_world.grantTo(entry.player, true); - return false; - } - break; - } - return true; - })()) - delete aroundTheWorld[uuid]; - }; - try { - for (var _b = __values(Object.entries(aroundTheWorld)), _c = _b.next(); !_c.done; _c = _b.next()) { - var _d = __read(_c.value, 2), uuid = _d[0], entry = _d[1]; - _loop_5(uuid, entry); - } - } - catch (e_5_1) { e_5 = { error: e_5_1 }; } - finally { - try { - if (_c && !_c.done && (_a = _b.return)) _a.call(_b); - } - finally { if (e_5) throw e_5.error; } - } -}, 1, 0.5); -Events.on(EventType.GameOverEvent, function () { return coreHealthTime.clear(); }); -Events.on(EventType.WorldLoadEvent, function () { return coreHealthTime.clear(); }); -globals_1.FishEvents.on("scriptKiddie", function (_, p) { return Timer.schedule(function () { - if (!exports.Achievements.script_kiddie.has(p)) - exports.Achievements.script_kiddie.grantTo(p); -}, 2); }); -globals_1.FishEvents.on("memoryCorruption", function () { return exports.Achievements.memory_corruption.grantToAllOnline(); }); -globals_1.FishEvents.on("serverSays", function () { return exports.Achievements.server_speak.grantToAllOnline(); }); -var templateObject_1, templateObject_2; +"use strict"; +var __makeTemplateObject = (this && this.__makeTemplateObject) || function (cooked, raw) { + if (Object.defineProperty) { Object.defineProperty(cooked, "raw", { value: raw }); } else { cooked.raw = raw; } + return cooked; +}; +var __read = (this && this.__read) || function (o, n) { + var m = typeof Symbol === "function" && o[Symbol.iterator]; + if (!m) return o; + var i = m.call(o), r, ar = [], e; + try { + while ((n === void 0 || n-- > 0) && !(r = i.next()).done) ar.push(r.value); + } + catch (error) { e = { error: error }; } + finally { + try { + if (r && !r.done && (m = i["return"])) m.call(i); + } + finally { if (e) throw e.error; } + } + return ar; +}; +var __spreadArray = (this && this.__spreadArray) || function (to, from, pack) { + if (pack || arguments.length === 2) for (var i = 0, l = from.length, ar; i < l; i++) { + if (ar || !(i in from)) { + if (!ar) ar = Array.prototype.slice.call(from, 0, i); + ar[i] = from[i]; + } + } + return to.concat(ar || Array.prototype.slice.call(from)); +}; +var __values = (this && this.__values) || function(o) { + var s = typeof Symbol === "function" && Symbol.iterator, m = s && o[s], i = 0; + if (m) return m.call(o); + if (o && typeof o.length === "number") return { + next: function () { + if (o && i >= o.length) o = void 0; + return { value: o && o[i++], done: !o }; + } + }; + throw new TypeError(s ? "Object is not iterable." : "Symbol.iterator is not defined."); +}; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.Achievements = exports.Achievement = void 0; +var config_1 = require("/config"); +var funcs_1 = require("/funcs"); +var globals_1 = require("/globals"); +var players_1 = require("/players"); +var ranks_1 = require("/ranks"); +var utils_1 = require("/utils"); +//scrap doesn't count +var serpuloItems = [Items.copper, Items.lead, Items.graphite, Items.silicon, Items.metaglass, Items.titanium, Items.plastanium, Items.thorium, Items.surgeAlloy, Items.phaseFabric]; +var erekirItems = [Items.beryllium, Items.graphite, Items.silicon, Items.tungsten, Items.oxide, Items.surgeAlloy, Items.thorium, Items.carbide, Items.phaseFabric]; +var usefulItems10k = { + serpulo: serpuloItems.map(function (i) { return new ItemStack(i, 10000); }), + erekir: erekirItems.map(function (i) { return new ItemStack(i, 10000); }), + sun: __spreadArray(__spreadArray([], __read(serpuloItems), false), __read(erekirItems), false).map(function (i) { return new ItemStack(i, 10000); }), +}; +var allItems1k = Vars.content.items().select(function (i) { return !i.hidden; }).toArray().map(function (i) { return new ItemStack(i, 1000); }); +var mixtechItems = Items.serpuloItems.copy(); +Items.erekirItems.each(function (i) { return mixtechItems.add(i); }); +var Achievement = /** @class */ (function () { + function Achievement(icon, name, description, options) { + var _a; + if (options === void 0) { options = {}; } + this.name = name; + this.notify = "player"; + this.hidden = false; + this.disabled = false; + if (Array.isArray(icon)) { + this.icon = (icon[0].startsWith("[") ? icon[0] : "[".concat(icon[0], "]")) + (typeof icon[1] == "number" ? String.fromCharCode(icon[1]) : icon[1]); + } + else if (typeof icon == "number") { + this.icon = String.fromCharCode(icon); + } + else { + this.icon = icon; + } + if (Array.isArray(description)) { + _a = __read(description, 2), this.description = _a[0], this.extendedDescription = _a[1]; + } + else + this.description = description; + this.nid = Achievement._id++; + Object.assign(this, options); + if (options.modes) { + var _b = __read(options.modes), type = _b[0], modes_1 = _b.slice(1); + if (type == "only") { + this.allowedModes = modes_1; + this.modesText = modes_1.join(", "); + } + else { + this.allowedModes = config_1.GamemodeNames.filter(function (m) { return !modes_1.includes(m); }); + this.modesText = "all except ".concat(modes_1.join(", ")); + } + } + else { + this.allowedModes = config_1.GamemodeNames; + this.modesText = "all"; + } + if (!this.disabled) { + Achievement.all.push(this); + if (this.checkPlayerFrequent || this.checkFrequent) + Achievement.checkFrequent.push(this); + if (this.checkPlayerInfrequent || this.checkInfrequent) + Achievement.checkInfrequent.push(this); + if (this.checkPlayerJoin) + Achievement.checkJoin.push(this); + if (this.checkPlayerGameover || this.checkGameover) + Achievement.checkGameover.push(this); + } + } + Achievement.prototype.message = function () { + return config_1.FColor.achievement(templateObject_1 || (templateObject_1 = __makeTemplateObject(["Achievement granted!\n[accent]", "[white]: ", ""], ["Achievement granted!\\n[accent]", "[white]: ", ""])), this.name, this.description); + }; + Achievement.prototype.messageToEveryone = function (player) { + return config_1.FColor.achievement(templateObject_2 || (templateObject_2 = __makeTemplateObject(["Player ", " has completed the achievement \"", "\"."], ["Player ", " has completed the achievement \"", "\"."])), player.prefixedName, this.name); + }; + Achievement.prototype.allowedInMode = function () { + return this.allowedModes.includes(config_1.Gamemode.name()); + }; + Achievement.prototype.grantToAllOnline = function (team) { + var _this = this; + players_1.FishPlayer.forEachPlayer(function (p) { + if (!_this.has(p) && (!team || p.team() == team)) { + if (_this.notify != "nobody") + p.sendMessage(_this.message()); + _this.setObtained(p); + } + }); + }; + /** Do not call this in a loop on an achievement set to notify everyone. */ + Achievement.prototype.grantTo = function (player, allowRepeatMessage) { + if (allowRepeatMessage === void 0) { allowRepeatMessage = true; } + var has = this.has(player); + if (!has || allowRepeatMessage) { + if (this.notify == "everyone") + Call.sendMessage(this.messageToEveryone(player)); + else if (this.notify == "player") + player.sendMessage(this.message()); + } + if (!has) + this.setObtained(player); + }; + Achievement.prototype.setObtained = function (player) { + //void player.updateSynced(fishP => fishP.achievements.set(this.nid)); + player.achievements.set(this.nid); + }; + Achievement.prototype.has = function (player) { + return player.achievements.get(this.nid); + }; + Achievement.all = []; + /** Checked every second. */ + Achievement.checkFrequent = []; + /** Checked every 10 seconds. Use for states that can be gained but not lost, such as "x wins". */ + Achievement.checkInfrequent = []; + Achievement.checkJoin = []; + Achievement.checkGameover = []; + Achievement._id = 0; + return Achievement; +}()); +exports.Achievement = Achievement; +Events.on(EventType.PlayerJoin, function (_a) { + var e_1, _b; + var _c; + var player = _a.player; + Time.mark(); + var _loop_1 = function (ach) { + if (ach.allowedInMode()) { + var fishP_1 = players_1.FishPlayer.get(player); + if (!ach.has(fishP_1) && ((_c = ach.checkPlayerJoin) === null || _c === void 0 ? void 0 : _c.call(ach, fishP_1))) { + if (fishP_1.dataSynced) + ach.grantTo(fishP_1); + else + Timer.schedule(function () { return ach.grantTo(fishP_1); }, 2); //2 seconds should be enough + } + } + }; + try { + for (var _d = __values(Achievement.checkJoin), _e = _d.next(); !_e.done; _e = _d.next()) { + var ach = _e.value; + _loop_1(ach); + } + } + catch (e_1_1) { e_1 = { error: e_1_1 }; } + finally { + try { + if (_e && !_e.done && (_b = _d.return)) _b.call(_d); + } + finally { if (e_1) throw e_1.error; } + } + Log.debug("ach join @", Time.elapsed()); +}); +globals_1.FishEvents.on("gameOver", function (_, winner) { + var e_2, _a; + var _b; + Time.mark(); + var _loop_2 = function (ach) { + if (ach.allowedInMode()) { + if ((_b = ach.checkGameover) === null || _b === void 0 ? void 0 : _b.call(ach, winner)) + ach.grantToAllOnline(); + else + players_1.FishPlayer.forEachPlayer(function (fishP) { + var _a; + if (!ach.has(fishP) && ((_a = ach.checkPlayerGameover) === null || _a === void 0 ? void 0 : _a.call(ach, fishP, winner))) { + ach.grantTo(fishP); + } + }); + } + }; + try { + for (var _c = __values(Achievement.checkGameover), _d = _c.next(); !_d.done; _d = _c.next()) { + var ach = _d.value; + _loop_2(ach); + } + } + catch (e_2_1) { e_2 = { error: e_2_1 }; } + finally { + try { + if (_d && !_d.done && (_a = _c.return)) _a.call(_c); + } + finally { if (e_2) throw e_2.error; } + } + Log.debug("ach gameover @", Time.elapsed()); +}); +Timer.schedule(function () { + var e_3, _a; + Time.mark(); + var _loop_3 = function (ach) { + if (ach.allowedInMode()) { + if (ach.checkFrequent) { + if (config_1.Gamemode.pvp()) { + Vars.state.teams.active.each(function (_a) { + var team = _a.team; + if (ach.checkFrequent(team)) + ach.grantToAllOnline(team); + }); + } + else { + if (ach.checkFrequent(Vars.state.rules.defaultTeam)) + ach.grantToAllOnline(); + } + } + else { + players_1.FishPlayer.forEachPlayer(function (fishP) { + var _a; + if (!ach.has(fishP) && ((_a = ach.checkPlayerFrequent) === null || _a === void 0 ? void 0 : _a.call(ach, fishP))) + ach.grantTo(fishP); + }); + } + } + }; + try { + for (var _b = __values(Achievement.checkFrequent), _c = _b.next(); !_c.done; _c = _b.next()) { + var ach = _c.value; + _loop_3(ach); + } + } + catch (e_3_1) { e_3 = { error: e_3_1 }; } + finally { + try { + if (_c && !_c.done && (_a = _b.return)) _a.call(_b); + } + finally { if (e_3) throw e_3.error; } + } + Log.debug("ach frequent @", Time.elapsed()); +}, 1, 1); +Timer.schedule(function () { + var e_4, _a; + Time.mark(); + var _loop_4 = function (ach) { + if (ach.allowedInMode()) { + if (ach.checkInfrequent) { + if (config_1.Gamemode.pvp()) { + Vars.state.teams.active.each(function (_a) { + var team = _a.team; + if (ach.checkInfrequent(team)) + ach.grantToAllOnline(team); + }); + } + else { + if (ach.checkInfrequent(Vars.state.rules.defaultTeam)) + ach.grantToAllOnline(); + } + } + else { + players_1.FishPlayer.forEachPlayer(function (fishP) { + var _a; + if (!ach.has(fishP) && ((_a = ach.checkPlayerInfrequent) === null || _a === void 0 ? void 0 : _a.call(ach, fishP))) + ach.grantTo(fishP); + }); + } + } + }; + try { + for (var _b = __values(Achievement.checkInfrequent), _c = _b.next(); !_c.done; _c = _b.next()) { + var ach = _c.value; + _loop_4(ach); + } + } + catch (e_4_1) { e_4 = { error: e_4_1 }; } + finally { + try { + if (_c && !_c.done && (_a = _b.return)) _a.call(_b); + } + finally { if (e_4) throw e_4.error; } + } + Log.debug("ach infrequent @", Time.elapsed()); +}, 10, 10); +exports.Achievements = { + // =========================== + // ╦ ╦ ╔═╗ ╦═╗ ╔╗╔ ╦ ╔╗╔ ╔═╗ ┬ + // ║║║ ╠═╣ ╠╦╝ ║║║ ║ ║║║ ║ ╦ │ + // ╚╩╝ ╩ ╩ ╩╚═ ╝╚╝ ╩ ╝╚╝ ╚═╝ o + // =========================== + // Do not change the order of any achievements. + // Do not remove any achievements: instead, set the "disabled" option to true. + // Reordering achievements will cause ID shifts. + //Joining based + welcome: new Achievement(["gold", Iconc.infoCircle], "Welcome", "Join the server.", { + checkPlayerJoin: function () { return true; }, + notify: "nobody" + }), + migratory_fish: new Achievement(Iconc.exit, "Migratory Fish", "Join all of our servers.", { + disabled: true + }), //TODO + frequent_visitor: new Achievement(Iconc.planeOutline, "Frequent Visitor", ["Join the server 100 times.", "Note: Do not reconnect frequently, that will not work. This achievement requires that you have been playing for 1 month."], { + checkPlayerJoin: function (p) { return p.info().timesJoined >= 100 && (Date.now() - p.globalFirstJoined > funcs_1.Duration.months(1)); } + }), + //Gamemode based + attack: new Achievement(Iconc.modeAttack, "Attack", ["Defeat an attack map.", "You must be present for the beginning and end of the game."], { + modes: ["only", "attack"], + checkPlayerGameover: function (player, winTeam) { + return Vars.state.rules.defaultTeam == winTeam && player.tstats.lastMapStartTime == players_1.FishPlayer.lastMapStartTime; + }, + }), + survival: new Achievement(Iconc.modeSurvival, "Survival", ["Survive 50 waves in a survival map.", "Must be during the same game."], { + modes: ["only", "survival"], + checkPlayerInfrequent: function (player) { + return player.tstats.wavesSurvived >= 50; + }, + }), + pvp: new Achievement(Iconc.modePvp, "PVP", ["Win a match of PVP.", "You must be present for the beginning and end of the game."], { + modes: ["only", "pvp"], + checkPlayerGameover: function (player, winTeam) { + return player.team() == winTeam && player.tstats.lastMapStartTime == players_1.FishPlayer.lastMapStartTime; + }, + }), + sandbox: new Achievement(Iconc.image, "Sandbox", "Spend 1 hour in Sandbox.", { + modes: ["only", "sandbox"], + checkPlayerInfrequent: function (p) { return p.stats.timeInGame > funcs_1.Duration.hours(1); }, + }), + hexed: new Achievement(Iconc.layers, "Hexed", ["Play a match of Hexed.", "You must be present for the beginning and end of the game."], { + modes: ["only", "hexed"], + checkPlayerGameover: function (player) { + return player.tstats.lastMapStartTime == players_1.FishPlayer.lastMapStartTime; + }, + }), + minigame: new Achievement(Iconc.play, "Minigame", ["Win a Minigame.", "You must be present for the beginning and end of the game."], { + modes: ["only", "minigame"], + checkPlayerGameover: function (player, winTeam) { + return player.team() == winTeam && player.tstats.lastMapStartTime == players_1.FishPlayer.lastMapStartTime; + }, + }), + //playtime based + playtime_1: new Achievement(["white", Iconc.googleplay], "Playtime 1", "Spend 1 hour in-game.", { + checkPlayerInfrequent: function (p) { return p.globalStats.timeInGame >= funcs_1.Duration.hours(1); } + }), + playtime_2: new Achievement(["red", Iconc.googleplay], "Playtime 2", "Spend 12 hours in-game.", { + checkPlayerInfrequent: function (p) { return p.globalStats.timeInGame >= funcs_1.Duration.hours(12); } + }), + playtime_3: new Achievement(["orange", Iconc.googleplay], "Playtime 3", "Spend 2 days in-game.", { + checkPlayerInfrequent: function (p) { return p.globalStats.timeInGame >= funcs_1.Duration.days(2); } + }), + playtime_4: new Achievement(["yellow", Iconc.googleplay], "Playtime 4", "Spend 10 days in-game.", { + checkPlayerInfrequent: function (p) { return p.globalStats.timeInGame >= funcs_1.Duration.days(10); } + }), + //victories based + victory_1: new Achievement(["white", Iconc.star], "First Victory", "Win a map run.", { + checkPlayerGameover: function (p) { return p.globalStats.gamesWon >= 1; } + }), + victory_2: new Achievement(["red", Iconc.star], "Victories 2", "Win 5 map runs.", { + checkPlayerGameover: function (p) { return p.globalStats.gamesWon >= 5; } + }), + victory_3: new Achievement(["orange", Iconc.star], "Victories 3", "Win 30 map runs.", { + checkPlayerGameover: function (p) { return p.globalStats.gamesWon >= 30; } + }), + victory_4: new Achievement(["yellow", Iconc.star], "Victories 4", "Win 100 map runs.", { + checkPlayerGameover: function (p) { return p.globalStats.gamesWon >= 100; }, + notify: "everyone" + }), + //games based + games_1: new Achievement(["white", Iconc.itchio], "Games 1", "Play 10 map runs.", { + checkPlayerGameover: function (p) { return p.globalStats.gamesFinished >= 10; } + }), + games_2: new Achievement(["red", Iconc.itchio], "Games 2", "Play 40 map runs.", { + checkPlayerGameover: function (p) { return p.globalStats.gamesFinished >= 40; } + }), + games_3: new Achievement(["orange", Iconc.itchio], "Games 3", "Play 100 map runs.", { + checkPlayerGameover: function (p) { return p.globalStats.gamesFinished >= 100; } + }), + games_4: new Achievement(["yellow", Iconc.itchio], "Games 4", "Play 200 map runs.", { + checkPlayerGameover: function (p) { return p.globalStats.gamesFinished >= 200; }, + notify: "everyone" + }), + //messages based + messages_1: new Achievement(["white", Iconc.chat], "Hello", "Send your first chat message.", { + checkPlayerInfrequent: function (p) { return p.globalStats.chatMessagesSent >= 1; }, + notify: "nobody" + }), + messages_2: new Achievement(["red", Iconc.chat], "Chat 2", ["Send 100 chat messages.", "Warning: you will be kicked if you spam the chat."], { + checkPlayerInfrequent: function (p) { return p.globalStats.chatMessagesSent >= 100; } + }), + messages_3: new Achievement(["orange", Iconc.chat], "Chat 3", ["Send 500 chat messages.", "Warning: you will be kicked if you spam the chat."], { + checkPlayerInfrequent: function (p) { return p.globalStats.chatMessagesSent >= 500; } + }), + messages_4: new Achievement(["yellow", Iconc.chat], "Chat 4", ["Send 2000 chat messages.", "Warning: you will be kicked if you spam the chat."], { + checkPlayerInfrequent: function (p) { return p.globalStats.chatMessagesSent >= 2000; } + }), + messages_5: new Achievement(["lime", Iconc.chat], "Chat 4", ["Send 5000 chat messages.", "Warning: you will be kicked if you spam the chat."], { + checkPlayerInfrequent: function (p) { return p.globalStats.chatMessagesSent >= 5000; }, + notify: "everyone" + }), + //blocks built based + builds_1: new Achievement(["white", Iconc.fileText], "The Factory Must Prepare", "Construct 1 buildings.", { + checkPlayerInfrequent: function (p) { return p.globalStats.blocksPlaced >= 1; }, + notify: "nobody" + }), + builds_2: new Achievement(["red", Iconc.fileText], "The Factory Must Begin", "Construct 200 buildings.", { + checkPlayerInfrequent: function (p) { return p.globalStats.blocksPlaced > 200; } + }), + builds_3: new Achievement(["orange", Iconc.fileText], "The Factory Must Produce", "Construct 1000 buildings.", { + checkPlayerInfrequent: function (p) { return p.globalStats.blocksPlaced > 1000; } + }), + builds_4: new Achievement(["yellow", Iconc.fileText], "The Factory Must Grow", "Construct 5000 buildings.", { + checkPlayerInfrequent: function (p) { return p.globalStats.blocksPlaced > 5000; }, + }), + //units + t5: new Achievement(Blocks.tetrativeReconstructor.emoji(), "T5", "Control a T5 unit.", { + modes: ["not", "sandbox"], + checkPlayerFrequent: function (player) { + var _a; + return globals_1.unitsT5.includes((_a = player.unit()) === null || _a === void 0 ? void 0 : _a.type); + }, + }), + dibs: new Achievement(["green", Blocks.tetrativeReconstructor.emoji()], "Dibs", "Be the first player to control the first T5 unit made by a reconstructor that you placed.", { + modes: ["not", "sandbox"], + disabled: true + }), //TODO + worm: new Achievement(UnitTypes.latum.emoji(), "Worm", "Control a Latum.", { + checkPlayerFrequent: function (player) { + var _a; + return ((_a = player.unit()) === null || _a === void 0 ? void 0 : _a.type) == UnitTypes.latum; + } + }), + //pvp + above_average: new Achievement(Iconc.chartBar, "Above Average", ["Reach a win rate above 50%.", "Must be over at least 20 games of PVP."], { + modes: ["only", "pvp"], + checkPlayerInfrequent: function (p) { return p.stats.gamesWon / p.stats.gamesFinished > 0.5 && p.stats.gamesFinished >= 20; } + }), + head_start: new Achievement(Iconc.commandAttack, "Head Start", ["Win a match of PVP where your opponents have a 5 minute head start.", "Your team must wait for the first 5 minutes without building or descontructing any buildings."], { + modes: ["only", "pvp"], + disabled: true + }), //TODO + one_v_two: new Achievement(["red", Iconc.modePvp], "1v2", "Defeat two (or more) opponents in PVP without help from other players.", { + modes: ["only", "pvp"], + disabled: true + }), //TODO + //sandbox + underpowered: new Achievement(["red", Blocks.powerSource.emoji()], "Underpowered", "Overload a power source.", { + modes: ["only", "sandbox"], + checkFrequent: function () { + var found = false; + //deliberate ordering for performance reasons + Groups.powerGraph.each(function (_a) { + var graph = _a.graph; + //we don't need to actually check for power sources, just assume that ~1mil power is a source + if (graph.lastPowerNeeded > graph.lastPowerProduced && graph.lastPowerNeeded < 1e10 && (graph.lastPowerProduced / Time.delta * 60) >= 999900) + found = true; + }); + return found; + } + }), + //easter eggs + memory_corruption: new Achievement(["red", Iconc.host], "Is the server OK?", "Witness a memory corruption.", { + notify: "nobody" + }), + run_js_without_perms: new Achievement(["yellow", Iconc.warning], "XKCD 838", ["Receive a warning from the server that an incident will be reported.", "One of the admin commands has a custom error message."], { + notify: "everyone" + }), + script_kiddie: new Achievement(["red", Iconc.warning], "Script Kiddie", ["Pretend to be a hacker. The server will disagree.", "Change your name to something including \"hacker\"."], { + notify: "nobody" + }), + hacker: new Achievement(["lightgray", Iconc.host], "Hacker", "Find a bug in the server and report it responsibly.", { + hidden: true + }), + //items based + items_10k: new Achievement(["green", Iconc.distribution], "Cornucopia", "Obtain 10k of every useful resource.", { + modes: ["not", "sandbox"], + checkPlayerFrequent: function (player) { + var _a; + if (!Vars.state.planet) + return false; + return ((_a = player.team().items()) === null || _a === void 0 ? void 0 : _a.has(usefulItems10k[Vars.state.planet.name])) || false; + }, + }), + fullVault: new Achievement(["green", Blocks.vault.emoji()], "Well Stocked", ["Fill a vault with every obtainable item.", "Requires mixtech."], { + modes: ["not", "sandbox"], + checkInfrequent: function (team) { + return Vars.indexer.getFlagged(team, BlockFlag.storage).contains(boolf(function (b) { return b.block == Blocks.vault && b.items.has(allItems1k) && b.linkedCore == null; })); + }, + }), + full_core: new Achievement(["green", Blocks.coreAcropolis.emoji()], "Multiblock Incinerator", "Completely fill the core with all obtainable items on a map with core incineration enabled.", { + modes: ["not", "sandbox"], + checkFrequent: function (team) { + var _a; + if (!Vars.state.planet) + return false; + var items; + switch (Vars.state.planet.name) { + case "serpulo": + items = Items.serpuloItems; + break; + case "erekir": + items = Items.erekirItems; + break; + case "sun": + items = mixtechItems; + break; + } + var capacity = (_a = team.core()) === null || _a === void 0 ? void 0 : _a.storageCapacity; + if (!capacity) + return false; + var module = team.items(); + return items.allMatch(function (i) { return module.has(i, capacity); }); + }, + }), + siligone: new Achievement(["red", Items.silicon.emoji()], "Siligone", ["Run out of silicon.", "You must have reached 2000 silicon before running out."], { + modes: ["not", "sandbox"] + }), + silicon_100k: new Achievement(["green", Items.silicon.emoji()], "Silicon for days", "Obtain 100k silicon.", { + modes: ["not", "sandbox"], + checkFrequent: function (team) { return team.items().has(Items.silicon, 100000); } + }), + //other players based + alone: new Achievement(["red", Iconc.players], "Alone", "Be the only player online for more than two minutes", { + notify: "nobody" + }), + join_playercount_20: new Achievement(["lime", Iconc.players], "Is there enough room?", "Join a server with 20 players online", { + checkPlayerJoin: function () { return Groups.player.size() > 20; }, + }), + meet_staff: new Achievement(["lime", Iconc.hammer], "Griefer Beware", "Meet a staff member in-game", { + checkPlayerJoin: function () { return Groups.player.contains(function (p) { return players_1.FishPlayer.get(p).ranksAtLeast("mod"); }); }, + }), + meet_fish: new Achievement(["blue", Iconc.admin], "The Big Fish", "Meet >|||>Fish himself in-game", { + checkPlayerJoin: function () { return Groups.player.contains(function (p) { return players_1.FishPlayer.get(p).ranksAtLeast("fish"); }); }, + hidden: true, + }), + server_speak: new Achievement(["pink", Iconc.host], "It Speaks!", "Hear the server talk in chat."), + see_marked_griefer: new Achievement(["red", Iconc.hammer], "Flying Tonk", "See a marked griefer in-game.", { + checkInfrequent: function () { return Groups.player.contains(function (p) { return players_1.FishPlayer.get(p).marked(); }); }, + }), + //maps based + beat_map_not_in_rotation: new Achievement(["pink", Iconc.map], "How?", "Beat a map that isn't in the list of maps.", { + notify: "everyone", + modes: ["not", "pvp"], + checkGameover: function (team) { return team == Vars.state.rules.defaultTeam && !Vars.state.map.custom; } + }), + //misc + power_1mil: new Achievement(["green", Blocks.powerSource.emoji()], "Who needs sources?", "Reach a power production of 1 million without using power sources.", { + modes: ["not", "sandbox"], + checkFrequent: function (team) { + var found = false; + //deliberate ordering for performance reasons + Groups.powerGraph.each(function (_a) { + var _b; + var graph = _a.graph; + //we need to actually check for power sources + if ((graph.lastPowerProduced / Time.delta * 60) > 1e6 && + !graph.producers.contains(boolf(function (b) { return b.block == Blocks.powerSource; })) && + ((_b = graph.producers.firstOpt()) === null || _b === void 0 ? void 0 : _b.team) == team) + found = true; + }); + return found; + } + }), + pacifist_crawler: new Achievement(UnitTypes.crawler.emoji(), "Pacifist Crawler", "Control a crawler for 15 minutes without exploding.", { + modes: ["not", "sandbox"], + disabled: true + }), //TODO + core_low_hp: new Achievement(["yellow", Blocks.coreNucleus.emoji()], "Close Call", "Have your core reach less than 50 health, but survive.", { + modes: ["not", "sandbox"], + }), + enemy_core_low_hp: new Achievement(["red", Blocks.coreNucleus.emoji()], "So Close", "Cause the enemy core to reach less than 50 health, but survive.", { + modes: ["not", "sandbox"], + }), + verified: new Achievement([ranks_1.Rank.active.color, Iconc.ok], "Verified", "Be promoted automatically to ".concat(ranks_1.Rank.active.coloredName(), " rank."), { + checkPlayerJoin: function (p) { return p.ranksAtLeast("active"); }, notify: "nobody" + }), + click_me: new Achievement(Iconc.bookOpen, "Clicked", "Run /achievementgrid and click this achievement."), + afk: new Achievement(["yellow", Iconc.lock], "AFK?", "Win a game without interacting with any blocks.", { + modes: ["not", "sandbox"], + checkPlayerGameover: function (player, winTeam) { + return player.team() == winTeam && player.tstats.blockInteractionsThisMap == 0; + }, + }), + status_effects_5: new Achievement(StatusEffects.electrified.emoji(), "A Furious Cocktail", "Have at least 5 status effects at once.", { + checkPlayerFrequent: function (p) { + var unit = p.unit(); + if (!unit) + return false; + var statuses = (0, utils_1.getStatuses)(unit); + return statuses.size >= 5; + }, + modes: ["not", "sandbox"] + }), + drown_big_tank: new Achievement(["blue", UnitTypes.conquer.emoji()], "Not Waterproof", "Drown an enemy Conquer or Vanquish.", { + notify: "everyone", + modes: ["not", "sandbox"] + }), + drown_mace_in_cryo: new Achievement(["cyan", UnitTypes.mace.emoji()], "Cooldown", "Drown a Mace in ".concat(Blocks.cryofluid.emoji(), " Cryofluid."), { + notify: "everyone", + modes: ["not", "sandbox"] + }), + max_boost_duo: new Achievement(["yellow", Blocks.duo.emoji()], "In Duo We Trust", "Control a Duo with maximum boosts.", { + checkPlayerFrequent: function (player) { + var _a, _b; + var tile = (_b = (_a = player.unit()) === null || _a === void 0 ? void 0 : _a.tile) === null || _b === void 0 ? void 0 : _b.call(_a); + if (!tile) + return false; + return tile.block == Blocks.duo && !tile.ammo.isEmpty() && tile.ammo.peek().item == Items.silicon && tile.liquids.current() == Liquids.cryofluid && tile.timeScale() >= 2.5; + }, + notify: "everyone", + modes: ["not", "sandbox"] + }), + foreshadow_overkill: new Achievement(["yellow", Blocks.foreshadow.emoji()], "Overkill", ["Kill a Dagger with a maximally boosted Foreshadow.", "Hint: the maximum overdrive is not +150%..."], { + notify: "everyone", + modes: ["not", "sandbox"] + }), + impacts_15: new Achievement(["green", Blocks.impactReactor.emoji()], "Darthscion's Nightmare", "Run 15 impact reactors at full efficiency.", { + modes: ["not", "sandbox"], + notify: "everyone", + checkInfrequent: function (team) { + var found = false; + //deliberate ordering for performance reasons + Groups.powerGraph.each(function (_a) { + var graph = _a.graph; + if (graph.producers.size >= 15 && graph.producers.count(function (b) { return b.block == Blocks.impactReactor && b.warmup > 0.99999; }) > 15 && graph.producers.first().team == team) + found = true; + }); + return found; + }, + }), + help_help: new Achievement(["brown", Iconc.info], "Help with help", "Run /help help", { + notify: "everyone" + }), + ohno: new Achievement(["scarlet", UnitTypes.alpha.emoji()], "Oh no", "Control an ohno unit."), + sniper_duel: new Achievement(["yellow", UnitTypes.omura.emoji()], "Sniper duel", "Kill a Foreshadow with an Omura from outside its range."), + around_the_world: new Achievement(Iconc.planet, "Around the World", "Fly your unit around the entire map without entering it, starting from the lower left.", { + notify: "everyone", + }), +}; +Object.entries(exports.Achievements).forEach(function (_a) { + var _b = __read(_a, 2), id = _b[0], a = _b[1]; + return a.sid = id; +}); +globals_1.FishEvents.on("commandUnauthorized", function (_, player, name) { + if ((name == "js" || name == "fjs") && !exports.Achievements.run_js_without_perms.has(player)) + exports.Achievements.run_js_without_perms.grantTo(player); +}); +Events.on(EventType.UnitDrownEvent, function (_a) { + var _b; + var unit = _a.unit; + if (!config_1.Gamemode.sandbox()) { + if (unit.type == UnitTypes.mace && ((_b = unit.tileOn()) === null || _b === void 0 ? void 0 : _b.floor()) == Blocks.cryofluid) + exports.Achievements.drown_mace_in_cryo.grantToAllOnline(); + else if (unit.type == UnitTypes.conquer || unit.type == UnitTypes.vanquish) { + if (config_1.Gamemode.pvp()) { + Vars.state.teams.active.map(function (t) { return t.team; }).select(function (t) { return t !== unit.team; }).each(function (t) { return exports.Achievements.drown_big_tank.grantToAllOnline(t); }); + } + else { + if (unit.team !== Vars.state.rules.defaultTeam) + exports.Achievements.drown_big_tank.grantToAllOnline(); + } + } + } +}); +Events.on(EventType.UnitBulletDestroyEvent, function (_a) { + var _b; + var unit = _a.unit, bullet = _a.bullet; + if (!config_1.Gamemode.sandbox() && unit.type == UnitTypes.dagger && ((_b = bullet.owner) === null || _b === void 0 ? void 0 : _b.block) == Blocks.foreshadow) { + var build = bullet.owner; + if (build.liquids.current() == Liquids.cryofluid && build.timeScale() >= 3) + exports.Achievements.foreshadow_overkill.grantToAllOnline(build.team); + } +}); +Events.on(EventType.BuildingBulletDestroyEvent, function (_a) { + var _b; + var build = _a.build, bullet = _a.bullet; + if (!config_1.Gamemode.sandbox() && build.block == Blocks.foreshadow && ((_b = bullet.owner) === null || _b === void 0 ? void 0 : _b.type) == UnitTypes.omura) { + var unit = bullet.owner; + var player = unit.getPlayer(); + if (player && !unit.within(build, build.range() + unit.hitSize / 2)) + exports.Achievements.sniper_duel.grantTo(players_1.FishPlayer.get(player)); + } +}); +var siliconReached = Team.all.map(function (_) { return false; }); +Events.on(EventType.GameOverEvent, function () { return siliconReached = Team.all.map(function (_) { return false; }); }); +var isAlone = 0; +Timer.schedule(function () { + if (!Vars.state.gameOver && !config_1.Gamemode.sandbox()) { + Vars.state.teams.active.each(function (_a) { + var team = _a.team; + if (team.items().has(Items.silicon, 2000)) + siliconReached[team.id] = true; + else if (siliconReached[team.id] && team.items().get(Items.silicon) == 0) + exports.Achievements.siligone.grantToAllOnline(team); + }); + } + if (Groups.player.size() == 1) { + if (isAlone == 0) + isAlone = Date.now(); + else if (Date.now() > isAlone + funcs_1.Duration.minutes(2)) + exports.Achievements.alone.grantToAllOnline(); + } + else + isAlone = 0; +}, 2, 2); +var coreHealthTime = new Map(); +if (!config_1.Gamemode.sandbox()) + Timer.schedule(function () { + coreHealthTime.forEach(function (value, core) { + if (Date.now() > value) { + if (core.dead) { + coreHealthTime.delete(core); + } + else if (core.health > 50) { + //grant achievement + exports.Achievements.core_low_hp.grantToAllOnline(core.team); + players_1.FishPlayer.forEachPlayer(function (p) { + if (core.team != p.team() && !exports.Achievements.enemy_core_low_hp.has(p)) + exports.Achievements.enemy_core_low_hp.grantTo(p); + }); + coreHealthTime.delete(core); + } + } + }); + Vars.state.teams.active.flatMap(function (t) { return t.cores; }).each(function (core) { + if (core.health < 50 && !coreHealthTime.get(core)) + coreHealthTime.set(core, Date.now() + 12000); + }); + }, 1, 1); +var aroundTheWorld = {}; +Timer.schedule(function () { + var e_5, _a; + players_1.FishPlayer.forEachPlayer(function (p) { + var _a; + var _b; + var unit = p.unit(); + if (unit && unit.x < 0 && unit.y < 0) { + (_a = aroundTheWorld[_b = p.uuid]) !== null && _a !== void 0 ? _a : (aroundTheWorld[_b] = { player: p, unit: unit, side: "left" }); + } + }); + var _loop_5 = function (uuid, entry) { + if (!(function () { + if (entry.unit.dead) + return false; + var left = entry.unit.x < 0; + var bottom = entry.unit.y < 0; + var right = entry.unit.x > (Vars.world.width() - 1) * 8; + var top = entry.unit.y > (Vars.world.height() - 1) * 8; + switch (entry.side) { + case "left": + if (!left) + return false; + if (top) + entry.side = "top"; + break; + case "top": + if (!top) + return false; + if (right) + entry.side = "right"; + break; + case "right": + if (!right) + return false; + if (bottom) + entry.side = "bottom"; + break; + case "bottom": + if (!bottom) + return false; + if (left) { + exports.Achievements.around_the_world.grantTo(entry.player, true); + return false; + } + break; + } + return true; + })()) + delete aroundTheWorld[uuid]; + }; + try { + for (var _b = __values(Object.entries(aroundTheWorld)), _c = _b.next(); !_c.done; _c = _b.next()) { + var _d = __read(_c.value, 2), uuid = _d[0], entry = _d[1]; + _loop_5(uuid, entry); + } + } + catch (e_5_1) { e_5 = { error: e_5_1 }; } + finally { + try { + if (_c && !_c.done && (_a = _b.return)) _a.call(_b); + } + finally { if (e_5) throw e_5.error; } + } +}, 1, 0.5); +Events.on(EventType.GameOverEvent, function () { return coreHealthTime.clear(); }); +Events.on(EventType.WorldLoadEvent, function () { return coreHealthTime.clear(); }); +globals_1.FishEvents.on("scriptKiddie", function (_, p) { return Timer.schedule(function () { + if (!exports.Achievements.script_kiddie.has(p)) + exports.Achievements.script_kiddie.grantTo(p); +}, 2); }); +globals_1.FishEvents.on("memoryCorruption", function () { return exports.Achievements.memory_corruption.grantToAllOnline(); }); +globals_1.FishEvents.on("serverSays", function () { return exports.Achievements.server_speak.grantToAllOnline(); }); +var templateObject_1, templateObject_2; diff --git a/build/scripts/api.js b/build/scripts/api.js index e479452f..8196294e 100644 --- a/build/scripts/api.js +++ b/build/scripts/api.js @@ -1,214 +1,214 @@ -"use strict"; -/* -Copyright © BalaM314, 2026. All Rights Reserved. -This file contains wrappers over the API calls to the backend server. -*/ -Object.defineProperty(exports, "__esModule", { value: true }); -exports.isVpn = isVpn; -exports.isVpnCached = isVpnCached; -exports.sendModerationMessage = sendModerationMessage; -exports.getStaffMessages = getStaffMessages; -exports.sendStaffMessage = sendStaffMessage; -exports.ban = ban; -exports.unban = unban; -exports.getBanned = getBanned; -exports.getFishPlayerData = getFishPlayerData; -exports.setFishPlayerData = setFishPlayerData; -var config_1 = require("/config"); -var players_1 = require("/players"); -var promise_1 = require("/promise"); -var cachedIps = {}; -/** Make an API request to see if an IP is likely VPN. */ -function isVpn(ip, callback, callbackError) { - if (ip in cachedIps) - return callback(cachedIps[ip]); - Http.get("http://ip-api.com/json/".concat(ip, "?fields=proxy,hosting"), function (res) { - var data = res.getResultAsString(); - var json = JSON.parse(data); - var isVpn = json.proxy || json.hosting; - cachedIps[ip] = isVpn; - players_1.FishPlayer.stats.numIpsChecked++; - if (isVpn) - players_1.FishPlayer.stats.numIpsFlagged++; - callback(isVpn); - }, callbackError !== null && callbackError !== void 0 ? callbackError : (function (err) { - Log.err("[API] Network error when trying to call api.isVpn()"); - players_1.FishPlayer.stats.numIpsErrored++; - callback(false); - })); -} -function isVpnCached(ip) { - return cachedIps[ip]; -} -/** Send text to the moderation logs channel in Discord. */ -function sendModerationMessage(message) { - if (config_1.Mode.noBackend) { - Log.info("Sent moderation log message: ".concat(message)); - return; - } - var req = Http.post("http://".concat(config_1.backendIP, "/api/mod-dump"), JSON.stringify({ message: message })).header('Content-Type', 'application/json').header('Accept', '*/*'); - req.timeout = 10000; - req.error(function () { return Log.err("[API] Network error when trying to call api.sendModerationMessage()"); }); - req.submit(function (response) { - //Log.info(response.getResultAsString()); - }); -} -/** Get staff messages from discord. */ -function getStaffMessages(callback) { - if (config_1.Mode.noBackend) - return; - var req = Http.post("http://".concat(config_1.backendIP, "/api/getStaffMessages"), JSON.stringify({ server: config_1.Gamemode.name() })) - .header('Content-Type', 'application/json').header('Accept', '*/*'); - req.timeout = 10000; - req.error(function () { return Log.err("[API] Network error when trying to call api.getStaffMessages()"); }); - req.submit(function (response) { - var temp = response.getResultAsString(); - if (!temp.length) - Log.err("[API] Network error(empty response) when trying to call api.getStaffMessages()"); - else - callback(JSON.parse(temp).messages); - }); -} -/** Send staff messages from server. */ -function sendStaffMessage(message, playerName, isStaff, callback) { - if (config_1.Mode.noBackend) - return; - var req = Http.post("http://".concat(config_1.backendIP, "/api/sendStaffMessage"), - // need to send both name variants so one can be sent to the other servers with color and discord can use the clean one - JSON.stringify({ message: message, playerName: playerName, cleanedName: Strings.stripColors(playerName), server: config_1.Gamemode.name(), isStaff: isStaff })).header('Content-Type', 'application/json').header('Accept', '*/*'); - req.timeout = 10000; - req.error(function () { - Log.err("[API] Network error when trying to call api.sendStaffMessage()"); - callback === null || callback === void 0 ? void 0 : callback(false); - }); - req.submit(function (response) { - var temp = response.getResultAsString(); - if (!temp.length) - Log.err("[API] Network error(empty response) when trying to call api.sendStaffMessage()"); - else - callback === null || callback === void 0 ? void 0 : callback(JSON.parse(temp).data); - }); -} -/** Bans the provided ip and/or uuid. */ -function ban(data, callback) { - if (callback === void 0) { callback = function () { }; } - if (config_1.Mode.noBackend) - return; - var req = Http.post("http://".concat(config_1.backendIP, "/api/ban"), JSON.stringify(data)) - .header('Content-Type', 'application/json') - .header('Accept', '*/*'); - req.timeout = 10000; - req.error(function () { return Log.err("[API] Network error when trying to call api.ban(".concat(data.ip, ", ").concat(data.uuid, ")")); }); - req.submit(function (response) { - var str = response.getResultAsString(); - if (!str.length) - return Log.err("[API] Network error(empty response) when trying to call api.ban()"); - callback(JSON.parse(str).data); - }); -} -/** Unbans the provided ip and/or uuid. */ -function unban(data, callback) { - if (callback === void 0) { callback = function () { }; } - if (config_1.Mode.noBackend) - return; - var req = Http.post("http://".concat(config_1.backendIP, "/api/unban"), JSON.stringify(data)) - .header('Content-Type', 'application/json') - .header('Accept', '*/*'); - req.timeout = 10000; - req.error(function () { return Log.err("[API] Network error when trying to call api.ban({".concat(data.ip, ", ").concat(data.uuid, "})")); }); - req.submit(function (response) { - var str = response.getResultAsString(); - if (!str.length) - return Log.err("[API] Network error(empty response) when trying to call api.unban()"); - var parsedData = JSON.parse(str); - callback(parsedData.status, parsedData.error); - }); -} -/** Gets if either the provided uuid or ip is banned. */ -function getBanned(data, callback) { - if (config_1.Mode.noBackend) { - Log.info("[API] Attempted to getBanned(".concat(data.uuid, "/").concat(data.ip, "), assuming false due to local debug")); - callback(false); - return; - } - //TODO cache 4s - var req = Http.post("http://".concat(config_1.backendIP, "/api/checkIsBanned"), JSON.stringify(data)) - .header('Content-Type', 'application/json') - .header('Accept', '*/*'); - req.timeout = 10000; - req.error(function () { return Log.err("[API] Network error when trying to call api.getBanned()"); }); - req.submit(function (response) { - var str = response.getResultAsString(); - if (!str.length) - return Log.err("[API] Network error(empty response) when trying to call api.getBanned()"); - callback(JSON.parse(str).data); - }); -} -/** - * Fetches fish player data from the backend. - **/ -function getFishPlayerData(uuid) { - var _a = promise_1.Promise.withResolvers(), promise = _a.promise, resolve = _a.resolve, reject = _a.reject; - function fail(err) { - Log.err("[API] Network error when trying to call api.getFishPlayerData()"); - if (err) - Log.err(err); - reject(err); - } - if (config_1.Mode.noBackend) { - reject("local debug mode"); - return promise; - } - var req = Http.post("http://".concat(config_1.backendIP, "/api/fish-player"), JSON.stringify({ - id: uuid, - gamemode: config_1.Gamemode.name(), - })) - .header('Content-Type', 'application/json') - .header('Accept', '*/*'); - req.timeout = 10000; - req.error(fail); - req.submit(function (response) { - var data = response.getResultAsString(); - if (data) { - var result = JSON.parse(data); - if (!result || typeof result != "object") - fail("Invalid fish player data"); - resolve(result); - } - else { - resolve(null); - } - }); - return promise; -} -/** Pushes fish player data to the backend. */ -function setFishPlayerData(data, repeats, ignoreActivelySyncedFields) { - var _a = promise_1.Promise.withResolvers(), promise = _a.promise, resolve = _a.resolve, reject = _a.reject; - if (config_1.Mode.noBackend) { - resolve(); - return promise; - } - var req = Http.post("http://".concat(config_1.backendIP, "/api/fish-player/set"), JSON.stringify({ - player: data, - gamemode: config_1.Gamemode.name(), - ignoreActivelySyncedFields: ignoreActivelySyncedFields, - })) - .header('Content-Type', 'application/json') - .header('Accept', '*/*'); - req.timeout = 10000; - req.error(function (err) { - var _a, _b; - Log.err("[API] Network error when trying to call api.setFishPlayerData(), repeats=".concat(repeats)); - Log.err(err); - if (err === null || err === void 0 ? void 0 : err.response) - Log.err(err.response.getResultAsString()); - if (repeats > 0 && !(((_a = err.status) === null || _a === void 0 ? void 0 : _a.code) >= 400 && ((_b = err.status) === null || _b === void 0 ? void 0 : _b.code) <= 499)) - setFishPlayerData(data, repeats - 1, ignoreActivelySyncedFields).then(resolve).catch(reject); - else - reject(err); - }); - req.submit(function (response) { - resolve(); - }); - return promise; -} +"use strict"; +/* +Copyright © BalaM314, 2026. All Rights Reserved. +This file contains wrappers over the API calls to the backend server. +*/ +Object.defineProperty(exports, "__esModule", { value: true }); +exports.isVpn = isVpn; +exports.isVpnCached = isVpnCached; +exports.sendModerationMessage = sendModerationMessage; +exports.getStaffMessages = getStaffMessages; +exports.sendStaffMessage = sendStaffMessage; +exports.ban = ban; +exports.unban = unban; +exports.getBanned = getBanned; +exports.getFishPlayerData = getFishPlayerData; +exports.setFishPlayerData = setFishPlayerData; +var config_1 = require("/config"); +var players_1 = require("/players"); +var promise_1 = require("/promise"); +var cachedIps = {}; +/** Make an API request to see if an IP is likely VPN. */ +function isVpn(ip, callback, callbackError) { + if (ip in cachedIps) + return callback(cachedIps[ip]); + Http.get("http://ip-api.com/json/".concat(ip, "?fields=proxy,hosting"), function (res) { + var data = res.getResultAsString(); + var json = JSON.parse(data); + var isVpn = json.proxy || json.hosting; + cachedIps[ip] = isVpn; + players_1.FishPlayer.stats.numIpsChecked++; + if (isVpn) + players_1.FishPlayer.stats.numIpsFlagged++; + callback(isVpn); + }, callbackError !== null && callbackError !== void 0 ? callbackError : (function (err) { + Log.err("[API] Network error when trying to call api.isVpn()"); + players_1.FishPlayer.stats.numIpsErrored++; + callback(false); + })); +} +function isVpnCached(ip) { + return cachedIps[ip]; +} +/** Send text to the moderation logs channel in Discord. */ +function sendModerationMessage(message) { + if (config_1.Mode.noBackend) { + Log.info("Sent moderation log message: ".concat(message)); + return; + } + var req = Http.post("http://".concat(config_1.backendIP, "/api/mod-dump"), JSON.stringify({ message: message })).header('Content-Type', 'application/json').header('Accept', '*/*'); + req.timeout = 10000; + req.error(function () { return Log.err("[API] Network error when trying to call api.sendModerationMessage()"); }); + req.submit(function (response) { + //Log.info(response.getResultAsString()); + }); +} +/** Get staff messages from discord. */ +function getStaffMessages(callback) { + if (config_1.Mode.noBackend) + return; + var req = Http.post("http://".concat(config_1.backendIP, "/api/getStaffMessages"), JSON.stringify({ server: config_1.Gamemode.name() })) + .header('Content-Type', 'application/json').header('Accept', '*/*'); + req.timeout = 10000; + req.error(function () { return Log.err("[API] Network error when trying to call api.getStaffMessages()"); }); + req.submit(function (response) { + var temp = response.getResultAsString(); + if (!temp.length) + Log.err("[API] Network error(empty response) when trying to call api.getStaffMessages()"); + else + callback(JSON.parse(temp).messages); + }); +} +/** Send staff messages from server. */ +function sendStaffMessage(message, playerName, isStaff, callback) { + if (config_1.Mode.noBackend) + return; + var req = Http.post("http://".concat(config_1.backendIP, "/api/sendStaffMessage"), + // need to send both name variants so one can be sent to the other servers with color and discord can use the clean one + JSON.stringify({ message: message, playerName: playerName, cleanedName: Strings.stripColors(playerName), server: config_1.Gamemode.name(), isStaff: isStaff })).header('Content-Type', 'application/json').header('Accept', '*/*'); + req.timeout = 10000; + req.error(function () { + Log.err("[API] Network error when trying to call api.sendStaffMessage()"); + callback === null || callback === void 0 ? void 0 : callback(false); + }); + req.submit(function (response) { + var temp = response.getResultAsString(); + if (!temp.length) + Log.err("[API] Network error(empty response) when trying to call api.sendStaffMessage()"); + else + callback === null || callback === void 0 ? void 0 : callback(JSON.parse(temp).data); + }); +} +/** Bans the provided ip and/or uuid. */ +function ban(data, callback) { + if (callback === void 0) { callback = function () { }; } + if (config_1.Mode.noBackend) + return; + var req = Http.post("http://".concat(config_1.backendIP, "/api/ban"), JSON.stringify(data)) + .header('Content-Type', 'application/json') + .header('Accept', '*/*'); + req.timeout = 10000; + req.error(function () { return Log.err("[API] Network error when trying to call api.ban(".concat(data.ip, ", ").concat(data.uuid, ")")); }); + req.submit(function (response) { + var str = response.getResultAsString(); + if (!str.length) + return Log.err("[API] Network error(empty response) when trying to call api.ban()"); + callback(JSON.parse(str).data); + }); +} +/** Unbans the provided ip and/or uuid. */ +function unban(data, callback) { + if (callback === void 0) { callback = function () { }; } + if (config_1.Mode.noBackend) + return; + var req = Http.post("http://".concat(config_1.backendIP, "/api/unban"), JSON.stringify(data)) + .header('Content-Type', 'application/json') + .header('Accept', '*/*'); + req.timeout = 10000; + req.error(function () { return Log.err("[API] Network error when trying to call api.ban({".concat(data.ip, ", ").concat(data.uuid, "})")); }); + req.submit(function (response) { + var str = response.getResultAsString(); + if (!str.length) + return Log.err("[API] Network error(empty response) when trying to call api.unban()"); + var parsedData = JSON.parse(str); + callback(parsedData.status, parsedData.error); + }); +} +/** Gets if either the provided uuid or ip is banned. */ +function getBanned(data, callback) { + if (config_1.Mode.noBackend) { + Log.info("[API] Attempted to getBanned(".concat(data.uuid, "/").concat(data.ip, "), assuming false due to local debug")); + callback(false); + return; + } + //TODO cache 4s + var req = Http.post("http://".concat(config_1.backendIP, "/api/checkIsBanned"), JSON.stringify(data)) + .header('Content-Type', 'application/json') + .header('Accept', '*/*'); + req.timeout = 10000; + req.error(function () { return Log.err("[API] Network error when trying to call api.getBanned()"); }); + req.submit(function (response) { + var str = response.getResultAsString(); + if (!str.length) + return Log.err("[API] Network error(empty response) when trying to call api.getBanned()"); + callback(JSON.parse(str).data); + }); +} +/** + * Fetches fish player data from the backend. + **/ +function getFishPlayerData(uuid) { + var _a = promise_1.Promise.withResolvers(), promise = _a.promise, resolve = _a.resolve, reject = _a.reject; + function fail(err) { + Log.err("[API] Network error when trying to call api.getFishPlayerData()"); + if (err) + Log.err(err); + reject(err); + } + if (config_1.Mode.noBackend) { + reject("local debug mode"); + return promise; + } + var req = Http.post("http://".concat(config_1.backendIP, "/api/fish-player"), JSON.stringify({ + id: uuid, + gamemode: config_1.Gamemode.name(), + })) + .header('Content-Type', 'application/json') + .header('Accept', '*/*'); + req.timeout = 10000; + req.error(fail); + req.submit(function (response) { + var data = response.getResultAsString(); + if (data) { + var result = JSON.parse(data); + if (!result || typeof result != "object") + fail("Invalid fish player data"); + resolve(result); + } + else { + resolve(null); + } + }); + return promise; +} +/** Pushes fish player data to the backend. */ +function setFishPlayerData(data, repeats, ignoreActivelySyncedFields) { + var _a = promise_1.Promise.withResolvers(), promise = _a.promise, resolve = _a.resolve, reject = _a.reject; + if (config_1.Mode.noBackend) { + resolve(); + return promise; + } + var req = Http.post("http://".concat(config_1.backendIP, "/api/fish-player/set"), JSON.stringify({ + player: data, + gamemode: config_1.Gamemode.name(), + ignoreActivelySyncedFields: ignoreActivelySyncedFields, + })) + .header('Content-Type', 'application/json') + .header('Accept', '*/*'); + req.timeout = 10000; + req.error(function (err) { + var _a, _b; + Log.err("[API] Network error when trying to call api.setFishPlayerData(), repeats=".concat(repeats)); + Log.err(err); + if (err === null || err === void 0 ? void 0 : err.response) + Log.err(err.response.getResultAsString()); + if (repeats > 0 && !(((_a = err.status) === null || _a === void 0 ? void 0 : _a.code) >= 400 && ((_b = err.status) === null || _b === void 0 ? void 0 : _b.code) <= 499)) + setFishPlayerData(data, repeats - 1, ignoreActivelySyncedFields).then(resolve).catch(reject); + else + reject(err); + }); + req.submit(function (response) { + resolve(); + }); + return promise; +} diff --git a/build/scripts/commands/aggregate.js b/build/scripts/commands/aggregate.js index ed0e4481..c8d305d9 100644 --- a/build/scripts/commands/aggregate.js +++ b/build/scripts/commands/aggregate.js @@ -1,54 +1,54 @@ -"use strict"; -var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { - if (k2 === undefined) k2 = k; - var desc = Object.getOwnPropertyDescriptor(m, k); - if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { - desc = { enumerable: true, get: function() { return m[k]; } }; - } - Object.defineProperty(o, k2, desc); -}) : (function(o, m, k, k2) { - if (k2 === undefined) k2 = k; - o[k2] = m[k]; -})); -var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { - Object.defineProperty(o, "default", { enumerable: true, value: v }); -}) : function(o, v) { - o["default"] = v; -}); -var __importStar = (this && this.__importStar) || (function () { - var ownKeys = function(o) { - ownKeys = Object.getOwnPropertyNames || function (o) { - var ar = []; - for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k; - return ar; - }; - return ownKeys(o); - }; - return function (mod) { - if (mod && mod.__esModule) return mod; - var result = {}; - if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]); - __setModuleDefault(result, mod); - return result; - }; -})(); -Object.defineProperty(exports, "__esModule", { value: true }); -exports.registerAll = registerAll; -/* -Copyright © BalaM314, 2026. All Rights Reserved. -This file combines all the commands defined in various files into one function. -*/ -var console_1 = require("/commands/console"); -var general_1 = require("/commands/general"); -var member_1 = require("/commands/member"); -var staff_1 = require("/commands/staff"); -var commands = __importStar(require("/frameworks/commands")); -var packetHandlers_1 = require("/packetHandlers"); -function registerAll(clientHandler, serverHandler) { - commands.register(staff_1.commands, clientHandler, serverHandler); - commands.register(general_1.commands, clientHandler, serverHandler); - commands.register(member_1.commands, clientHandler, serverHandler); - commands.register(packetHandlers_1.commands, clientHandler, serverHandler); - commands.registerConsole(console_1.commands, serverHandler); - commands.initialize(); -} +"use strict"; +var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + var desc = Object.getOwnPropertyDescriptor(m, k); + if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { + desc = { enumerable: true, get: function() { return m[k]; } }; + } + Object.defineProperty(o, k2, desc); +}) : (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + o[k2] = m[k]; +})); +var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { + Object.defineProperty(o, "default", { enumerable: true, value: v }); +}) : function(o, v) { + o["default"] = v; +}); +var __importStar = (this && this.__importStar) || (function () { + var ownKeys = function(o) { + ownKeys = Object.getOwnPropertyNames || function (o) { + var ar = []; + for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k; + return ar; + }; + return ownKeys(o); + }; + return function (mod) { + if (mod && mod.__esModule) return mod; + var result = {}; + if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]); + __setModuleDefault(result, mod); + return result; + }; +})(); +Object.defineProperty(exports, "__esModule", { value: true }); +exports.registerAll = registerAll; +/* +Copyright © BalaM314, 2026. All Rights Reserved. +This file combines all the commands defined in various files into one function. +*/ +var console_1 = require("/commands/console"); +var general_1 = require("/commands/general"); +var member_1 = require("/commands/member"); +var staff_1 = require("/commands/staff"); +var commands = __importStar(require("/frameworks/commands")); +var packetHandlers_1 = require("/packetHandlers"); +function registerAll(clientHandler, serverHandler) { + commands.register(staff_1.commands, clientHandler, serverHandler); + commands.register(general_1.commands, clientHandler, serverHandler); + commands.register(member_1.commands, clientHandler, serverHandler); + commands.register(packetHandlers_1.commands, clientHandler, serverHandler); + commands.registerConsole(console_1.commands, serverHandler); + commands.initialize(); +} diff --git a/build/scripts/commands/console.js b/build/scripts/commands/console.js index 6c1ce86a..6caa9505 100644 --- a/build/scripts/commands/console.js +++ b/build/scripts/commands/console.js @@ -1,1130 +1,1130 @@ -"use strict"; -/* -Copyright © BalaM314, 2026. All Rights Reserved. -This file contains all the console commands, which can be run through the server console. -*/ -var __makeTemplateObject = (this && this.__makeTemplateObject) || function (cooked, raw) { - if (Object.defineProperty) { Object.defineProperty(cooked, "raw", { value: raw }); } else { cooked.raw = raw; } - return cooked; -}; -var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { - if (k2 === undefined) k2 = k; - var desc = Object.getOwnPropertyDescriptor(m, k); - if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { - desc = { enumerable: true, get: function() { return m[k]; } }; - } - Object.defineProperty(o, k2, desc); -}) : (function(o, m, k, k2) { - if (k2 === undefined) k2 = k; - o[k2] = m[k]; -})); -var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { - Object.defineProperty(o, "default", { enumerable: true, value: v }); -}) : function(o, v) { - o["default"] = v; -}); -var __importStar = (this && this.__importStar) || (function () { - var ownKeys = function(o) { - ownKeys = Object.getOwnPropertyNames || function (o) { - var ar = []; - for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k; - return ar; - }; - return ownKeys(o); - }; - return function (mod) { - if (mod && mod.__esModule) return mod; - var result = {}; - if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]); - __setModuleDefault(result, mod); - return result; - }; -})(); -var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { - function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } - return new (P || (P = Promise))(function (resolve, reject) { - function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } - function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } - function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } - step((generator = generator.apply(thisArg, _arguments || [])).next()); - }); -}; -var __generator = (this && this.__generator) || function (thisArg, body) { - var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g = Object.create((typeof Iterator === "function" ? Iterator : Object).prototype); - return g.next = verb(0), g["throw"] = verb(1), g["return"] = verb(2), typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g; - function verb(n) { return function (v) { return step([n, v]); }; } - function step(op) { - if (f) throw new TypeError("Generator is already executing."); - while (g && (g = 0, op[0] && (_ = 0)), _) try { - if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t; - if (y = 0, t) op = [op[0] & 2, t.value]; - switch (op[0]) { - case 0: case 1: t = op; break; - case 4: _.label++; return { value: op[1], done: false }; - case 5: _.label++; y = op[1]; op = [0]; continue; - case 7: op = _.ops.pop(); _.trys.pop(); continue; - default: - if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; } - if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; } - if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; } - if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; } - if (t[2]) _.ops.pop(); - _.trys.pop(); continue; - } - op = body.call(thisArg, _); - } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; } - if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true }; - } -}; -var __read = (this && this.__read) || function (o, n) { - var m = typeof Symbol === "function" && o[Symbol.iterator]; - if (!m) return o; - var i = m.call(o), r, ar = [], e; - try { - while ((n === void 0 || n-- > 0) && !(r = i.next()).done) ar.push(r.value); - } - catch (error) { e = { error: error }; } - finally { - try { - if (r && !r.done && (m = i["return"])) m.call(i); - } - finally { if (e) throw e.error; } - } - return ar; -}; -var __values = (this && this.__values) || function(o) { - var s = typeof Symbol === "function" && Symbol.iterator, m = s && o[s], i = 0; - if (m) return m.call(o); - if (o && typeof o.length === "number") return { - next: function () { - if (o && i >= o.length) o = void 0; - return { value: o && o[i++], done: !o }; - } - }; - throw new TypeError(s ? "Object is not iterable." : "Symbol.iterator is not defined."); -}; -var __spreadArray = (this && this.__spreadArray) || function (to, from, pack) { - if (pack || arguments.length === 2) for (var i = 0, l = from.length, ar; i < l; i++) { - if (ar || !(i in from)) { - if (!ar) ar = Array.prototype.slice.call(from, 0, i); - ar[i] = from[i]; - } - } - return to.concat(ar || Array.prototype.slice.call(from)); -}; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.commands = void 0; -var api = __importStar(require("/api")); -var config_1 = require("/config"); -var files_1 = require("/files"); -var fjsContext = __importStar(require("/fjsContext")); -var commands_1 = require("/frameworks/commands"); -var funcs_1 = require("/funcs"); -var globals_1 = require("/globals"); -var players_1 = require("/players"); -var ranks_1 = require("/ranks"); -var utils_1 = require("/utils"); -exports.commands = (0, commands_1.consoleCommandList)({ - setrank: { - args: ["player:player", "rank:rank"], - description: "Set a player's rank.", - handler: function (_a) { - return __awaiter(this, arguments, void 0, function (_b) { - var args = _b.args, outputSuccess = _b.outputSuccess, f = _b.f; - return __generator(this, function (_c) { - switch (_c.label) { - case 0: - if (args.rank == ranks_1.Rank.pi && !config_1.Mode.localDebug) - (0, commands_1.fail)(f(templateObject_1 || (templateObject_1 = __makeTemplateObject(["Rank ", " is immutable."], ["Rank ", " is immutable."])), args.rank)); - if (args.player.immutable() && !config_1.Mode.localDebug) - (0, commands_1.fail)(f(templateObject_2 || (templateObject_2 = __makeTemplateObject(["Player ", " is immutable."], ["Player ", " is immutable."])), args.player)); - return [4 /*yield*/, args.player.setRank(args.rank)]; - case 1: - _c.sent(); - (0, utils_1.logAction)("set rank to ".concat(args.rank.name, " for"), "console", args.player); - outputSuccess(f(templateObject_3 || (templateObject_3 = __makeTemplateObject(["Set rank of player ", " to ", ""], ["Set rank of player ", " to ", ""])), args.player, args.rank)); - args.player.sendMessage("[royal]Your rank has been set to ".concat(args.rank.coloredName(), ".")); - return [2 /*return*/]; - } - }); - }); - } - }, - admin: { - args: ["nothing:string?"], - description: "Use the setrank command instead.", - handler: function () { - (0, commands_1.fail)("Use the \"setrank\" command instead. Hint: \"setrank player admin\""); - } - }, - setflag: { - args: ["player:player", "flag:roleflag", "value:boolean"], - description: "Set a player's role flags.", - handler: function (_a) { - return __awaiter(this, arguments, void 0, function (_b) { - var args = _b.args, outputSuccess = _b.outputSuccess, f = _b.f; - return __generator(this, function (_c) { - switch (_c.label) { - case 0: return [4 /*yield*/, args.player.setFlag(args.flag, args.value)]; - case 1: - _c.sent(); - (0, utils_1.logAction)("set roleflag ".concat(args.flag.name, " to ").concat(args.value, " for"), "console", args.player); - outputSuccess(f(templateObject_4 || (templateObject_4 = __makeTemplateObject(["Set role flag ", " of player ", " to ", ""], ["Set role flag ", " of player ", " to ", ""])), args.flag, args.player, args.value)); - return [2 /*return*/]; - } - }); - }); - } - }, - savePlayers: { - args: [], - description: "Runs FishPlayer.save()", - handler: function (_a) { - var outputSuccess = _a.outputSuccess; - players_1.FishPlayer.saveAll(); - outputSuccess("Successfully wrote fish player data."); - } - }, - info: { - args: ["player:string"], - description: "Find player info(s). Displays all names and ips of a player.", - handler: function (_a) { - return __awaiter(this, arguments, void 0, function (_b) { - function display(infoList) { - var e_2, _a; - var outputString = [""]; - var _loop_1 = function (playerInfo, fishP) { - var flagsText = [ - (fishP === null || fishP === void 0 ? void 0 : fishP.marked()) && (globals_1.maxTime - fishP.unmarkTime < 20000 ? - "&lris marked forever&fr" - : "&lris marked&fr until ".concat((0, utils_1.formatTimeRelative)(fishP.unmarkTime))), - (fishP === null || fishP === void 0 ? void 0 : fishP.muted) && "&lris muted&fr", - (fishP === null || fishP === void 0 ? void 0 : fishP.hasFlag("member")) && "&lmis member&fr", - (fishP === null || fishP === void 0 ? void 0 : fishP.autoflagged) && "&lris autoflagged&fr", - playerInfo.banned && "&bris UUID banned&fr", - ].filter(Boolean).join(", "); - var lastJoinedColor = (fishP === null || fishP === void 0 ? void 0 : fishP.lastJoined) && fishP.lastJoined !== -1 ? (function () { - var timeSinceLastJoin = (Date.now() - fishP.lastJoined) / 1000; - if (timeSinceLastJoin < 3600) - return "&br"; - if (timeSinceLastJoin < 24 * 3600) - return "&by"; - if (timeSinceLastJoin < 7 * 24 * 3600) - return "&lw"; - return "&lk"; - })() : "&fr"; - outputString.push([ - "".concat(lastJoinedColor, "Trace info for player &fr&y").concat(playerInfo.id, "&fr").concat(lastJoinedColor, " / &c\"").concat((0, funcs_1.escapeStringColorsServer)(Strings.stripColors(playerInfo.lastName)), "\" &lk(").concat((0, funcs_1.escapeStringColorsServer)(playerInfo.lastName), ")&fr"), - playerInfo.names.size > 1 && "all names used: ".concat(playerInfo.names.map(funcs_1.escapeStringColorsServer).map(function (n) { return "&c\"".concat(n, "\"&fr"); }).items.join(', ')), - "all IPs used: ".concat(playerInfo.ips.map(function (n) { return (n == playerInfo.lastIP ? '&c' : '&w') + n + '&fr'; }).items.join(", ")), - "joined &c".concat(playerInfo.timesJoined, "&fr times, kicked &c").concat(playerInfo.timesKicked, "&fr times"), - fishP && fishP.lastJoined !== -1 && "Last joined: ".concat((0, utils_1.formatTimeRelative)(fishP.lastJoined)), - fishP && fishP.firstJoined !== -1 && (0, utils_1.formatTimeRelative)(fishP.firstJoined), - fishP && "USID: &c".concat(fishP.usid, "&fr"), - fishP && fishP.rank !== ranks_1.Rank.player && "Rank: &c".concat(fishP.rank.name, "&fr"), - flagsText, - ].filter(Boolean).map(function (l, i) { return i == 0 ? l : '\t' + l; }).join("\n")); - }; - try { - for (var infoList_1 = __values(infoList), infoList_1_1 = infoList_1.next(); !infoList_1_1.done; infoList_1_1 = infoList_1.next()) { - var _b = __read(infoList_1_1.value, 2), playerInfo = _b[0], fishP = _b[1]; - _loop_1(playerInfo, fishP); - } - } - catch (e_2_1) { e_2 = { error: e_2_1 }; } - finally { - try { - if (infoList_1_1 && !infoList_1_1.done && (_a = infoList_1.return)) _a.call(infoList_1); - } - finally { if (e_2) throw e_2.error; } - } - output(outputString.join("\n")); - } - var infoList, playersToFetch, _c, _d, batch, e_1_1, err_1; - var e_1, _e; - var _this = this; - var args = _b.args, output = _b.output, admins = _b.admins; - return __generator(this, function (_f) { - switch (_f.label) { - case 0: - infoList = admins.findByName(args.player) - .toSeq().toArray() - .map(function (p) { return [p, players_1.FishPlayer.getById(p.id)]; }); - if (infoList.length == 0) - (0, commands_1.fail)("No players found."); - playersToFetch = infoList.filter(function (_a) { - var _b = __read(_a, 2), a = _b[0], b = _b[1]; - return !b; - }).map(function (_a) { - var _b = __read(_a, 2), a = _b[0], b = _b[1]; - return a; - }); - if (!(playersToFetch.length == 0)) return [3 /*break*/, 1]; - display(infoList); - return [3 /*break*/, 13]; - case 1: - //Attempt to fetch data - //If there are too many players, give up - if (playersToFetch.length > 50) - display(infoList); - output("Fetching data..."); - _f.label = 2; - case 2: - _f.trys.push([2, 11, , 12]); - _f.label = 3; - case 3: - _f.trys.push([3, 8, 9, 10]); - _c = __values((0, funcs_1.to2DArray)(playersToFetch, 10)), _d = _c.next(); - _f.label = 4; - case 4: - if (!!_d.done) return [3 /*break*/, 7]; - batch = _d.value; - return [4 /*yield*/, Promise.all(batch.map(function (info) { return __awaiter(_this, void 0, void 0, function () { - var data, fishP; - return __generator(this, function (_a) { - switch (_a.label) { - case 0: return [4 /*yield*/, api.getFishPlayerData(info.id)]; - case 1: - data = _a.sent(); - if (data) { - fishP = players_1.FishPlayer.createFromInfo(info); - fishP.updateData(data); - players_1.FishPlayer.cachedPlayers[info.id] = fishP; - } - return [2 /*return*/]; - } - }); - }); }))]; - case 5: - _f.sent(); - _f.label = 6; - case 6: - _d = _c.next(); - return [3 /*break*/, 4]; - case 7: return [3 /*break*/, 10]; - case 8: - e_1_1 = _f.sent(); - e_1 = { error: e_1_1 }; - return [3 /*break*/, 10]; - case 9: - try { - if (_d && !_d.done && (_e = _c.return)) _e.call(_c); - } - finally { if (e_1) throw e_1.error; } - return [7 /*endfinally*/]; - case 10: return [3 /*break*/, 12]; - case 11: - err_1 = _f.sent(); - Log.err(err_1); - return [3 /*break*/, 12]; - case 12: - infoList = admins.findByName(args.player) - .toSeq().toArray() - .map(function (p) { return [p, players_1.FishPlayer.getById(p.id)]; }); - display(infoList); - _f.label = 13; - case 13: return [2 /*return*/]; - } - }); - }); - } - }, - infoonline: { - args: ["player:string"], - description: "Display information about an online player.", - handler: function (_a) { - var e_3, _b; - var args = _a.args, output = _a.output, admins = _a.admins; - var infoList = args.player == "*" ? players_1.FishPlayer.getAllOnline() : players_1.FishPlayer.getAllByName(args.player, false); - if (infoList.length == 0) - (0, commands_1.fail)("Nobody with that name could be found."); - var outputString = [""]; - var _loop_2 = function (player) { - var playerInfo = admins.getInfo(player.uuid); - outputString.push("Info for player &c\"".concat(player.cleanedName, "\" &lk(").concat(player.name, ")&fr\n\tUUID: &c\"").concat(playerInfo.id, "\"&fr\n\tUSID: &c").concat(player.usid ? "\"".concat(player.usid, "\"") : "unknown", "&fr\n\tall names used: ").concat(playerInfo.names.map(function (n) { return "&c\"".concat(n, "\"&fr"); }).items.join(', '), "\n\tall IPs used: ").concat(playerInfo.ips.map(function (n) { return (n == playerInfo.lastIP ? '&c' : '&w') + n + '&fr'; }).items.join(", "), "\n\tjoined &c").concat(playerInfo.timesJoined, "&fr times, kicked &c").concat(playerInfo.timesKicked, "&fr times\n\trank: &c").concat(player.rank.name, "&fr").concat((player.marked() ? ", &lris marked&fr" : "") + (player.muted ? ", &lris muted&fr" : "") + (player.hasFlag("member") ? ", &lmis member&fr" : "") + (player.autoflagged ? ", &lris autoflagged&fr" : ""))); - }; - try { - for (var infoList_2 = __values(infoList), infoList_2_1 = infoList_2.next(); !infoList_2_1.done; infoList_2_1 = infoList_2.next()) { - var player = infoList_2_1.value; - _loop_2(player); - } - } - catch (e_3_1) { e_3 = { error: e_3_1 }; } - finally { - try { - if (infoList_2_1 && !infoList_2_1.done && (_b = infoList_2.return)) _b.call(infoList_2); - } - finally { if (e_3) throw e_3.error; } - } - output(outputString.join("\n")); - } - }, - unblacklist: { - args: ["ip:string"], - description: "Unblacklists an ip from the DOS blacklist.", - handler: function (_a) { - var args = _a.args, output = _a.output, admins = _a.admins; - if (args.ip === '*') { - var size = admins.dosBlacklist.size; - if (size == 0) - (0, commands_1.fail)('DOS blacklist is already empty.'); - admins.dosBlacklist.clear(); - output("Cleared ".concat(size, " IPs from the DOS blacklist.")); - } - else { - if (admins.dosBlacklist.remove(args.ip)) - output("Removed ".concat(args.ip, " from the DOS blacklist.")); - else - (0, commands_1.fail)("IP address ".concat(args.ip, " is not DOS blacklisted.")); - } - } - }, - blacklist: { - args: ["verbose:boolean?"], - description: "Allows you to view the DOS blacklist.", - handler: function (_a) { - var args = _a.args, output = _a.output, admins = _a.admins; - var blacklist = admins.dosBlacklist; - if (blacklist.isEmpty()) - (0, commands_1.fail)("The blacklist is empty"); - if (args.verbose) { - var outputString_1 = ["DOS Blacklist:"]; - blacklist.each(function (ip) { - var info = admins.findByIP(ip); - if (info) { - outputString_1.push("IP: &c".concat(ip, "&fr UUID: &c\"").concat(info.id, "\"&fr Last name used: &c\"").concat(info.plainLastName(), "\"&fr")); - } - }); - output(outputString_1.join("\n")); - output("".concat(blacklist.size, " blacklisted IPs")); - } - else { - output(blacklist.toString()); - output("".concat(blacklist.size, " blacklisted IPs")); - } - } - }, - whack: { - args: ["target:string"], - description: "Whacks (ipbans) a player.", - handler: function (_a) { - var args = _a.args, output = _a.output, outputFail = _a.outputFail, admins = _a.admins; - var range; - if (globals_1.ipPattern.test(args.target)) { - //target is an ip - api.ban({ ip: args.target }); - var info = admins.findByIP(args.target); - if (info) - (0, utils_1.logAction)("whacked", "console", info); - else - (0, utils_1.logAction)("console ip-whacked ".concat(args.target)); - if (admins.isIPBanned(args.target)) { - output("IP &c\"".concat(args.target, "\"&fr is already banned. Ban was synced to other servers.")); - } - else { - admins.banPlayerIP(args.target); - output("&lrIP &c\"".concat(args.target, "\"&lr was banned. Ban was synced to other servers.")); - } - } - else if ((range = (0, utils_1.getIPRange)(args.target)) != null) { - if (admins.subnetBans.contains(boolf(function (ip) { return ip.replace(/\.$/, "") == range; }))) { - output("Subnet &c\"".concat(range, "\"&fr is already banned.")); - } - else { - admins.subnetBans.add(range); - output("&lrIP range &c\"".concat(range, "\"&lr was banned. Subnet bans are not synced.")); - } - } - else if (globals_1.uuidPattern.test(args.target)) { - var info = admins.getInfoOptional(args.target); - if (info) - (0, utils_1.logAction)("whacked", "console", info); - else - (0, utils_1.logAction)("console ip-whacked ".concat(args.target)); - if (admins.isIDBanned(args.target)) { - api.ban({ uuid: args.target }); - output("UUID &c\"".concat(args.target, "\"&fr is already banned. Ban was synced to other servers.")); - } - else { - admins.banPlayerID(args.target); - if (info) { - admins.banPlayerIP(info.lastIP); - api.ban({ uuid: args.target, ip: info.lastIP }); - output("&lrUUID &c\"".concat(args.target, "\" &lrwas banned. IP &c\"").concat(info.lastIP, "\"&lr was banned. Ban was synced to other servers.")); - } - else { - api.ban({ uuid: args.target }); - output("&lrUUID &c\"".concat(args.target, "\" &lrwas banned. Ban was synced to other servers. Warning: no stored info for this UUID, player may not exist. Unable to determine IP.")); - } - } - } - else { - var player = players_1.FishPlayer.getOneMindustryPlayerByName(args.target); - if (player === "none") { - outputFail("Could not find a player name matching &c\"".concat(args.target, "\"")); - } - else if (player === "multiple") { - outputFail("Name &c\"".concat(args.target, "\"&fr could refer to more than one player.")); - } - else { - if (player.admin) - (0, commands_1.fail)("Player &c\"".concat(player.name, "\"&fr is an admin, you probably don't want to ban them.")); - var ip = player.ip(); - var uuid = player.uuid(); - admins.banPlayerID(uuid); - admins.banPlayerIP(ip); - (0, utils_1.logAction)("console whacked ".concat(Strings.stripColors(player.name), " (`").concat(uuid, "`/`").concat(ip, "`)")); - api.ban({ uuid: uuid, ip: ip }); - output("&lrIP &c\"".concat(ip, "\"&lr was banned. UUID &c\"").concat(uuid, "\"&lr was banned. Ban was synced to other servers.")); - } - } - (0, utils_1.updateBans)(function (player) { return "[scarlet]Player [yellow]".concat(player.name, "[scarlet] has been whacked."); }); - } - }, - unwhack: { - args: ["target:string"], - description: "Unbans a player.", - handler: function (_a) { - var args = _a.args, output = _a.output, admins = _a.admins; - var range; - if (globals_1.ipPattern.test(args.target)) { - //target is an ip - if (players_1.FishPlayer.removePunishedIP(args.target)) { - output("Removed IP &c\"".concat(args.target, "\"&fr from the anti-evasion list.")); - } - if (admins.kickedIPs.remove(args.target)) { - output("Removed temporary kick for IP &c\"".concat(args.target, "\"&fr.")); - } - output("Checking ban status..."); - api.getBanned({ ip: args.target }, function (banned) { - if (banned) { - api.unban({ ip: args.target }); - (0, utils_1.logAction)("console unbanned ip `".concat(args.target, "`")); - output("IP &c\"".concat(args.target, "\"&fr has been globally unbanned.")); - } - else { - output("IP &c\"".concat(args.target, "\"&fr is not globally banned.")); - } - if (admins.isIPBanned(args.target)) { - admins.unbanPlayerIP(args.target); - output("IP &c\"".concat(args.target, "\"&fr has been locally unbanned.")); - } - else { - output("IP &c\"".concat(args.target, "\"&fr was not locally banned.")); - } - var size = admins.subnetBans.size; - admins.subnetBans.removeAll(function (r) { return args.target.startsWith(r); }); - if (admins.subnetBans.size < size) { - output("Unbanned IP ranges affecting this IP."); - } - }); - } - else if ((range = (0, utils_1.getIPRange)(args.target)) != null) { - if (admins.subnetBans.remove(function (b) { return b.replace(/\.$/, ".") == range.replace(/\.$/, "."); })) { - output("IP range &c\"".concat(range, "\"&fr was unbanned.")); - } - else { - output("IP range &c\"".concat(range, "\"&fr was not banned.")); - } - } - else if (globals_1.uuidPattern.test(args.target)) { - if (players_1.FishPlayer.removePunishedUUID(args.target)) { - output("Removed UUID &c\"".concat(args.target, "\"&fr from the anti-evasion list.")); - } - output("Checking ban status..."); - var info_1 = admins.findByIP(args.target); - api.getBanned({ uuid: args.target }, function (banned) { - if (banned) { - api.unban({ uuid: args.target }); - (0, utils_1.logAction)("console unbanned uuid `".concat(args.target, "`")); - output("UUID &c\"".concat(args.target, "\"&fr has been globally unbanned.")); - } - else { - output("UUID &c\"".concat(args.target, "\"&fr is not globally banned.")); - } - if (admins.isIDBanned(args.target)) { - admins.unbanPlayerID(args.target); - output("UUID &c\"".concat(args.target, "\"&fr has been locally unbanned.")); - } - else { - output("UUID &c\"".concat(args.target, "\"&fr was not locally banned.")); - } - if (info_1) { - if (info_1.lastKicked > 0) { - info_1.lastKicked = 0; - output("Removed temporary kick for UUID &c\"".concat(args.target, "\"&fr.")); - } - output("You may also want to consider unbanning the IP \"".concat(info_1.lastIP, "\".")); - } - }); - } - else { - (0, commands_1.fail)("Cannot unban by name; please use the info or search commands to find the IP and UUID of the player you are looking for."); - } - } - }, - ban: { - args: ["any:string"], - description: "Please use the whack command instead.", - handler: function () { - (0, commands_1.fail)("Use the whack command instead."); - } - }, - unban: { - args: ["any:string"], - description: "Please use the unwhack command instead.", - handler: function () { - (0, commands_1.fail)("Use the unwhack command instead."); - } - }, - "subnet-ban": { - args: ["any:string?", "anyb:string?"], - description: "Please use the whack and unwhack commands instead.", - handler: function (_a) { - var args = _a.args, output = _a.output, admins = _a.admins; - if (args.any && args.any != "list") - (0, commands_1.fail)("Use the whack and unwhack commands instead."); - output("List of all subnet bans:"); - output(admins.subnetBans.toString("\n")); - } - }, - joinbell: { - args: ["on:boolean?"], - description: "Toggles the join bell function.", - handler: function (_a) { - var _b = _a.args.on, on = _b === void 0 ? !globals_1.fishState.joinBell : _b; - globals_1.fishState.joinBell = on; - if (globals_1.fishState.joinBell) { - Log.info("Enabled sound on new player join. Run \"joinbell\" again to turn it off."); - } - else { - Log.info("Disabled sound on new player join."); - } - } - }, - loadfishplayerdata: { - args: ["areyousure:boolean", "fishplayerdata:string"], - description: "Overwrites current fish player data.", - handler: function (_a) { - var args = _a.args, output = _a.output; - if (args.areyousure) { - var before = Object.keys(players_1.FishPlayer.cachedPlayers).length; - players_1.FishPlayer.loadAll(args.fishplayerdata); - output("Loaded fish player data. before:".concat(before, ", after:").concat(Object.keys(players_1.FishPlayer.cachedPlayers).length)); - } - } - }, - resetauth: { - args: ["player:string"], - description: "Removes the USID of the player provided, use this if they are getting kicked with the message \"Authorization failure!\". Specify \"last\" to use the last player that got kicked.", - handler: function (_a) { - var _b, _c; - var args = _a.args, outputSuccess = _a.outputSuccess, outputFail = _a.outputFail, admins = _a.admins; - var player = args.player == "last" ? ((_b = players_1.FishPlayer.lastAuthKicked) !== null && _b !== void 0 ? _b : (0, commands_1.fail)("Nobody has been kicked for authorization failure since the last restart.")) : - (_c = players_1.FishPlayer.getById(args.player)) !== null && _c !== void 0 ? _c : (0, commands_1.fail)(admins.getInfoOptional(args.player) - ? "Player ".concat(args.player, " has joined the server, but their info was not cached, most likely because they have no rank, so there is no stored USID.") - : "Unknown player ".concat(args.player)); - if (player.ranksAtLeast("admin")) - (0, commands_1.fail)("Please use the setusid command instead."); - var oldusid = player.usid; - player.usid = null; - api.setFishPlayerData(player.getData(), 1, true).then(function () { - outputSuccess("Removed the usid of player ".concat(player.name, "/").concat(player.uuid, " (was ").concat(oldusid, ")")); - }).catch(function (err) { - Log.err(err); - outputFail("Failed to remove the usid, please try running the command again."); - }); - } - }, - setusid: { - args: ["uuid:string", "usid:string"], - description: "Sets the USID of a player.", - handler: function (_a) { - var _b; - var args = _a.args, outputSuccess = _a.outputSuccess, outputFail = _a.outputFail, f = _a.f; - if (args.usid.length !== 12) - (0, commands_1.fail)("Invalid USID: should be 12 characters ending with an equal sign"); - var player = (_b = players_1.FishPlayer.lastAuthKicked) !== null && _b !== void 0 ? _b : (0, commands_1.fail)("No authorization failures have occurred since the last restart."); - var oldusid = player.usid; - player.usid = args.usid; - api.setFishPlayerData(player.getData(), 1, true).then(function () { - outputSuccess("Set the usid of player ".concat(player.name, "/").concat(player.uuid, " to ").concat(args.usid, " (was ").concat(oldusid, ")")); - }).catch(function (err) { - Log.err(err); - outputFail("Failed to remove the usid, please try running the command again."); - }); - } - }, - update: { - args: ["branch:string?"], - description: "Updates the plugin.", - handler: function (_a) { - var args = _a.args, output = _a.output, outputSuccess = _a.outputSuccess, outputFail = _a.outputFail; - if (config_1.Mode.localDebug) - (0, commands_1.fail)("Cannot update in local debug mode."); - output("Updating..."); - var path = (0, utils_1.fishCommandsRootDirPath)().toString(); - Threads.thread(function () { - var _a, _b; - try { - var initialVersion = OS.exec("git", "-C", path, "rev-parse", "HEAD"); - var gitFetch = new ProcessBuilder("git", "-C", path, "fetch", "origin") - .redirectErrorStream(true) - .redirectOutput(ProcessBuilder.Redirect.INHERIT) - .start(); - gitFetch.waitFor(); - if (gitFetch.exitValue() == 0) { - outputSuccess("Fetched data, updating files..."); - } - else { - outputFail("Update failed!"); - return; - } - var newVersion = OS.exec("git", "-C", path, "rev-parse", "origin/".concat((_a = args.branch) !== null && _a !== void 0 ? _a : "master")); - if (initialVersion == newVersion) { - outputSuccess("Already up to date."); - return; - } - var gitCheckout = new ProcessBuilder("git", "-C", path, "checkout", "-q", "-f", "origin/".concat((_b = args.branch) !== null && _b !== void 0 ? _b : "master")) - .redirectErrorStream(true) - .redirectOutput(ProcessBuilder.Redirect.INHERIT) - .start(); - gitCheckout.waitFor(); - if (gitCheckout.exitValue() == 0) { - outputSuccess("Updated successfully from ".concat(initialVersion, " to ").concat(newVersion, ". Restart to apply changes.")); - } - else { - outputFail("Update failed!"); - return; - } - } - catch (err) { - Log.err(err); - outputFail("Update failed!"); - } - }); - } - }, - restart: { - args: ["time:number?"], - description: "Restarts the server.", - handler: function (_a) { - var _b; - var time = _a.args.time; - (_b = globals_1.fishState.restartLoopTask) === null || _b === void 0 ? void 0 : _b.cancel(); - var timeInferred = time == undefined; - if (Groups.player.isEmpty()) { - if (time == undefined) { - Log.info("Restarting immediately as no players are online."); - time !== null && time !== void 0 ? time : (time = 0); - } - } - else if (config_1.Gamemode.pvp()) { - time !== null && time !== void 0 ? time : (time = -1); - } - else { - time !== null && time !== void 0 ? time : (time = 60); - } - if (time == -1) { - Call.sendMessage("[accent]---[[[coral]+++[]]---\n[accent]Server restart queued. The server will restart after the current match is over.[]\n[accent]---[[[coral]+++[]]---"); - if (config_1.Gamemode.pvp() && timeInferred) - Log.info("PVP: restart will occur at the end of the current game. Specify a time to override, but &rthat would interrupt the current pvp match, and players would lose their teams.&fr"); - else - Log.info("Restarting once the current game ends."); - globals_1.fishState.restartQueued = true; - } - else { - if (time < 0 || time > 100) - (0, commands_1.fail)("Invalid time: out of valid range."); - (0, utils_1.serverRestartLoop)(time); - if (time == 0) - Log.info("Restarting now."); - else - Log.info("Restarting in ".concat(time, " second").concat(time == 1 ? "" : "s", ".")); - if (config_1.Gamemode.pvp()) { - Call.sendMessage("[accent]---[[[coral]+++[]]---\n[accent]Server restart imminent. [green]We'll be back after 20 seconds.[]\n[accent]---[[[coral]+++[]]---"); - } - else { - Call.sendMessage("[accent]---[[[coral]+++[]]---\n[accent]Server restart imminent. [green]We'll be back after 20 seconds, and all progress will be saved.[]\n[accent]---[[[coral]+++[]]---"); - } - } - } - }, - restartcancel: { - args: [], - description: "Cancels a planned server restart.", - handler: function (_a) { - var _b; - var outputSuccess = _a.outputSuccess; - var task = (_b = globals_1.fishState.restartLoopTask) !== null && _b !== void 0 ? _b : (0, commands_1.fail)("No restart scheduled."); - Call.sendMessage("[scarlet]Aborting..."); - task.cancel(); - Call.sendMessage("[scarlet]Server restart canceled."); - outputSuccess("Canceled restart."); - } - }, - rename: { - args: ["player:player", "newname:string"], - description: "Changes the name of a player.", - handler: function (_a) { - var args = _a.args, f = _a.f, outputSuccess = _a.outputSuccess; - if (args.player.hasPerm("blockTrolling")) - (0, commands_1.fail)(f(templateObject_5 || (templateObject_5 = __makeTemplateObject(["Operation aborted: Player ", " is insufficiently trollable."], ["Operation aborted: Player ", " is insufficiently trollable."])), args.player)); - var oldName = args.player.name; - args.player.player.name = args.player.prefixedName = args.newname; - args.player.shouldUpdateName = false; - outputSuccess("Renamed ".concat(oldName, " to ").concat(args.newname, ".")); - } - }, - fjs: { - args: ["js:string"], - description: "Executes arbitrary javascript code, but has access to fish-commands's variables.", - handler: function (_a) { - var args = _a.args; - fjsContext.runJS(args.js); - } - }, - checkmem: { - args: [], - description: "Checks memory usage of various objects.", - handler: function (_a) { - var output = _a.output; - output("Memory usage:\nTotal: ".concat(Math.round(Core.app.getJavaHeap() / (Math.pow(2, 10))), " KB\nNumber of cached fish players: ").concat(Object.keys(players_1.FishPlayer.cachedPlayers).length, " (stored locally: ").concat(Object.values(players_1.FishPlayer.cachedPlayers).filter(function (p) { return p.shouldCache(); }).length, ")\nFish player data string length: ").concat(players_1.FishPlayer.getFishPlayersString.length, " (").concat(Core.settings.getInt("fish-subkeys"), " subkeys)\nLength of tilelog entries: ").concat(Math.round(Object.values(globals_1.tileHistory).reduce(function (acc, a) { return acc + a.length; }, 0) / (Math.pow(2, 10))), " KB")); - } - }, - stopplayer: { - args: ['player:player', "time:time?", "message:string?"], - description: 'Stops a player.', - handler: function (_a) { - return __awaiter(this, arguments, void 0, function (_b) { - var previousTime, time; - var _c, _d, _e; - var args = _b.args, f = _b.f, outputSuccess = _b.outputSuccess; - return __generator(this, function (_f) { - switch (_f.label) { - case 0: - if (!args.player.marked()) return [3 /*break*/, 2]; - //overload: overwrite stoptime - if (!args.time) - (0, commands_1.fail)(f(templateObject_6 || (templateObject_6 = __makeTemplateObject(["Player ", " is already marked."], ["Player ", " is already marked."])), args.player)); - previousTime = (0, utils_1.formatTime)(args.player.unmarkTime - Date.now()); - return [4 /*yield*/, args.player.updateStopTime(args.time)]; - case 1: - _f.sent(); - outputSuccess(f(templateObject_7 || (templateObject_7 = __makeTemplateObject(["Player ", "'s stop time has been updated to ", " (was ", ")."], ["Player ", "'s stop time has been updated to ", " (was ", ")."])), args.player, (0, utils_1.formatTime)(args.time), previousTime)); - return [2 /*return*/]; - case 2: - time = (_c = args.time) !== null && _c !== void 0 ? _c : funcs_1.Duration.days(7); - if (time + Date.now() > globals_1.maxTime) - (0, commands_1.fail)("Error: time too high."); - return [4 /*yield*/, args.player.stop("console", time, (_d = args.message) !== null && _d !== void 0 ? _d : undefined)]; - case 3: - _f.sent(); - (0, utils_1.logAction)('stopped', "console", args.player, (_e = args.message) !== null && _e !== void 0 ? _e : undefined, time); - Call.sendMessage("[scarlet]Player \"".concat(args.player.prefixedName, "[scarlet]\" has been marked for ").concat((0, utils_1.formatTime)(time)).concat(args.message ? " with reason: [white]".concat(args.message, "[]") : "", ".")); - return [2 /*return*/]; - } - }); - }); - } - }, - stopoffline: { - args: ["uuid:uuid", "time:time?"], - description: "Stops a player by uuid.", - handler: function (_a) { - return __awaiter(this, arguments, void 0, function (_b) { - var stopTime, info, fishP; - var _c = _b.args, uuid = _c.uuid, time = _c.time, outputSuccess = _b.outputSuccess, admins = _b.admins; - return __generator(this, function (_d) { - switch (_d.label) { - case 0: - stopTime = time !== null && time !== void 0 ? time : (globals_1.maxTime - Date.now() - 10000); - info = admins.getInfoOptional(uuid); - if (info == null) - (0, commands_1.fail)("Unknown player ".concat(uuid)); - fishP = players_1.FishPlayer.getFromInfo(info); - return [4 /*yield*/, fishP.stop("console", stopTime)]; - case 1: - _d.sent(); - (0, utils_1.logAction)('stopped', "console", info, undefined, stopTime); - outputSuccess("Player \"".concat(info.lastName, "\" was marked for ").concat((0, utils_1.formatTime)(stopTime), ".")); - return [2 /*return*/]; - } - }); - }); - } - }, - clearfire: { - args: [], - description: "Clears all the fires.", - handler: function (_a) { - var output = _a.output, outputSuccess = _a.outputSuccess; - output("Removing fires..."); - var totalRemoved = 0; - Call.sendMessage("[scarlet][[Fire Department]:[yellow] Fires were reported. Trucks are en-route. Removing all fires shortly."); - Timer.schedule(function () { - totalRemoved += Groups.fire.size(); - Groups.fire.each(function (f) { return f.remove(); }); - Groups.fire.clear(); - }, 2, 0.1, 40); - Timer.schedule(function () { - outputSuccess("Removed ".concat(totalRemoved, " fires.")); - Call.sendMessage("[scarlet][[Fire Department]:[yellow] We've extinguished ".concat(totalRemoved, " fires.")); - }, 6.1); - } - }, - status: { - args: [], - description: "Displays server status.", - handler: function (_a) { - var output = _a.output; - if (Vars.state.isMenu()) - (0, commands_1.fail)("Status: Server closed."); - var uptime = Packages.java.lang.management.ManagementFactory.getRuntimeMXBean().getUptime(); - var numStaff = 0; - players_1.FishPlayer.forEachPlayer(function (p) { - if (p.ranksAtLeast("mod")) - numStaff++; - }); - var uptimeColor = uptime < funcs_1.Duration.days(2) ? "" : - uptime < funcs_1.Duration.days(5) ? "&ly" : - uptime < funcs_1.Duration.days(9) ? "&y" : - "&br"; - output("\nStatus:\nPlaying on map &fi".concat(Vars.state.map.plainName(), "&fr for ").concat((0, utils_1.formatTime)(1000 * Vars.state.tick / 60), "\n").concat(Vars.state.rules.waves ? "Wave &c".concat(Vars.state.wave, "&fr, &c").concat(Math.ceil(Vars.state.wavetime / 60), "&fr seconds until next wave.\n") : "", "&c").concat(Groups.unit.size(), "&fr units, &c").concat(Vars.state.enemies, "&fr enemies, &c").concat(Groups.build.size(), "&fr buildings\nTPS: ").concat((0, utils_1.colorNumber)(Core.graphics.getFramesPerSecond(), function (f) { return f > 58 ? "&g" : f > 30 ? "&y" : f > 10 ? "&r" : "&br&w"; }, "server"), ", Memory: &c").concat(Math.round(Core.app.getJavaHeap() / 1048576), "&fr MB\nServer uptime: ").concat(uptimeColor).concat((0, utils_1.formatTime)(uptime), "&fr (since ").concat((0, utils_1.formatTimestampFull)(Date.now() - uptime), ")\n").concat([ - globals_1.fishState.restartQueued ? "&by&lwRestart queued&fr" : "", - globals_1.fishState.restartLoopTask ? "&by&lwRestarting now&fr" : "", - players_1.FishPlayer.antiBotMode() ? "&br&wANTIBOT ACTIVE!&fr" + (0, utils_1.getAntiBotInfo)("server") : "", - ].filter(function (l) { return l.length > 0; }).join("\n"), "\n").concat((0, utils_1.colorNumber)(Groups.player.size(), function (n) { return n > 0 ? "&c" : "&lr"; }, "server"), " players online, ").concat((0, utils_1.colorNumber)(numStaff, function (n) { return n > 0 ? "&c" : "&lr"; }, "server"), " staff members.\n").concat(players_1.FishPlayer.mapPlayers(function (p) { - return "\t".concat(p.rank.shortPrefix, " &c").concat(p.uuid, "&fr &c").concat(p.name, "&fr"); - }).join("\n") || "&lrNo players connected.&fr", "\n")); - } - }, - tmux: { - args: ["attach:string"], - description: "Oopsie", - handler: function () { - (0, commands_1.fail)("You are already in the Mindustry server console. Please regain situational awareness before running any further commands."); - } - }, - BEGIN: { - args: ["transaction:string"], - description: "Oopsie", - handler: function (_a) { - var args = _a.args; - if (args.transaction == "TRANSACTION") - (0, commands_1.fail)("Not possible :( please download and run locally, and make a backup"); - else - (0, commands_1.fail)("Command not found. Did you mean \"BEGIN TRANSACTION\"?"); - } - }, - prune: { - args: ["confirm:boolean?"], - description: "Prunes fish player data", - handler: function (_a) { - var args = _a.args, admins = _a.admins, outputSuccess = _a.outputSuccess, outputFail = _a.outputFail; - var playersToPrune = Object.values(players_1.FishPlayer.cachedPlayers) - .filter(function (player) { - if (player.hasData()) - return false; - var data = admins.getInfoOptional(player.uuid); - return (!data || - data.timesJoined == 1 || - (data.timesJoined < 10 && - (Date.now() - player.lastJoined) > funcs_1.Duration.months(1))); - }); - if (args.confirm) { - outputSuccess("Creating backup..."); - var backupScript = Core.settings.getDataDirectory().child("backup.sh"); - if (!backupScript.exists()) - (0, commands_1.fail)("./backup.sh does not exist! aborting"); - var backupProcess_1 = new ProcessBuilder(backupScript.absolutePath()) - .directory(Core.settings.getDataDirectory().file()) - .redirectErrorStream(true) - .redirectOutput(ProcessBuilder.Redirect.INHERIT) - .start(); - Threads.daemon(function () { - backupProcess_1.waitFor(); - if (backupProcess_1.exitValue() == 0) { - outputSuccess("Successfully created a backup."); - Core.app.post(function () { - playersToPrune.forEach(function (u) { delete players_1.FishPlayer.cachedPlayers[u.uuid]; }); - outputSuccess("Pruned ".concat(playersToPrune.length, " players.")); - }); - } - else { - outputFail("Backup failed!"); - } - }); - } - else { - outputSuccess("Pruning would remove fish data for ".concat(playersToPrune.length, " players with no data and (1 join or inactive with <10 joins). (Mindustry data will remain.)\nRun \"prune y\" to prune data.")); - } - } - }, - backup: { - args: [], - description: "Creates a backup of the settings.bin file.", - handler: function (_a) { - var output = _a.output, outputFail = _a.outputFail, outputSuccess = _a.outputSuccess; - output("Creating backup..."); - var backupScript = Core.settings.getDataDirectory().child("backup.sh"); - if (!backupScript.exists()) - (0, commands_1.fail)("./backup.sh does not exist! aborting"); - var backupProcess = new ProcessBuilder(backupScript.absolutePath()) - .directory(Core.settings.getDataDirectory().file()) - .redirectErrorStream(true) - .redirectOutput(ProcessBuilder.Redirect.INHERIT) - .start(); - Threads.daemon(function () { - backupProcess.waitFor(); - if (backupProcess.exitValue() == 0) - outputSuccess("Successfully created a backup."); - else - outputFail("Backup failed!"); - }); - } - }, - updateMaps: { - args: [], - description: 'Attempt to fetch and update all map files', - handler: function (_a) { - var output = _a.output, outputSuccess = _a.outputSuccess, outputFail = _a.outputFail; - output("Updating maps... (this may take a while)"); - (0, files_1.updateMaps)() - .then(function (changed) { return outputSuccess(changed ? "Maps were updated." : "Map update completed, already up to date."); }) - .catch(function (message) { return outputFail("Map update failed: ".concat(String(message))); }); - }, - }, - switchall: { - args: ["server:string"], - description: "Forces all currently online players to another server.", - handler: function (_a) { - var _b; - var args = _a.args; - if (globals_1.ipPortPattern.test(args.server)) { - Groups.player.each(function (target) { - //direct connect - Call.connect.apply(Call, __spreadArray([target.con], __read(args.server.split(":")), false)); - }); - } - else { - var server_1 = (_b = config_1.FishServer.byName(args.server)) !== null && _b !== void 0 ? _b : (0, commands_1.fail)("Unknown server ".concat(args.server, ". Valid options: ").concat(config_1.FishServer.all.map(function (s) { return s.name; }).join(", "))); - Groups.player.each(function (target) { - Call.connect(target.con, server_1.ip, server_1.port); - }); - } - } - }, - mute: { - args: ['player:player'], - description: 'Stops a player from chatting.', - handler: function (_a) { - return __awaiter(this, arguments, void 0, function (_b) { - var args = _b.args, outputSuccess = _b.outputSuccess, f = _b.f; - return __generator(this, function (_c) { - switch (_c.label) { - case 0: - if (args.player.muted) - (0, commands_1.fail)(f(templateObject_8 || (templateObject_8 = __makeTemplateObject(["Player ", " is already muted."], ["Player ", " is already muted."])), args.player)); - return [4 /*yield*/, args.player.mute("console")]; - case 1: - _c.sent(); - (0, utils_1.logAction)('muted', "console", args.player); - outputSuccess(f(templateObject_9 || (templateObject_9 = __makeTemplateObject(["Muted player ", "."], ["Muted player ", "."])), args.player)); - return [2 /*return*/]; - } - }); - }); - } - }, - unmute: { - args: ['player:player'], - description: 'Unmutes a player', - handler: function (_a) { - return __awaiter(this, arguments, void 0, function (_b) { - var args = _b.args, outputSuccess = _b.outputSuccess, f = _b.f; - return __generator(this, function (_c) { - switch (_c.label) { - case 0: - if (!args.player.muted && args.player.autoflagged) - (0, commands_1.fail)(f(templateObject_10 || (templateObject_10 = __makeTemplateObject(["Player ", " is not muted, but they are autoflagged. You probably want to free them with /free."], ["Player ", " is not muted, but they are autoflagged. You probably want to free them with /free."])), args.player)); - if (!args.player.muted) - (0, commands_1.fail)(f(templateObject_11 || (templateObject_11 = __makeTemplateObject(["Player ", " is not muted."], ["Player ", " is not muted."])), args.player)); - return [4 /*yield*/, args.player.unmute("console")]; - case 1: - _c.sent(); - (0, utils_1.logAction)('unmuted', "console", args.player); - outputSuccess(f(templateObject_12 || (templateObject_12 = __makeTemplateObject(["Unmuted player ", "."], ["Unmuted player ", "."])), args.player)); - return [2 /*return*/]; - } - }); - }); - } - }, - free: { - args: ['player:player'], - description: 'Frees a player.', - handler: function (_a) { - return __awaiter(this, arguments, void 0, function (_b) { - var args = _b.args, outputSuccess = _b.outputSuccess, outputFail = _b.outputFail, f = _b.f; - return __generator(this, function (_c) { - switch (_c.label) { - case 0: - if (!args.player.marked()) return [3 /*break*/, 2]; - return [4 /*yield*/, args.player.free("console")]; - case 1: - _c.sent(); - (0, utils_1.logAction)('freed', "console", args.player); - outputSuccess(f(templateObject_13 || (templateObject_13 = __makeTemplateObject(["Player ", " has been unmarked."], ["Player ", " has been unmarked."])), args.player)); - return [3 /*break*/, 3]; - case 2: - if (args.player.autoflagged) { - args.player.autoflagged = false; - if (args.player.connected()) { - args.player.sendMessage("[yellow]You have been unflagged."); - args.player.updateName(); - args.player.forceRespawn(); - } - outputSuccess(f(templateObject_14 || (templateObject_14 = __makeTemplateObject(["Player ", " has been unflagged."], ["Player ", " has been unflagged."])), args.player)); - } - else { - outputFail(f(templateObject_15 || (templateObject_15 = __makeTemplateObject(["Player ", " is not marked or autoflagged."], ["Player ", " is not marked or autoflagged."])), args.player)); - } - _c.label = 3; - case 3: return [2 /*return*/]; - } - }); - }); - } - }, - say: { - args: ["message:string"], - description: "Sends a message to the in-game chat.", - handler: function (_a) { - var message = _a.args.message; - Call.sendMessage("[scarlet][[Server]:[] ".concat(message)); - Log.info("&fi&lcServer: &fr&lw".concat(message)); - globals_1.FishEvents.fire("serverSays", []); - } - }, - whitelist: { - args: ["_:string?"], - description: "Disabled to prevent accidental lag", - handler: function () { - (0, commands_1.fail)("This command has been disabled to prevent lag. Fish servers do not use a whitelist."); - } - }, - loglevel: { - args: ["duration:time?"], - description: "Sets log level to debug", - handler: function (_a) { - var _b = _a.args.duration, duration = _b === void 0 ? funcs_1.Duration.minutes(5) : _b, outputSuccess = _a.outputSuccess; - Log.level = Log.LogLevel.debug; - Timer.schedule(function () { - Log.level = Log.LogLevel.info; - }, duration / 1000); - outputSuccess("Set log level to debug for ".concat((0, utils_1.formatTime)(duration))); - } - } -}); -var templateObject_1, templateObject_2, templateObject_3, templateObject_4, templateObject_5, templateObject_6, templateObject_7, templateObject_8, templateObject_9, templateObject_10, templateObject_11, templateObject_12, templateObject_13, templateObject_14, templateObject_15; +"use strict"; +/* +Copyright © BalaM314, 2026. All Rights Reserved. +This file contains all the console commands, which can be run through the server console. +*/ +var __makeTemplateObject = (this && this.__makeTemplateObject) || function (cooked, raw) { + if (Object.defineProperty) { Object.defineProperty(cooked, "raw", { value: raw }); } else { cooked.raw = raw; } + return cooked; +}; +var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + var desc = Object.getOwnPropertyDescriptor(m, k); + if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { + desc = { enumerable: true, get: function() { return m[k]; } }; + } + Object.defineProperty(o, k2, desc); +}) : (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + o[k2] = m[k]; +})); +var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { + Object.defineProperty(o, "default", { enumerable: true, value: v }); +}) : function(o, v) { + o["default"] = v; +}); +var __importStar = (this && this.__importStar) || (function () { + var ownKeys = function(o) { + ownKeys = Object.getOwnPropertyNames || function (o) { + var ar = []; + for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k; + return ar; + }; + return ownKeys(o); + }; + return function (mod) { + if (mod && mod.__esModule) return mod; + var result = {}; + if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]); + __setModuleDefault(result, mod); + return result; + }; +})(); +var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { + function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } + return new (P || (P = Promise))(function (resolve, reject) { + function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } + function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } + function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } + step((generator = generator.apply(thisArg, _arguments || [])).next()); + }); +}; +var __generator = (this && this.__generator) || function (thisArg, body) { + var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g = Object.create((typeof Iterator === "function" ? Iterator : Object).prototype); + return g.next = verb(0), g["throw"] = verb(1), g["return"] = verb(2), typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g; + function verb(n) { return function (v) { return step([n, v]); }; } + function step(op) { + if (f) throw new TypeError("Generator is already executing."); + while (g && (g = 0, op[0] && (_ = 0)), _) try { + if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t; + if (y = 0, t) op = [op[0] & 2, t.value]; + switch (op[0]) { + case 0: case 1: t = op; break; + case 4: _.label++; return { value: op[1], done: false }; + case 5: _.label++; y = op[1]; op = [0]; continue; + case 7: op = _.ops.pop(); _.trys.pop(); continue; + default: + if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; } + if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; } + if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; } + if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; } + if (t[2]) _.ops.pop(); + _.trys.pop(); continue; + } + op = body.call(thisArg, _); + } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; } + if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true }; + } +}; +var __read = (this && this.__read) || function (o, n) { + var m = typeof Symbol === "function" && o[Symbol.iterator]; + if (!m) return o; + var i = m.call(o), r, ar = [], e; + try { + while ((n === void 0 || n-- > 0) && !(r = i.next()).done) ar.push(r.value); + } + catch (error) { e = { error: error }; } + finally { + try { + if (r && !r.done && (m = i["return"])) m.call(i); + } + finally { if (e) throw e.error; } + } + return ar; +}; +var __values = (this && this.__values) || function(o) { + var s = typeof Symbol === "function" && Symbol.iterator, m = s && o[s], i = 0; + if (m) return m.call(o); + if (o && typeof o.length === "number") return { + next: function () { + if (o && i >= o.length) o = void 0; + return { value: o && o[i++], done: !o }; + } + }; + throw new TypeError(s ? "Object is not iterable." : "Symbol.iterator is not defined."); +}; +var __spreadArray = (this && this.__spreadArray) || function (to, from, pack) { + if (pack || arguments.length === 2) for (var i = 0, l = from.length, ar; i < l; i++) { + if (ar || !(i in from)) { + if (!ar) ar = Array.prototype.slice.call(from, 0, i); + ar[i] = from[i]; + } + } + return to.concat(ar || Array.prototype.slice.call(from)); +}; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.commands = void 0; +var api = __importStar(require("/api")); +var config_1 = require("/config"); +var files_1 = require("/files"); +var fjsContext = __importStar(require("/fjsContext")); +var commands_1 = require("/frameworks/commands"); +var funcs_1 = require("/funcs"); +var globals_1 = require("/globals"); +var players_1 = require("/players"); +var ranks_1 = require("/ranks"); +var utils_1 = require("/utils"); +exports.commands = (0, commands_1.consoleCommandList)({ + setrank: { + args: ["player:player", "rank:rank"], + description: "Set a player's rank.", + handler: function (_a) { + return __awaiter(this, arguments, void 0, function (_b) { + var args = _b.args, outputSuccess = _b.outputSuccess, f = _b.f; + return __generator(this, function (_c) { + switch (_c.label) { + case 0: + if (args.rank == ranks_1.Rank.pi && !config_1.Mode.localDebug) + (0, commands_1.fail)(f(templateObject_1 || (templateObject_1 = __makeTemplateObject(["Rank ", " is immutable."], ["Rank ", " is immutable."])), args.rank)); + if (args.player.immutable() && !config_1.Mode.localDebug) + (0, commands_1.fail)(f(templateObject_2 || (templateObject_2 = __makeTemplateObject(["Player ", " is immutable."], ["Player ", " is immutable."])), args.player)); + return [4 /*yield*/, args.player.setRank(args.rank)]; + case 1: + _c.sent(); + (0, utils_1.logAction)("set rank to ".concat(args.rank.name, " for"), "console", args.player); + outputSuccess(f(templateObject_3 || (templateObject_3 = __makeTemplateObject(["Set rank of player ", " to ", ""], ["Set rank of player ", " to ", ""])), args.player, args.rank)); + args.player.sendMessage("[royal]Your rank has been set to ".concat(args.rank.coloredName(), ".")); + return [2 /*return*/]; + } + }); + }); + } + }, + admin: { + args: ["nothing:string?"], + description: "Use the setrank command instead.", + handler: function () { + (0, commands_1.fail)("Use the \"setrank\" command instead. Hint: \"setrank player admin\""); + } + }, + setflag: { + args: ["player:player", "flag:roleflag", "value:boolean"], + description: "Set a player's role flags.", + handler: function (_a) { + return __awaiter(this, arguments, void 0, function (_b) { + var args = _b.args, outputSuccess = _b.outputSuccess, f = _b.f; + return __generator(this, function (_c) { + switch (_c.label) { + case 0: return [4 /*yield*/, args.player.setFlag(args.flag, args.value)]; + case 1: + _c.sent(); + (0, utils_1.logAction)("set roleflag ".concat(args.flag.name, " to ").concat(args.value, " for"), "console", args.player); + outputSuccess(f(templateObject_4 || (templateObject_4 = __makeTemplateObject(["Set role flag ", " of player ", " to ", ""], ["Set role flag ", " of player ", " to ", ""])), args.flag, args.player, args.value)); + return [2 /*return*/]; + } + }); + }); + } + }, + savePlayers: { + args: [], + description: "Runs FishPlayer.save()", + handler: function (_a) { + var outputSuccess = _a.outputSuccess; + players_1.FishPlayer.saveAll(); + outputSuccess("Successfully wrote fish player data."); + } + }, + info: { + args: ["player:string"], + description: "Find player info(s). Displays all names and ips of a player.", + handler: function (_a) { + return __awaiter(this, arguments, void 0, function (_b) { + function display(infoList) { + var e_2, _a; + var outputString = [""]; + var _loop_1 = function (playerInfo, fishP) { + var flagsText = [ + (fishP === null || fishP === void 0 ? void 0 : fishP.marked()) && (globals_1.maxTime - fishP.unmarkTime < 20000 ? + "&lris marked forever&fr" + : "&lris marked&fr until ".concat((0, utils_1.formatTimeRelative)(fishP.unmarkTime))), + (fishP === null || fishP === void 0 ? void 0 : fishP.muted) && "&lris muted&fr", + (fishP === null || fishP === void 0 ? void 0 : fishP.hasFlag("member")) && "&lmis member&fr", + (fishP === null || fishP === void 0 ? void 0 : fishP.autoflagged) && "&lris autoflagged&fr", + playerInfo.banned && "&bris UUID banned&fr", + ].filter(Boolean).join(", "); + var lastJoinedColor = (fishP === null || fishP === void 0 ? void 0 : fishP.lastJoined) && fishP.lastJoined !== -1 ? (function () { + var timeSinceLastJoin = (Date.now() - fishP.lastJoined) / 1000; + if (timeSinceLastJoin < 3600) + return "&br"; + if (timeSinceLastJoin < 24 * 3600) + return "&by"; + if (timeSinceLastJoin < 7 * 24 * 3600) + return "&lw"; + return "&lk"; + })() : "&fr"; + outputString.push([ + "".concat(lastJoinedColor, "Trace info for player &fr&y").concat(playerInfo.id, "&fr").concat(lastJoinedColor, " / &c\"").concat((0, funcs_1.escapeStringColorsServer)(Strings.stripColors(playerInfo.lastName)), "\" &lk(").concat((0, funcs_1.escapeStringColorsServer)(playerInfo.lastName), ")&fr"), + playerInfo.names.size > 1 && "all names used: ".concat(playerInfo.names.map(funcs_1.escapeStringColorsServer).map(function (n) { return "&c\"".concat(n, "\"&fr"); }).items.join(', ')), + "all IPs used: ".concat(playerInfo.ips.map(function (n) { return (n == playerInfo.lastIP ? '&c' : '&w') + n + '&fr'; }).items.join(", ")), + "joined &c".concat(playerInfo.timesJoined, "&fr times, kicked &c").concat(playerInfo.timesKicked, "&fr times"), + fishP && fishP.lastJoined !== -1 && "Last joined: ".concat((0, utils_1.formatTimeRelative)(fishP.lastJoined)), + fishP && fishP.firstJoined !== -1 && (0, utils_1.formatTimeRelative)(fishP.firstJoined), + fishP && "USID: &c".concat(fishP.usid, "&fr"), + fishP && fishP.rank !== ranks_1.Rank.player && "Rank: &c".concat(fishP.rank.name, "&fr"), + flagsText, + ].filter(Boolean).map(function (l, i) { return i == 0 ? l : '\t' + l; }).join("\n")); + }; + try { + for (var infoList_1 = __values(infoList), infoList_1_1 = infoList_1.next(); !infoList_1_1.done; infoList_1_1 = infoList_1.next()) { + var _b = __read(infoList_1_1.value, 2), playerInfo = _b[0], fishP = _b[1]; + _loop_1(playerInfo, fishP); + } + } + catch (e_2_1) { e_2 = { error: e_2_1 }; } + finally { + try { + if (infoList_1_1 && !infoList_1_1.done && (_a = infoList_1.return)) _a.call(infoList_1); + } + finally { if (e_2) throw e_2.error; } + } + output(outputString.join("\n")); + } + var infoList, playersToFetch, _c, _d, batch, e_1_1, err_1; + var e_1, _e; + var _this = this; + var args = _b.args, output = _b.output, admins = _b.admins; + return __generator(this, function (_f) { + switch (_f.label) { + case 0: + infoList = admins.findByName(args.player) + .toSeq().toArray() + .map(function (p) { return [p, players_1.FishPlayer.getById(p.id)]; }); + if (infoList.length == 0) + (0, commands_1.fail)("No players found."); + playersToFetch = infoList.filter(function (_a) { + var _b = __read(_a, 2), a = _b[0], b = _b[1]; + return !b; + }).map(function (_a) { + var _b = __read(_a, 2), a = _b[0], b = _b[1]; + return a; + }); + if (!(playersToFetch.length == 0)) return [3 /*break*/, 1]; + display(infoList); + return [3 /*break*/, 13]; + case 1: + //Attempt to fetch data + //If there are too many players, give up + if (playersToFetch.length > 50) + display(infoList); + output("Fetching data..."); + _f.label = 2; + case 2: + _f.trys.push([2, 11, , 12]); + _f.label = 3; + case 3: + _f.trys.push([3, 8, 9, 10]); + _c = __values((0, funcs_1.to2DArray)(playersToFetch, 10)), _d = _c.next(); + _f.label = 4; + case 4: + if (!!_d.done) return [3 /*break*/, 7]; + batch = _d.value; + return [4 /*yield*/, Promise.all(batch.map(function (info) { return __awaiter(_this, void 0, void 0, function () { + var data, fishP; + return __generator(this, function (_a) { + switch (_a.label) { + case 0: return [4 /*yield*/, api.getFishPlayerData(info.id)]; + case 1: + data = _a.sent(); + if (data) { + fishP = players_1.FishPlayer.createFromInfo(info); + fishP.updateData(data); + players_1.FishPlayer.cachedPlayers[info.id] = fishP; + } + return [2 /*return*/]; + } + }); + }); }))]; + case 5: + _f.sent(); + _f.label = 6; + case 6: + _d = _c.next(); + return [3 /*break*/, 4]; + case 7: return [3 /*break*/, 10]; + case 8: + e_1_1 = _f.sent(); + e_1 = { error: e_1_1 }; + return [3 /*break*/, 10]; + case 9: + try { + if (_d && !_d.done && (_e = _c.return)) _e.call(_c); + } + finally { if (e_1) throw e_1.error; } + return [7 /*endfinally*/]; + case 10: return [3 /*break*/, 12]; + case 11: + err_1 = _f.sent(); + Log.err(err_1); + return [3 /*break*/, 12]; + case 12: + infoList = admins.findByName(args.player) + .toSeq().toArray() + .map(function (p) { return [p, players_1.FishPlayer.getById(p.id)]; }); + display(infoList); + _f.label = 13; + case 13: return [2 /*return*/]; + } + }); + }); + } + }, + infoonline: { + args: ["player:string"], + description: "Display information about an online player.", + handler: function (_a) { + var e_3, _b; + var args = _a.args, output = _a.output, admins = _a.admins; + var infoList = args.player == "*" ? players_1.FishPlayer.getAllOnline() : players_1.FishPlayer.getAllByName(args.player, false); + if (infoList.length == 0) + (0, commands_1.fail)("Nobody with that name could be found."); + var outputString = [""]; + var _loop_2 = function (player) { + var playerInfo = admins.getInfo(player.uuid); + outputString.push("Info for player &c\"".concat(player.cleanedName, "\" &lk(").concat(player.name, ")&fr\n\tUUID: &c\"").concat(playerInfo.id, "\"&fr\n\tUSID: &c").concat(player.usid ? "\"".concat(player.usid, "\"") : "unknown", "&fr\n\tall names used: ").concat(playerInfo.names.map(function (n) { return "&c\"".concat(n, "\"&fr"); }).items.join(', '), "\n\tall IPs used: ").concat(playerInfo.ips.map(function (n) { return (n == playerInfo.lastIP ? '&c' : '&w') + n + '&fr'; }).items.join(", "), "\n\tjoined &c").concat(playerInfo.timesJoined, "&fr times, kicked &c").concat(playerInfo.timesKicked, "&fr times\n\trank: &c").concat(player.rank.name, "&fr").concat((player.marked() ? ", &lris marked&fr" : "") + (player.muted ? ", &lris muted&fr" : "") + (player.hasFlag("member") ? ", &lmis member&fr" : "") + (player.autoflagged ? ", &lris autoflagged&fr" : ""))); + }; + try { + for (var infoList_2 = __values(infoList), infoList_2_1 = infoList_2.next(); !infoList_2_1.done; infoList_2_1 = infoList_2.next()) { + var player = infoList_2_1.value; + _loop_2(player); + } + } + catch (e_3_1) { e_3 = { error: e_3_1 }; } + finally { + try { + if (infoList_2_1 && !infoList_2_1.done && (_b = infoList_2.return)) _b.call(infoList_2); + } + finally { if (e_3) throw e_3.error; } + } + output(outputString.join("\n")); + } + }, + unblacklist: { + args: ["ip:string"], + description: "Unblacklists an ip from the DOS blacklist.", + handler: function (_a) { + var args = _a.args, output = _a.output, admins = _a.admins; + if (args.ip === '*') { + var size = admins.dosBlacklist.size; + if (size == 0) + (0, commands_1.fail)('DOS blacklist is already empty.'); + admins.dosBlacklist.clear(); + output("Cleared ".concat(size, " IPs from the DOS blacklist.")); + } + else { + if (admins.dosBlacklist.remove(args.ip)) + output("Removed ".concat(args.ip, " from the DOS blacklist.")); + else + (0, commands_1.fail)("IP address ".concat(args.ip, " is not DOS blacklisted.")); + } + } + }, + blacklist: { + args: ["verbose:boolean?"], + description: "Allows you to view the DOS blacklist.", + handler: function (_a) { + var args = _a.args, output = _a.output, admins = _a.admins; + var blacklist = admins.dosBlacklist; + if (blacklist.isEmpty()) + (0, commands_1.fail)("The blacklist is empty"); + if (args.verbose) { + var outputString_1 = ["DOS Blacklist:"]; + blacklist.each(function (ip) { + var info = admins.findByIP(ip); + if (info) { + outputString_1.push("IP: &c".concat(ip, "&fr UUID: &c\"").concat(info.id, "\"&fr Last name used: &c\"").concat(info.plainLastName(), "\"&fr")); + } + }); + output(outputString_1.join("\n")); + output("".concat(blacklist.size, " blacklisted IPs")); + } + else { + output(blacklist.toString()); + output("".concat(blacklist.size, " blacklisted IPs")); + } + } + }, + whack: { + args: ["target:string"], + description: "Whacks (ipbans) a player.", + handler: function (_a) { + var args = _a.args, output = _a.output, outputFail = _a.outputFail, admins = _a.admins; + var range; + if (globals_1.ipPattern.test(args.target)) { + //target is an ip + api.ban({ ip: args.target }); + var info = admins.findByIP(args.target); + if (info) + (0, utils_1.logAction)("whacked", "console", info); + else + (0, utils_1.logAction)("console ip-whacked ".concat(args.target)); + if (admins.isIPBanned(args.target)) { + output("IP &c\"".concat(args.target, "\"&fr is already banned. Ban was synced to other servers.")); + } + else { + admins.banPlayerIP(args.target); + output("&lrIP &c\"".concat(args.target, "\"&lr was banned. Ban was synced to other servers.")); + } + } + else if ((range = (0, utils_1.getIPRange)(args.target)) != null) { + if (admins.subnetBans.contains(boolf(function (ip) { return ip.replace(/\.$/, "") == range; }))) { + output("Subnet &c\"".concat(range, "\"&fr is already banned.")); + } + else { + admins.subnetBans.add(range); + output("&lrIP range &c\"".concat(range, "\"&lr was banned. Subnet bans are not synced.")); + } + } + else if (globals_1.uuidPattern.test(args.target)) { + var info = admins.getInfoOptional(args.target); + if (info) + (0, utils_1.logAction)("whacked", "console", info); + else + (0, utils_1.logAction)("console ip-whacked ".concat(args.target)); + if (admins.isIDBanned(args.target)) { + api.ban({ uuid: args.target }); + output("UUID &c\"".concat(args.target, "\"&fr is already banned. Ban was synced to other servers.")); + } + else { + admins.banPlayerID(args.target); + if (info) { + admins.banPlayerIP(info.lastIP); + api.ban({ uuid: args.target, ip: info.lastIP }); + output("&lrUUID &c\"".concat(args.target, "\" &lrwas banned. IP &c\"").concat(info.lastIP, "\"&lr was banned. Ban was synced to other servers.")); + } + else { + api.ban({ uuid: args.target }); + output("&lrUUID &c\"".concat(args.target, "\" &lrwas banned. Ban was synced to other servers. Warning: no stored info for this UUID, player may not exist. Unable to determine IP.")); + } + } + } + else { + var player = players_1.FishPlayer.getOneMindustryPlayerByName(args.target); + if (player === "none") { + outputFail("Could not find a player name matching &c\"".concat(args.target, "\"")); + } + else if (player === "multiple") { + outputFail("Name &c\"".concat(args.target, "\"&fr could refer to more than one player.")); + } + else { + if (player.admin) + (0, commands_1.fail)("Player &c\"".concat(player.name, "\"&fr is an admin, you probably don't want to ban them.")); + var ip = player.ip(); + var uuid = player.uuid(); + admins.banPlayerID(uuid); + admins.banPlayerIP(ip); + (0, utils_1.logAction)("console whacked ".concat(Strings.stripColors(player.name), " (`").concat(uuid, "`/`").concat(ip, "`)")); + api.ban({ uuid: uuid, ip: ip }); + output("&lrIP &c\"".concat(ip, "\"&lr was banned. UUID &c\"").concat(uuid, "\"&lr was banned. Ban was synced to other servers.")); + } + } + (0, utils_1.updateBans)(function (player) { return "[scarlet]Player [yellow]".concat(player.name, "[scarlet] has been whacked."); }); + } + }, + unwhack: { + args: ["target:string"], + description: "Unbans a player.", + handler: function (_a) { + var args = _a.args, output = _a.output, admins = _a.admins; + var range; + if (globals_1.ipPattern.test(args.target)) { + //target is an ip + if (players_1.FishPlayer.removePunishedIP(args.target)) { + output("Removed IP &c\"".concat(args.target, "\"&fr from the anti-evasion list.")); + } + if (admins.kickedIPs.remove(args.target)) { + output("Removed temporary kick for IP &c\"".concat(args.target, "\"&fr.")); + } + output("Checking ban status..."); + api.getBanned({ ip: args.target }, function (banned) { + if (banned) { + api.unban({ ip: args.target }); + (0, utils_1.logAction)("console unbanned ip `".concat(args.target, "`")); + output("IP &c\"".concat(args.target, "\"&fr has been globally unbanned.")); + } + else { + output("IP &c\"".concat(args.target, "\"&fr is not globally banned.")); + } + if (admins.isIPBanned(args.target)) { + admins.unbanPlayerIP(args.target); + output("IP &c\"".concat(args.target, "\"&fr has been locally unbanned.")); + } + else { + output("IP &c\"".concat(args.target, "\"&fr was not locally banned.")); + } + var size = admins.subnetBans.size; + admins.subnetBans.removeAll(function (r) { return args.target.startsWith(r); }); + if (admins.subnetBans.size < size) { + output("Unbanned IP ranges affecting this IP."); + } + }); + } + else if ((range = (0, utils_1.getIPRange)(args.target)) != null) { + if (admins.subnetBans.remove(function (b) { return b.replace(/\.$/, ".") == range.replace(/\.$/, "."); })) { + output("IP range &c\"".concat(range, "\"&fr was unbanned.")); + } + else { + output("IP range &c\"".concat(range, "\"&fr was not banned.")); + } + } + else if (globals_1.uuidPattern.test(args.target)) { + if (players_1.FishPlayer.removePunishedUUID(args.target)) { + output("Removed UUID &c\"".concat(args.target, "\"&fr from the anti-evasion list.")); + } + output("Checking ban status..."); + var info_1 = admins.findByIP(args.target); + api.getBanned({ uuid: args.target }, function (banned) { + if (banned) { + api.unban({ uuid: args.target }); + (0, utils_1.logAction)("console unbanned uuid `".concat(args.target, "`")); + output("UUID &c\"".concat(args.target, "\"&fr has been globally unbanned.")); + } + else { + output("UUID &c\"".concat(args.target, "\"&fr is not globally banned.")); + } + if (admins.isIDBanned(args.target)) { + admins.unbanPlayerID(args.target); + output("UUID &c\"".concat(args.target, "\"&fr has been locally unbanned.")); + } + else { + output("UUID &c\"".concat(args.target, "\"&fr was not locally banned.")); + } + if (info_1) { + if (info_1.lastKicked > 0) { + info_1.lastKicked = 0; + output("Removed temporary kick for UUID &c\"".concat(args.target, "\"&fr.")); + } + output("You may also want to consider unbanning the IP \"".concat(info_1.lastIP, "\".")); + } + }); + } + else { + (0, commands_1.fail)("Cannot unban by name; please use the info or search commands to find the IP and UUID of the player you are looking for."); + } + } + }, + ban: { + args: ["any:string"], + description: "Please use the whack command instead.", + handler: function () { + (0, commands_1.fail)("Use the whack command instead."); + } + }, + unban: { + args: ["any:string"], + description: "Please use the unwhack command instead.", + handler: function () { + (0, commands_1.fail)("Use the unwhack command instead."); + } + }, + "subnet-ban": { + args: ["any:string?", "anyb:string?"], + description: "Please use the whack and unwhack commands instead.", + handler: function (_a) { + var args = _a.args, output = _a.output, admins = _a.admins; + if (args.any && args.any != "list") + (0, commands_1.fail)("Use the whack and unwhack commands instead."); + output("List of all subnet bans:"); + output(admins.subnetBans.toString("\n")); + } + }, + joinbell: { + args: ["on:boolean?"], + description: "Toggles the join bell function.", + handler: function (_a) { + var _b = _a.args.on, on = _b === void 0 ? !globals_1.fishState.joinBell : _b; + globals_1.fishState.joinBell = on; + if (globals_1.fishState.joinBell) { + Log.info("Enabled sound on new player join. Run \"joinbell\" again to turn it off."); + } + else { + Log.info("Disabled sound on new player join."); + } + } + }, + loadfishplayerdata: { + args: ["areyousure:boolean", "fishplayerdata:string"], + description: "Overwrites current fish player data.", + handler: function (_a) { + var args = _a.args, output = _a.output; + if (args.areyousure) { + var before = Object.keys(players_1.FishPlayer.cachedPlayers).length; + players_1.FishPlayer.loadAll(args.fishplayerdata); + output("Loaded fish player data. before:".concat(before, ", after:").concat(Object.keys(players_1.FishPlayer.cachedPlayers).length)); + } + } + }, + resetauth: { + args: ["player:string"], + description: "Removes the USID of the player provided, use this if they are getting kicked with the message \"Authorization failure!\". Specify \"last\" to use the last player that got kicked.", + handler: function (_a) { + var _b, _c; + var args = _a.args, outputSuccess = _a.outputSuccess, outputFail = _a.outputFail, admins = _a.admins; + var player = args.player == "last" ? ((_b = players_1.FishPlayer.lastAuthKicked) !== null && _b !== void 0 ? _b : (0, commands_1.fail)("Nobody has been kicked for authorization failure since the last restart.")) : + (_c = players_1.FishPlayer.getById(args.player)) !== null && _c !== void 0 ? _c : (0, commands_1.fail)(admins.getInfoOptional(args.player) + ? "Player ".concat(args.player, " has joined the server, but their info was not cached, most likely because they have no rank, so there is no stored USID.") + : "Unknown player ".concat(args.player)); + if (player.ranksAtLeast("admin")) + (0, commands_1.fail)("Please use the setusid command instead."); + var oldusid = player.usid; + player.usid = null; + api.setFishPlayerData(player.getData(), 1, true).then(function () { + outputSuccess("Removed the usid of player ".concat(player.name, "/").concat(player.uuid, " (was ").concat(oldusid, ")")); + }).catch(function (err) { + Log.err(err); + outputFail("Failed to remove the usid, please try running the command again."); + }); + } + }, + setusid: { + args: ["uuid:string", "usid:string"], + description: "Sets the USID of a player.", + handler: function (_a) { + var _b; + var args = _a.args, outputSuccess = _a.outputSuccess, outputFail = _a.outputFail, f = _a.f; + if (args.usid.length !== 12) + (0, commands_1.fail)("Invalid USID: should be 12 characters ending with an equal sign"); + var player = (_b = players_1.FishPlayer.lastAuthKicked) !== null && _b !== void 0 ? _b : (0, commands_1.fail)("No authorization failures have occurred since the last restart."); + var oldusid = player.usid; + player.usid = args.usid; + api.setFishPlayerData(player.getData(), 1, true).then(function () { + outputSuccess("Set the usid of player ".concat(player.name, "/").concat(player.uuid, " to ").concat(args.usid, " (was ").concat(oldusid, ")")); + }).catch(function (err) { + Log.err(err); + outputFail("Failed to remove the usid, please try running the command again."); + }); + } + }, + update: { + args: ["branch:string?"], + description: "Updates the plugin.", + handler: function (_a) { + var args = _a.args, output = _a.output, outputSuccess = _a.outputSuccess, outputFail = _a.outputFail; + if (config_1.Mode.localDebug) + (0, commands_1.fail)("Cannot update in local debug mode."); + output("Updating..."); + var path = (0, utils_1.fishCommandsRootDirPath)().toString(); + Threads.thread(function () { + var _a, _b; + try { + var initialVersion = OS.exec("git", "-C", path, "rev-parse", "HEAD"); + var gitFetch = new ProcessBuilder("git", "-C", path, "fetch", "origin") + .redirectErrorStream(true) + .redirectOutput(ProcessBuilder.Redirect.INHERIT) + .start(); + gitFetch.waitFor(); + if (gitFetch.exitValue() == 0) { + outputSuccess("Fetched data, updating files..."); + } + else { + outputFail("Update failed!"); + return; + } + var newVersion = OS.exec("git", "-C", path, "rev-parse", "origin/".concat((_a = args.branch) !== null && _a !== void 0 ? _a : "master")); + if (initialVersion == newVersion) { + outputSuccess("Already up to date."); + return; + } + var gitCheckout = new ProcessBuilder("git", "-C", path, "checkout", "-q", "-f", "origin/".concat((_b = args.branch) !== null && _b !== void 0 ? _b : "master")) + .redirectErrorStream(true) + .redirectOutput(ProcessBuilder.Redirect.INHERIT) + .start(); + gitCheckout.waitFor(); + if (gitCheckout.exitValue() == 0) { + outputSuccess("Updated successfully from ".concat(initialVersion, " to ").concat(newVersion, ". Restart to apply changes.")); + } + else { + outputFail("Update failed!"); + return; + } + } + catch (err) { + Log.err(err); + outputFail("Update failed!"); + } + }); + } + }, + restart: { + args: ["time:number?"], + description: "Restarts the server.", + handler: function (_a) { + var _b; + var time = _a.args.time; + (_b = globals_1.fishState.restartLoopTask) === null || _b === void 0 ? void 0 : _b.cancel(); + var timeInferred = time == undefined; + if (Groups.player.isEmpty()) { + if (time == undefined) { + Log.info("Restarting immediately as no players are online."); + time !== null && time !== void 0 ? time : (time = 0); + } + } + else if (config_1.Gamemode.pvp()) { + time !== null && time !== void 0 ? time : (time = -1); + } + else { + time !== null && time !== void 0 ? time : (time = 60); + } + if (time == -1) { + Call.sendMessage("[accent]---[[[coral]+++[]]---\n[accent]Server restart queued. The server will restart after the current match is over.[]\n[accent]---[[[coral]+++[]]---"); + if (config_1.Gamemode.pvp() && timeInferred) + Log.info("PVP: restart will occur at the end of the current game. Specify a time to override, but &rthat would interrupt the current pvp match, and players would lose their teams.&fr"); + else + Log.info("Restarting once the current game ends."); + globals_1.fishState.restartQueued = true; + } + else { + if (time < 0 || time > 100) + (0, commands_1.fail)("Invalid time: out of valid range."); + (0, utils_1.serverRestartLoop)(time); + if (time == 0) + Log.info("Restarting now."); + else + Log.info("Restarting in ".concat(time, " second").concat(time == 1 ? "" : "s", ".")); + if (config_1.Gamemode.pvp()) { + Call.sendMessage("[accent]---[[[coral]+++[]]---\n[accent]Server restart imminent. [green]We'll be back after 20 seconds.[]\n[accent]---[[[coral]+++[]]---"); + } + else { + Call.sendMessage("[accent]---[[[coral]+++[]]---\n[accent]Server restart imminent. [green]We'll be back after 20 seconds, and all progress will be saved.[]\n[accent]---[[[coral]+++[]]---"); + } + } + } + }, + restartcancel: { + args: [], + description: "Cancels a planned server restart.", + handler: function (_a) { + var _b; + var outputSuccess = _a.outputSuccess; + var task = (_b = globals_1.fishState.restartLoopTask) !== null && _b !== void 0 ? _b : (0, commands_1.fail)("No restart scheduled."); + Call.sendMessage("[scarlet]Aborting..."); + task.cancel(); + Call.sendMessage("[scarlet]Server restart canceled."); + outputSuccess("Canceled restart."); + } + }, + rename: { + args: ["player:player", "newname:string"], + description: "Changes the name of a player.", + handler: function (_a) { + var args = _a.args, f = _a.f, outputSuccess = _a.outputSuccess; + if (args.player.hasPerm("blockTrolling")) + (0, commands_1.fail)(f(templateObject_5 || (templateObject_5 = __makeTemplateObject(["Operation aborted: Player ", " is insufficiently trollable."], ["Operation aborted: Player ", " is insufficiently trollable."])), args.player)); + var oldName = args.player.name; + args.player.player.name = args.player.prefixedName = args.newname; + args.player.shouldUpdateName = false; + outputSuccess("Renamed ".concat(oldName, " to ").concat(args.newname, ".")); + } + }, + fjs: { + args: ["js:string"], + description: "Executes arbitrary javascript code, but has access to fish-commands's variables.", + handler: function (_a) { + var args = _a.args; + fjsContext.runJS(args.js); + } + }, + checkmem: { + args: [], + description: "Checks memory usage of various objects.", + handler: function (_a) { + var output = _a.output; + output("Memory usage:\nTotal: ".concat(Math.round(Core.app.getJavaHeap() / (Math.pow(2, 10))), " KB\nNumber of cached fish players: ").concat(Object.keys(players_1.FishPlayer.cachedPlayers).length, " (stored locally: ").concat(Object.values(players_1.FishPlayer.cachedPlayers).filter(function (p) { return p.shouldCache(); }).length, ")\nFish player data string length: ").concat(players_1.FishPlayer.getFishPlayersString.length, " (").concat(Core.settings.getInt("fish-subkeys"), " subkeys)\nLength of tilelog entries: ").concat(Math.round(Object.values(globals_1.tileHistory).reduce(function (acc, a) { return acc + a.length; }, 0) / (Math.pow(2, 10))), " KB")); + } + }, + stopplayer: { + args: ['player:player', "time:time?", "message:string?"], + description: 'Stops a player.', + handler: function (_a) { + return __awaiter(this, arguments, void 0, function (_b) { + var previousTime, time; + var _c, _d, _e; + var args = _b.args, f = _b.f, outputSuccess = _b.outputSuccess; + return __generator(this, function (_f) { + switch (_f.label) { + case 0: + if (!args.player.marked()) return [3 /*break*/, 2]; + //overload: overwrite stoptime + if (!args.time) + (0, commands_1.fail)(f(templateObject_6 || (templateObject_6 = __makeTemplateObject(["Player ", " is already marked."], ["Player ", " is already marked."])), args.player)); + previousTime = (0, utils_1.formatTime)(args.player.unmarkTime - Date.now()); + return [4 /*yield*/, args.player.updateStopTime(args.time)]; + case 1: + _f.sent(); + outputSuccess(f(templateObject_7 || (templateObject_7 = __makeTemplateObject(["Player ", "'s stop time has been updated to ", " (was ", ")."], ["Player ", "'s stop time has been updated to ", " (was ", ")."])), args.player, (0, utils_1.formatTime)(args.time), previousTime)); + return [2 /*return*/]; + case 2: + time = (_c = args.time) !== null && _c !== void 0 ? _c : funcs_1.Duration.days(7); + if (time + Date.now() > globals_1.maxTime) + (0, commands_1.fail)("Error: time too high."); + return [4 /*yield*/, args.player.stop("console", time, (_d = args.message) !== null && _d !== void 0 ? _d : undefined)]; + case 3: + _f.sent(); + (0, utils_1.logAction)('stopped', "console", args.player, (_e = args.message) !== null && _e !== void 0 ? _e : undefined, time); + Call.sendMessage("[scarlet]Player \"".concat(args.player.prefixedName, "[scarlet]\" has been marked for ").concat((0, utils_1.formatTime)(time)).concat(args.message ? " with reason: [white]".concat(args.message, "[]") : "", ".")); + return [2 /*return*/]; + } + }); + }); + } + }, + stopoffline: { + args: ["uuid:uuid", "time:time?"], + description: "Stops a player by uuid.", + handler: function (_a) { + return __awaiter(this, arguments, void 0, function (_b) { + var stopTime, info, fishP; + var _c = _b.args, uuid = _c.uuid, time = _c.time, outputSuccess = _b.outputSuccess, admins = _b.admins; + return __generator(this, function (_d) { + switch (_d.label) { + case 0: + stopTime = time !== null && time !== void 0 ? time : (globals_1.maxTime - Date.now() - 10000); + info = admins.getInfoOptional(uuid); + if (info == null) + (0, commands_1.fail)("Unknown player ".concat(uuid)); + fishP = players_1.FishPlayer.getFromInfo(info); + return [4 /*yield*/, fishP.stop("console", stopTime)]; + case 1: + _d.sent(); + (0, utils_1.logAction)('stopped', "console", info, undefined, stopTime); + outputSuccess("Player \"".concat(info.lastName, "\" was marked for ").concat((0, utils_1.formatTime)(stopTime), ".")); + return [2 /*return*/]; + } + }); + }); + } + }, + clearfire: { + args: [], + description: "Clears all the fires.", + handler: function (_a) { + var output = _a.output, outputSuccess = _a.outputSuccess; + output("Removing fires..."); + var totalRemoved = 0; + Call.sendMessage("[scarlet][[Fire Department]:[yellow] Fires were reported. Trucks are en-route. Removing all fires shortly."); + Timer.schedule(function () { + totalRemoved += Groups.fire.size(); + Groups.fire.each(function (f) { return f.remove(); }); + Groups.fire.clear(); + }, 2, 0.1, 40); + Timer.schedule(function () { + outputSuccess("Removed ".concat(totalRemoved, " fires.")); + Call.sendMessage("[scarlet][[Fire Department]:[yellow] We've extinguished ".concat(totalRemoved, " fires.")); + }, 6.1); + } + }, + status: { + args: [], + description: "Displays server status.", + handler: function (_a) { + var output = _a.output; + if (Vars.state.isMenu()) + (0, commands_1.fail)("Status: Server closed."); + var uptime = Packages.java.lang.management.ManagementFactory.getRuntimeMXBean().getUptime(); + var numStaff = 0; + players_1.FishPlayer.forEachPlayer(function (p) { + if (p.ranksAtLeast("mod")) + numStaff++; + }); + var uptimeColor = uptime < funcs_1.Duration.days(2) ? "" : + uptime < funcs_1.Duration.days(5) ? "&ly" : + uptime < funcs_1.Duration.days(9) ? "&y" : + "&br"; + output("\nStatus:\nPlaying on map &fi".concat(Vars.state.map.plainName(), "&fr for ").concat((0, utils_1.formatTime)(1000 * Vars.state.tick / 60), "\n").concat(Vars.state.rules.waves ? "Wave &c".concat(Vars.state.wave, "&fr, &c").concat(Math.ceil(Vars.state.wavetime / 60), "&fr seconds until next wave.\n") : "", "&c").concat(Groups.unit.size(), "&fr units, &c").concat(Vars.state.enemies, "&fr enemies, &c").concat(Groups.build.size(), "&fr buildings\nTPS: ").concat((0, utils_1.colorNumber)(Core.graphics.getFramesPerSecond(), function (f) { return f > 58 ? "&g" : f > 30 ? "&y" : f > 10 ? "&r" : "&br&w"; }, "server"), ", Memory: &c").concat(Math.round(Core.app.getJavaHeap() / 1048576), "&fr MB\nServer uptime: ").concat(uptimeColor).concat((0, utils_1.formatTime)(uptime), "&fr (since ").concat((0, utils_1.formatTimestampFull)(Date.now() - uptime), ")\n").concat([ + globals_1.fishState.restartQueued ? "&by&lwRestart queued&fr" : "", + globals_1.fishState.restartLoopTask ? "&by&lwRestarting now&fr" : "", + players_1.FishPlayer.antiBotMode() ? "&br&wANTIBOT ACTIVE!&fr" + (0, utils_1.getAntiBotInfo)("server") : "", + ].filter(function (l) { return l.length > 0; }).join("\n"), "\n").concat((0, utils_1.colorNumber)(Groups.player.size(), function (n) { return n > 0 ? "&c" : "&lr"; }, "server"), " players online, ").concat((0, utils_1.colorNumber)(numStaff, function (n) { return n > 0 ? "&c" : "&lr"; }, "server"), " staff members.\n").concat(players_1.FishPlayer.mapPlayers(function (p) { + return "\t".concat(p.rank.shortPrefix, " &c").concat(p.uuid, "&fr &c").concat(p.name, "&fr"); + }).join("\n") || "&lrNo players connected.&fr", "\n")); + } + }, + tmux: { + args: ["attach:string"], + description: "Oopsie", + handler: function () { + (0, commands_1.fail)("You are already in the Mindustry server console. Please regain situational awareness before running any further commands."); + } + }, + BEGIN: { + args: ["transaction:string"], + description: "Oopsie", + handler: function (_a) { + var args = _a.args; + if (args.transaction == "TRANSACTION") + (0, commands_1.fail)("Not possible :( please download and run locally, and make a backup"); + else + (0, commands_1.fail)("Command not found. Did you mean \"BEGIN TRANSACTION\"?"); + } + }, + prune: { + args: ["confirm:boolean?"], + description: "Prunes fish player data", + handler: function (_a) { + var args = _a.args, admins = _a.admins, outputSuccess = _a.outputSuccess, outputFail = _a.outputFail; + var playersToPrune = Object.values(players_1.FishPlayer.cachedPlayers) + .filter(function (player) { + if (player.hasData()) + return false; + var data = admins.getInfoOptional(player.uuid); + return (!data || + data.timesJoined == 1 || + (data.timesJoined < 10 && + (Date.now() - player.lastJoined) > funcs_1.Duration.months(1))); + }); + if (args.confirm) { + outputSuccess("Creating backup..."); + var backupScript = Core.settings.getDataDirectory().child("backup.sh"); + if (!backupScript.exists()) + (0, commands_1.fail)("./backup.sh does not exist! aborting"); + var backupProcess_1 = new ProcessBuilder(backupScript.absolutePath()) + .directory(Core.settings.getDataDirectory().file()) + .redirectErrorStream(true) + .redirectOutput(ProcessBuilder.Redirect.INHERIT) + .start(); + Threads.daemon(function () { + backupProcess_1.waitFor(); + if (backupProcess_1.exitValue() == 0) { + outputSuccess("Successfully created a backup."); + Core.app.post(function () { + playersToPrune.forEach(function (u) { delete players_1.FishPlayer.cachedPlayers[u.uuid]; }); + outputSuccess("Pruned ".concat(playersToPrune.length, " players.")); + }); + } + else { + outputFail("Backup failed!"); + } + }); + } + else { + outputSuccess("Pruning would remove fish data for ".concat(playersToPrune.length, " players with no data and (1 join or inactive with <10 joins). (Mindustry data will remain.)\nRun \"prune y\" to prune data.")); + } + } + }, + backup: { + args: [], + description: "Creates a backup of the settings.bin file.", + handler: function (_a) { + var output = _a.output, outputFail = _a.outputFail, outputSuccess = _a.outputSuccess; + output("Creating backup..."); + var backupScript = Core.settings.getDataDirectory().child("backup.sh"); + if (!backupScript.exists()) + (0, commands_1.fail)("./backup.sh does not exist! aborting"); + var backupProcess = new ProcessBuilder(backupScript.absolutePath()) + .directory(Core.settings.getDataDirectory().file()) + .redirectErrorStream(true) + .redirectOutput(ProcessBuilder.Redirect.INHERIT) + .start(); + Threads.daemon(function () { + backupProcess.waitFor(); + if (backupProcess.exitValue() == 0) + outputSuccess("Successfully created a backup."); + else + outputFail("Backup failed!"); + }); + } + }, + updateMaps: { + args: [], + description: 'Attempt to fetch and update all map files', + handler: function (_a) { + var output = _a.output, outputSuccess = _a.outputSuccess, outputFail = _a.outputFail; + output("Updating maps... (this may take a while)"); + (0, files_1.updateMaps)() + .then(function (changed) { return outputSuccess(changed ? "Maps were updated." : "Map update completed, already up to date."); }) + .catch(function (message) { return outputFail("Map update failed: ".concat(String(message))); }); + }, + }, + switchall: { + args: ["server:string"], + description: "Forces all currently online players to another server.", + handler: function (_a) { + var _b; + var args = _a.args; + if (globals_1.ipPortPattern.test(args.server)) { + Groups.player.each(function (target) { + //direct connect + Call.connect.apply(Call, __spreadArray([target.con], __read(args.server.split(":")), false)); + }); + } + else { + var server_1 = (_b = config_1.FishServer.byName(args.server)) !== null && _b !== void 0 ? _b : (0, commands_1.fail)("Unknown server ".concat(args.server, ". Valid options: ").concat(config_1.FishServer.all.map(function (s) { return s.name; }).join(", "))); + Groups.player.each(function (target) { + Call.connect(target.con, server_1.ip, server_1.port); + }); + } + } + }, + mute: { + args: ['player:player'], + description: 'Stops a player from chatting.', + handler: function (_a) { + return __awaiter(this, arguments, void 0, function (_b) { + var args = _b.args, outputSuccess = _b.outputSuccess, f = _b.f; + return __generator(this, function (_c) { + switch (_c.label) { + case 0: + if (args.player.muted) + (0, commands_1.fail)(f(templateObject_8 || (templateObject_8 = __makeTemplateObject(["Player ", " is already muted."], ["Player ", " is already muted."])), args.player)); + return [4 /*yield*/, args.player.mute("console")]; + case 1: + _c.sent(); + (0, utils_1.logAction)('muted', "console", args.player); + outputSuccess(f(templateObject_9 || (templateObject_9 = __makeTemplateObject(["Muted player ", "."], ["Muted player ", "."])), args.player)); + return [2 /*return*/]; + } + }); + }); + } + }, + unmute: { + args: ['player:player'], + description: 'Unmutes a player', + handler: function (_a) { + return __awaiter(this, arguments, void 0, function (_b) { + var args = _b.args, outputSuccess = _b.outputSuccess, f = _b.f; + return __generator(this, function (_c) { + switch (_c.label) { + case 0: + if (!args.player.muted && args.player.autoflagged) + (0, commands_1.fail)(f(templateObject_10 || (templateObject_10 = __makeTemplateObject(["Player ", " is not muted, but they are autoflagged. You probably want to free them with /free."], ["Player ", " is not muted, but they are autoflagged. You probably want to free them with /free."])), args.player)); + if (!args.player.muted) + (0, commands_1.fail)(f(templateObject_11 || (templateObject_11 = __makeTemplateObject(["Player ", " is not muted."], ["Player ", " is not muted."])), args.player)); + return [4 /*yield*/, args.player.unmute("console")]; + case 1: + _c.sent(); + (0, utils_1.logAction)('unmuted', "console", args.player); + outputSuccess(f(templateObject_12 || (templateObject_12 = __makeTemplateObject(["Unmuted player ", "."], ["Unmuted player ", "."])), args.player)); + return [2 /*return*/]; + } + }); + }); + } + }, + free: { + args: ['player:player'], + description: 'Frees a player.', + handler: function (_a) { + return __awaiter(this, arguments, void 0, function (_b) { + var args = _b.args, outputSuccess = _b.outputSuccess, outputFail = _b.outputFail, f = _b.f; + return __generator(this, function (_c) { + switch (_c.label) { + case 0: + if (!args.player.marked()) return [3 /*break*/, 2]; + return [4 /*yield*/, args.player.free("console")]; + case 1: + _c.sent(); + (0, utils_1.logAction)('freed', "console", args.player); + outputSuccess(f(templateObject_13 || (templateObject_13 = __makeTemplateObject(["Player ", " has been unmarked."], ["Player ", " has been unmarked."])), args.player)); + return [3 /*break*/, 3]; + case 2: + if (args.player.autoflagged) { + args.player.autoflagged = false; + if (args.player.connected()) { + args.player.sendMessage("[yellow]You have been unflagged."); + args.player.updateName(); + args.player.forceRespawn(); + } + outputSuccess(f(templateObject_14 || (templateObject_14 = __makeTemplateObject(["Player ", " has been unflagged."], ["Player ", " has been unflagged."])), args.player)); + } + else { + outputFail(f(templateObject_15 || (templateObject_15 = __makeTemplateObject(["Player ", " is not marked or autoflagged."], ["Player ", " is not marked or autoflagged."])), args.player)); + } + _c.label = 3; + case 3: return [2 /*return*/]; + } + }); + }); + } + }, + say: { + args: ["message:string"], + description: "Sends a message to the in-game chat.", + handler: function (_a) { + var message = _a.args.message; + Call.sendMessage("[scarlet][[Server]:[] ".concat(message)); + Log.info("&fi&lcServer: &fr&lw".concat(message)); + globals_1.FishEvents.fire("serverSays", []); + } + }, + whitelist: { + args: ["_:string?"], + description: "Disabled to prevent accidental lag", + handler: function () { + (0, commands_1.fail)("This command has been disabled to prevent lag. Fish servers do not use a whitelist."); + } + }, + loglevel: { + args: ["duration:time?"], + description: "Sets log level to debug", + handler: function (_a) { + var _b = _a.args.duration, duration = _b === void 0 ? funcs_1.Duration.minutes(5) : _b, outputSuccess = _a.outputSuccess; + Log.level = Log.LogLevel.debug; + Timer.schedule(function () { + Log.level = Log.LogLevel.info; + }, duration / 1000); + outputSuccess("Set log level to debug for ".concat((0, utils_1.formatTime)(duration))); + } + } +}); +var templateObject_1, templateObject_2, templateObject_3, templateObject_4, templateObject_5, templateObject_6, templateObject_7, templateObject_8, templateObject_9, templateObject_10, templateObject_11, templateObject_12, templateObject_13, templateObject_14, templateObject_15; diff --git a/build/scripts/commands/general.js b/build/scripts/commands/general.js index 64d7a3a7..649dc705 100644 --- a/build/scripts/commands/general.js +++ b/build/scripts/commands/general.js @@ -1,1553 +1,1553 @@ -"use strict"; -/* -Copyright © BalaM314, 2026. All Rights Reserved. -This file contains most in-game chat commands that can be run by untrusted players. -*/ -var __makeTemplateObject = (this && this.__makeTemplateObject) || function (cooked, raw) { - if (Object.defineProperty) { Object.defineProperty(cooked, "raw", { value: raw }); } else { cooked.raw = raw; } - return cooked; -}; -var __assign = (this && this.__assign) || function () { - __assign = Object.assign || function(t) { - for (var s, i = 1, n = arguments.length; i < n; i++) { - s = arguments[i]; - for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p)) - t[p] = s[p]; - } - return t; - }; - return __assign.apply(this, arguments); -}; -var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { - if (k2 === undefined) k2 = k; - var desc = Object.getOwnPropertyDescriptor(m, k); - if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { - desc = { enumerable: true, get: function() { return m[k]; } }; - } - Object.defineProperty(o, k2, desc); -}) : (function(o, m, k, k2) { - if (k2 === undefined) k2 = k; - o[k2] = m[k]; -})); -var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { - Object.defineProperty(o, "default", { enumerable: true, value: v }); -}) : function(o, v) { - o["default"] = v; -}); -var __importStar = (this && this.__importStar) || (function () { - var ownKeys = function(o) { - ownKeys = Object.getOwnPropertyNames || function (o) { - var ar = []; - for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k; - return ar; - }; - return ownKeys(o); - }; - return function (mod) { - if (mod && mod.__esModule) return mod; - var result = {}; - if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]); - __setModuleDefault(result, mod); - return result; - }; -})(); -var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { - function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } - return new (P || (P = Promise))(function (resolve, reject) { - function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } - function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } - function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } - step((generator = generator.apply(thisArg, _arguments || [])).next()); - }); -}; -var __generator = (this && this.__generator) || function (thisArg, body) { - var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g = Object.create((typeof Iterator === "function" ? Iterator : Object).prototype); - return g.next = verb(0), g["throw"] = verb(1), g["return"] = verb(2), typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g; - function verb(n) { return function (v) { return step([n, v]); }; } - function step(op) { - if (f) throw new TypeError("Generator is already executing."); - while (g && (g = 0, op[0] && (_ = 0)), _) try { - if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t; - if (y = 0, t) op = [op[0] & 2, t.value]; - switch (op[0]) { - case 0: case 1: t = op; break; - case 4: _.label++; return { value: op[1], done: false }; - case 5: _.label++; y = op[1]; op = [0]; continue; - case 7: op = _.ops.pop(); _.trys.pop(); continue; - default: - if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; } - if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; } - if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; } - if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; } - if (t[2]) _.ops.pop(); - _.trys.pop(); continue; - } - op = body.call(thisArg, _); - } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; } - if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true }; - } -}; -var __read = (this && this.__read) || function (o, n) { - var m = typeof Symbol === "function" && o[Symbol.iterator]; - if (!m) return o; - var i = m.call(o), r, ar = [], e; - try { - while ((n === void 0 || n-- > 0) && !(r = i.next()).done) ar.push(r.value); - } - catch (error) { e = { error: error }; } - finally { - try { - if (r && !r.done && (m = i["return"])) m.call(i); - } - finally { if (e) throw e.error; } - } - return ar; -}; -var __spreadArray = (this && this.__spreadArray) || function (to, from, pack) { - if (pack || arguments.length === 2) for (var i = 0, l = from.length, ar; i < l; i++) { - if (ar || !(i in from)) { - if (!ar) ar = Array.prototype.slice.call(from, 0, i); - ar[i] = from[i]; - } - } - return to.concat(ar || Array.prototype.slice.call(from)); -}; -var __values = (this && this.__values) || function(o) { - var s = typeof Symbol === "function" && Symbol.iterator, m = s && o[s], i = 0; - if (m) return m.call(o); - if (o && typeof o.length === "number") return { - next: function () { - if (o && i >= o.length) o = void 0; - return { value: o && o[i++], done: !o }; - } - }; - throw new TypeError(s ? "Object is not iterable." : "Symbol.iterator is not defined."); -}; -var _a; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.commands = void 0; -var achievements_1 = require("/achievements"); -var api = __importStar(require("/api")); -var config_1 = require("/config"); -var commands_1 = require("/frameworks/commands"); -var menus_1 = require("/frameworks/menus"); -var funcs_1 = require("/funcs"); -var globals_1 = require("/globals"); -var maps_1 = require("/maps"); -var players_1 = require("/players"); -var ranks_1 = require("/ranks"); -var utils_1 = require("/utils"); -var votes_1 = require("/votes"); -exports.commands = (0, commands_1.commandList)(__assign(__assign({ about: { - args: [], - description: 'Prints information about the plugin.', - perm: commands_1.Perm.none, - handler: function (_a) { - var _b, _c; - var output = _a.output; - output("[accent][cyan]fish-commands[] is the monolithic plugin used for the Fish servers' features.\n[accent]==========\n[accent]Source code available at: [cyan]https://github.com/Fish-Community/fish-commands/\n[accent]Current plugin version: [cyan]".concat((_c = (_b = globals_1.fishPlugin.version) === null || _b === void 0 ? void 0 : _b.slice(0, 8)) !== null && _c !== void 0 ? _c : "[scarlet]null[]", "[]")); - } - }, unpause: (0, commands_1.command)({ - args: [], - description: 'Unpauses the game.', - perm: commands_1.Perm.trusted, - requirements: [commands_1.Req.mode('pvp')], - init: function () { - var data = { unpaused: false }; - Events.on(EventType.PlayEvent, function () { - if (data.unpaused) { - data.unpaused = false; - Vars.state.rules.pvpAutoPause = true; - } - }); - return data; - }, - handler: function (_a) { - var data = _a.data, outputSuccess = _a.outputSuccess; - Vars.state.rules.pvpAutoPause = false; - data.unpaused = true; - Core.app.post(function () { return Vars.state.set(GameState.State.playing); }); - outputSuccess("Unpaused."); - }, - }), tp: { - args: ['player:player'], - description: 'Teleport to another player.', - perm: commands_1.Perm.play, - requirements: [commands_1.Req.modeNot("pvp")], - handler: function (_a) { - var _b, _c, _d; - var args = _a.args, sender = _a.sender; - if (!((_b = sender.unit()) === null || _b === void 0 ? void 0 : _b.spawnedByCore)) - (0, commands_1.fail)("Can only teleport while in a core unit."); - if (sender.team() !== args.player.team()) - (0, commands_1.fail)("Cannot teleport to players on another team."); - if ((_d = (_c = sender.unit()).hasPayload) === null || _d === void 0 ? void 0 : _d.call(_c)) - (0, commands_1.fail)("Cannot teleport to players while holding a payload."); - (0, utils_1.teleportPlayer)(sender.player, args.player.player); - }, - }, clean: (0, commands_1.command)({ - args: [], - description: 'Removes all boulders from the map.', - perm: commands_1.Perm.play, - requirements: [], - data: { lastRanMapStartTime: (_a = maps_1.PartialMapRun.current) === null || _a === void 0 ? void 0 : _a.startTime }, - handler: function (_a) { - return __awaiter(this, arguments, void 0, function (_b) { - var array, removed, i, t; - var sender = _b.sender, outputSuccess = _b.outputSuccess, data = _b.data; - return __generator(this, function (_c) { - switch (_c.label) { - case 0: - if (!maps_1.PartialMapRun.current) - (0, commands_1.fail)("This game is already over."); - if (data.lastRanMapStartTime == maps_1.PartialMapRun.current.startTime) - (0, commands_1.fail)("This command was already run on this map."); - data.lastRanMapStartTime = maps_1.PartialMapRun.current.startTime; - Timer.schedule(function () { return Call.sound(sender.con, Sounds.rockBreak, 1, 1, 0); }, 0, 0.05, 10); - array = ArcReflect.get(Vars.world.tiles, "array"); - removed = 0; - i = 0; - _c.label = 1; - case 1: - if (!(i < array.length)) return [3 /*break*/, 4]; - t = array[i]; - if (!(t.breakable() && t.block() instanceof Prop)) return [3 /*break*/, 3]; - t.removeNet(); - removed++; - if (!(removed % 500 == 0)) return [3 /*break*/, 3]; - return [4 /*yield*/, (0, funcs_1.delay)(100)]; - case 2: - _c.sent(); - _c.label = 3; - case 3: - i++; - return [3 /*break*/, 1]; - case 4: - outputSuccess("Cleared the map of boulders."); - return [2 /*return*/]; - } - }); - }); - } - }), die: { - args: [], - description: 'Kills your unit.', - perm: commands_1.Perm.mod.exceptModes({ - sandbox: commands_1.Perm.play - }, "You do not have permission to die."), - handler: function (_a) { - var _b; - var sender = _a.sender; - (_b = sender.unit()) === null || _b === void 0 ? void 0 : _b.kill(); - }, - }, discord: { - args: [], - description: 'Takes you to our discord.', - perm: commands_1.Perm.none, - handler: function (_a) { - var sender = _a.sender; - Call.openURI(sender.con, config_1.text.discordURL); - }, - }, tilelog: (0, commands_1.command)({ - args: ['persist:boolean?', 'showUUID:boolean?'], - description: 'Checks the history of a tile.', - perm: commands_1.Perm.none, - data: { showUUID: true }, - handler: function (_a) { - var args = _a.args, output = _a.output, outputSuccess = _a.outputSuccess, currentTapMode = _a.currentTapMode, handleTaps = _a.handleTaps, sender = _a.sender, data = _a.data; - var changed = args.showUUID !== undefined && args.showUUID != data.showUUID; - if (args.showUUID !== undefined) { - if (!sender.hasPerm("viewUUIDs")) - (0, commands_1.fail)("You do not have permission to show UUIDs."); - data.showUUID = args.showUUID; - } - if (args.persist && currentTapMode !== "on") { - outputSuccess("Tilelog mode enabled. Click tiles to check their recent history. Run /tilelog to disable."); - handleTaps("on"); - } - else if (args.persist && changed) { - outputSuccess("".concat(data.showUUID ? "Now showing UUIDs." : "No longer showing UUIDs.", " Click tiles to check their recent history. Run /tilelog to disable.")); - handleTaps("on"); - } - else if (currentTapMode == "off" || changed) { - handleTaps("once"); - output("Click on a tile to check its recent history..."); - } - else { - handleTaps("off"); - outputSuccess("Tilelog disabled."); - } - }, - tapped: function (_a) { - var _b; - var tile = _a.tile, x = _a.x, y = _a.y, output = _a.output, sender = _a.sender, admins = _a.admins, data = _a.data; - var historyData = (_b = globals_1.tileHistory["".concat(x, ",").concat(y)]) !== null && _b !== void 0 ? _b : (0, commands_1.fail)("There is no recorded history for the selected tile (".concat(tile.x, ", ").concat(tile.y, ").")); - var history = funcs_1.StringIO.read(historyData, function (str) { return str.readArray(function (d) { return ({ - action: d.readString(2), - uuid: d.readString(3), - time: d.readNumber(16), - type: d.readString(2), - }); }, 1); }); - output("[yellow]Tile history for tile (".concat(tile.x, ", ").concat(tile.y, "):\n") + history.map(function (e) { - var _a, _b; - return globals_1.uuidPattern.test(e.uuid) - ? (sender.hasPerm("viewUUIDs") && data.showUUID - ? "[yellow]".concat((_a = admins.getInfoOptional(e.uuid)) === null || _a === void 0 ? void 0 : _a.plainLastName(), "[lightgray](").concat(e.uuid, ")[yellow] ").concat(e.action, " a [cyan]").concat(e.type, "[] ").concat((0, utils_1.formatTimeRelative)(e.time)) - : "[yellow]".concat((_b = admins.getInfoOptional(e.uuid)) === null || _b === void 0 ? void 0 : _b.plainLastName(), " ").concat(e.action, " a [cyan]").concat(e.type, "[] ").concat((0, utils_1.formatTimeRelative)(e.time))) - : "[yellow]".concat(e.uuid, "[yellow] ").concat(e.action, " a [cyan]").concat(e.type, "[] ").concat((0, utils_1.formatTimeRelative)(e.time)); - }).join('\n')); - } - }), aoelog: (0, commands_1.command)(function () { - var allowedActions = [ - "built", "broke", "rotated", "killed", "configured", "pay-dropped", "picked up", "controlled" - ]; - var cachedPointMap = Object.create(null); - return { - args: ['persist:boolean?', 'amount:number?', 'action:string?'], - description: 'Checks the history of all tiles in the selected region. Can be filtered by action.', - perm: commands_1.Perm.none, - handler: function (_a) { - var args = _a.args, sender = _a.sender, outputSuccess = _a.outputSuccess, currentTapMode = _a.currentTapMode, handleTaps = _a.handleTaps; - if (currentTapMode === "off" || args.action || args.amount) { - if (args.action && !allowedActions.includes(args.action)) - (0, commands_1.fail)("Invalid action. Allowed actions: ".concat(allowedActions.join(", "))); - if (args.amount && args.amount > 100) - (0, commands_1.fail)("Limit cannot be greater than 100."); - cachedPointMap[sender.uuid] = undefined; - handleTaps("on"); - outputSuccess("Aoelog mode enabled. To see the recent history of all tiles in a rectangular region, tap opposite corners of the rectangle. Run /aoelog with no arguments to disable."); - } - else { - handleTaps("off"); - outputSuccess("Aoelog disabled."); - } - }, - tapped: function (_a) { - var x = _a.x, y = _a.y, output = _a.output, outputFail = _a.outputFail, sender = _a.sender, admins = _a.admins, handleTaps = _a.handleTaps, args = _a.args; - function handleArea(p1, p2) { - var minX = Math.min(p1[0], p2[0]); - var maxX = Math.max(p1[0], p2[0]); - var minY = Math.min(p1[1], p2[1]); - var maxY = Math.max(p1[1], p2[1]); - var limitTiles = 0; - var amount = args.amount != null ? Math.floor(Math.abs(args.amount)) : 10; - outer: for (var i = minX; i <= maxX; i++) { - for (var j = minY; j <= maxY; j++) { - var tileData = globals_1.tileHistory["".concat(i, ",").concat(j)]; - if (!tileData) - continue; - var history = funcs_1.StringIO.read(globals_1.tileHistory["".concat(i, ",").concat(j)], function (str) { return str.readArray(function (d) { - var _a, _b, _c; - return ({ - action: (_a = d.readString(2)) !== null && _a !== void 0 ? _a : "??", - uuid: (_b = d.readString(3)) !== null && _b !== void 0 ? _b : "??", - time: d.readNumber(16), - type: (_c = d.readString(2)) !== null && _c !== void 0 ? _c : "??", - }); - }, 1); }); - if (args.action) - history = history.filter(function (e) { return e.action === args.action; }); - if (history.length == 0) - continue; - output("[yellow]Tile history for tile (".concat(i, ", ").concat(j, "):\n") + history.map(function (e) { - var _a, _b; - if (globals_1.uuidPattern.test(e.uuid)) { - if (sender.hasPerm("viewUUIDs")) - return "[yellow]".concat((_a = admins.getInfoOptional(e.uuid)) === null || _a === void 0 ? void 0 : _a.plainLastName(), "[lightgray](").concat(e.uuid, ")[yellow] ").concat(e.action, " a [cyan]").concat(e.type, "[] ").concat((0, utils_1.formatTimeRelative)(e.time)); - else - return "[yellow]".concat((_b = admins.getInfoOptional(e.uuid)) === null || _b === void 0 ? void 0 : _b.plainLastName(), " ").concat(e.action, " a [cyan]").concat(e.type, "[] ").concat((0, utils_1.formatTimeRelative)(e.time)); - } - else - return "[yellow]".concat(e.uuid, "[yellow] ").concat(e.action, " a [cyan]").concat(e.type, "[] ").concat((0, utils_1.formatTimeRelative)(e.time)); - }).join('\n')); - limitTiles++; - if (limitTiles === amount) - break outer; - } - } - if (limitTiles == 0) { - if (args.action) - outputFail("There is no recorded history for the selected region matching the provided filters."); - else - outputFail("There is no recorded history for the selected region."); - } - if (limitTiles == amount) - output("Displaying first ".concat(limitTiles, " entries. To show other entries, increase the limit or select a smaller area.")); - } - var p1 = cachedPointMap[sender.uuid]; - if (!p1) { - cachedPointMap[sender.uuid] = [x, y]; - output("1st point set at (".concat(x, ",").concat(y, ")")); - } - else { - var p2 = [x, y]; - output("2nd point set at (".concat(x, ", ").concat(y, ")")); - var width = Math.abs(p1[0] - p2[0]); - var height = Math.abs(p1[1] - p2[1]); - if (width > 50 || height > 50) - (0, commands_1.fail)("Selection too large: width/height cannot be more than 50."); - handleArea(p1, p2); - cachedPointMap[sender.uuid] = undefined; - if (!args.persist) - handleTaps("off"); - } - }, - }; - }), afk: { - args: [], - description: 'Toggles your afk status.', - perm: commands_1.Perm.none, - handler: function (_a) { - var sender = _a.sender, outputSuccess = _a.outputSuccess; - sender.manualAfk = !sender.manualAfk; - sender.updateName(); - if (sender.manualAfk) - outputSuccess("You are now marked as AFK."); - else - outputSuccess("You are no longer marked as AFK."); - }, - }, vanish: { - args: ['target:player?'], - description: "Toggles visibility of your rank and flags.", - perm: commands_1.Perm.vanish, - handler: function (_a) { - var sender = _a.sender, _b = _a.args.target, target = _b === void 0 ? sender : _b, outputSuccess = _a.outputSuccess; - if (sender.stelled()) - (0, commands_1.fail)("Marked players may not hide flags."); - if (sender.muted) - (0, commands_1.fail)("Muted players may not hide flags."); - if (sender != target && target.hasPerm("blockTrolling")) - (0, commands_1.fail)("Target is insufficentlly trollable."); - if (sender != target && !sender.ranksAtLeast("mod")) - (0, commands_1.fail)("You do not have permission to vanish other players."); - target.showRankPrefix = !target.showRankPrefix; - outputSuccess("".concat(target == sender ? "Your" : "".concat(target.name, "'s"), " rank prefix is now ").concat(target.showRankPrefix ? "visible" : "hidden", ".")); - }, - }, tileid: { - args: [], - description: 'Checks id of a tile.', - perm: commands_1.Perm.none, - handler: function (_a) { - var output = _a.output, handleTaps = _a.handleTaps; - handleTaps("once"); - output("Click a tile to see its id..."); - }, - tapped: function (_a) { - var output = _a.output, f = _a.f, tile = _a.tile; - output(f(templateObject_1 || (templateObject_1 = __makeTemplateObject(["ID is ", ""], ["ID is ", ""])), tile.block().id)); - } - } }, Object.fromEntries(config_1.FishServer.all.map(function (server) { return [ - server.name, - { - args: [], - description: "Switches to the ".concat(server.name, " server."), - perm: server.requiredPerm ? commands_1.Perm.getByName(server.requiredPerm) : commands_1.Perm.none, - isHidden: true, - handler: function (_a) { - var sender = _a.sender, lastUsedSuccessfullySender = _a.lastUsedSuccessfullySender; - if (Date.now() - lastUsedSuccessfullySender > funcs_1.Duration.minutes(1)) - players_1.FishPlayer.messageAllWithPerm(server.requiredPerm, "".concat(sender.name, "[magenta] has gone to the ").concat(server.name, " server. Use [cyan]/").concat(server.name, " [magenta]to join them!")); - Call.connect(sender.con, server.ip, server.port); - }, - }, -]; }))), { switch: { - args: ["server:string", "target:player?"], - description: "Switches to another server.", - perm: commands_1.Perm.play, - handler: function (_a) { - var _b, _c; - var args = _a.args, sender = _a.sender, f = _a.f, lastUsedSuccessfullySender = _a.lastUsedSuccessfullySender; - if (args.target != null && args.target != sender && !sender.canModerate(args.target, true, "admin", true)) - (0, commands_1.fail)(f(templateObject_2 || (templateObject_2 = __makeTemplateObject(["You do not have permission to switch player ", "."], ["You do not have permission to switch player ", "."])), args.target)); - var target = (_b = args.target) !== null && _b !== void 0 ? _b : sender; - if (globals_1.ipPortPattern.test(args.server) && sender.hasPerm("admin")) { - //direct connect - Call.connect.apply(Call, __spreadArray([target.con], __read(args.server.split(":")), false)); - } - else { - var unknownServerMessage = "Unknown server ".concat(args.server, ". Valid options: ").concat(config_1.FishServer.all.filter(function (s) { return !s.requiredPerm || sender.hasPerm(s.requiredPerm); }).map(function (s) { return s.name; }).join(", ")); - var server = (_c = config_1.FishServer.byName(args.server)) !== null && _c !== void 0 ? _c : (0, commands_1.fail)(unknownServerMessage); - //Pretend the server doesn't exist - if (server.requiredPerm && !sender.hasPerm(server.requiredPerm)) - (0, commands_1.fail)(unknownServerMessage); - if (target == sender && Date.now() - lastUsedSuccessfullySender > funcs_1.Duration.minutes(1)) - players_1.FishPlayer.messageAllWithPerm(server.requiredPerm, "".concat(sender.name, "[magenta] has gone to the ").concat(server.name, " server. Use [cyan]/").concat(server.name, " [magenta]to join them!")); - Call.connect(target.con, server.ip, server.port); - } - } - }, s: { - args: ['message:string'], - description: "Sends a message to staff only.", - perm: commands_1.Perm.chat, - handler: function (_a) { - var sender = _a.sender, args = _a.args, outputSuccess = _a.outputSuccess, outputFail = _a.outputFail, lastUsedSender = _a.lastUsedSender; - if (!sender.hasPerm("mod")) { - if (Date.now() - lastUsedSender < 4000) - (0, commands_1.fail)("This command was used recently and is on cooldown. [orange]Misuse of this command may result in a mute."); - } - api.sendStaffMessage(args.message, sender.name, sender.hasPerm("mod"), function (sent) { - if (!sender.hasPerm("mod")) { - if (sent) { - outputSuccess("Message sent to [orange]all online staff."); - } - else { - var wasReceived = players_1.FishPlayer.messageStaff(sender.prefixedName, args.message); - if (wasReceived) - outputSuccess("Message sent to staff."); - else - outputFail("No staff were online to receive your message."); - } - } - }); - }, - }, - /** - * This command is mostly for mobile (or players without foos). - * - * Since the player's unit follows the camera and we are moving the - * camera, we need to keep setting the players real position to the - * spot the command was made. This is pretty buggy but otherwise the - * player will be up the target player's butt - */ - watch: (0, commands_1.command)({ - args: ['player:player?'], - description: "Watch/unwatch a player.", - perm: commands_1.Perm.none, - data: new Set, - handler: function (_a) { - var _b; - var args = _a.args, data = _a.data, sender = _a.sender, outputSuccess = _a.outputSuccess, outputFail = _a.outputFail; - if (data.has(sender.uuid)) { - outputSuccess("No longer watching a player."); - data.delete(sender.uuid); - } - else if (args.player) { - data.add(sender.uuid); - var senderUnit_1 = (_b = sender.unit()) !== null && _b !== void 0 ? _b : (0, commands_1.fail)("You do not have a unit."); - var stayX_1 = senderUnit_1.x; - var stayY_1 = senderUnit_1.y; - var target_1 = args.player.player; - (function watch() { - var _a, _b; - var unit = target_1.unit(); - if (data.has(sender.uuid) && unit) { - // Self.X+(172.5-Self.X)/10 - Call.setCameraPosition(sender.con, unit.x, unit.y); - if (senderUnit_1) - (_b = (_a = sender.unit()) === null || _a === void 0 ? void 0 : _a.set) === null || _b === void 0 ? void 0 : _b.call(_a, stayX_1, stayY_1); - Timer.schedule(function () { return watch(); }, 0.1, 0.1, 0); - } - else { - Call.setCameraPosition(sender.con, stayX_1, stayY_1); - } - })(); - } - else { - outputFail("No player to unwatch."); - } - }, - }), spectate: (0, commands_1.command)(function () { - //TODO revise code - /** Mapping between player and original team */ - var spectators = new Map(); - function spectate(target) { - spectators.set(target, target.team()); - target.forceRespawn(); - target.setTeam(Team.derelict); - target.forceRespawn(); - } - function resume(target) { - if (spectators.get(target) == null) - return; // this state is possible for a person who left not in spectate - target.setTeam(spectators.get(target)); - spectators.delete(target); - target.forceRespawn(); - } - Events.on(EventType.GameOverEvent, function () { return spectators.clear(); }); - Events.on(EventType.PlayerLeave, function (_a) { - var player = _a.player; - return resume(players_1.FishPlayer.get(player)); - }); - return { - args: ["target:player?"], - description: "Toggles spectator mode in PVP games.", - perm: commands_1.Perm.play, - requirements: [commands_1.Req.gameRunning], - handler: function (_a) { - var sender = _a.sender, _b = _a.args.target, target = _b === void 0 ? sender : _b, outputSuccess = _a.outputSuccess, f = _a.f; - if (!config_1.Gamemode.pvp() && !sender.hasPerm("mod")) - (0, commands_1.fail)("You do not have permission to spectate on a non-pvp server."); - if (target !== sender && target.hasPerm("blockTrolling")) - (0, commands_1.fail)("Target player is insufficiently trollable."); - if (target !== sender && !sender.ranksAtLeast("admin")) - (0, commands_1.fail)("You do not have permission to force other players to spectate."); - if (spectators.has(target)) { - resume(target); - outputSuccess(target == sender - ? f(templateObject_3 || (templateObject_3 = __makeTemplateObject(["Rejoining game as team ", "."], ["Rejoining game as team ", "."])), target.team()) : f(templateObject_4 || (templateObject_4 = __makeTemplateObject(["Forced ", " out of spectator mode."], ["Forced ", " out of spectator mode."])), target)); - } - else { - spectate(target); - outputSuccess(target == sender - ? f(templateObject_5 || (templateObject_5 = __makeTemplateObject(["Now spectating. Run /spectate again to resume gameplay."], ["Now spectating. Run /spectate again to resume gameplay."]))) : f(templateObject_6 || (templateObject_6 = __makeTemplateObject(["Forced ", " into spectator mode."], ["Forced ", " into spectator mode."])), target)); - } - } - }; - }), help: { - args: ['name:string?'], - description: 'Displays a list of all commands.', - perm: commands_1.Perm.none, - handler: function (_a) { - var _b; - var args = _a.args, output = _a.output, sender = _a.sender, allCommands = _a.allCommands; - var formatCommand = function (name, color) { - return new funcs_1.StringBuilder() - .add("".concat(color, "/").concat(name)) - .chunk("[white]".concat(allCommands[name].args.map(commands_1.formatArg).join(' '))) - .chunk("[lightgray]- ".concat(allCommands[name].description)).str; - }; - var formatList = function (commandList, color) { return commandList.map(function (c) { return formatCommand(c, color); }).join('\n'); }; - if (args.name && isNaN(parseInt(args.name)) && !['mod', 'admin', 'member'].includes(args.name)) { - //name is not a number or a category, therefore it is probably a command name - if (args.name in allCommands && (!allCommands[args.name].isHidden || allCommands[args.name].perm.check(sender))) { - if (args.name == "help") - achievements_1.Achievements.help_help.grantTo(sender, false); - output("Help for command ".concat(args.name, ":\n\t").concat(allCommands[args.name].description, "\n\tUsage: [sky]/").concat(args.name, " [white]").concat(allCommands[args.name].args.map(commands_1.formatArg).join(' '), "\n\tPermission required: ").concat(allCommands[args.name].perm.name)); - } - else - (0, commands_1.fail)("Command \"".concat(args.name, "\" does not exist.")); - } - else { - var commands_2 = { - player: [], - mod: [], - admin: [], - member: [], - }; - //TODO change this to category, not perm - Object.entries(allCommands).forEach(function (_a) { - var _b = __read(_a, 2), name = _b[0], data = _b[1]; - return (data.perm === commands_1.Perm.admin ? commands_2.admin : data.perm === commands_1.Perm.mod ? commands_2.mod : data.perm === commands_1.Perm.member ? commands_2.member : commands_2.player).push(name); - }); - var chunkedPlayerCommands = (0, funcs_1.to2DArray)(commands_2.player, 15); - switch (args.name) { - case 'admin': - output("".concat(commands_1.Perm.admin.color, "-- Admin commands --\n") + formatList(commands_2.admin, commands_1.Perm.admin.color)); - break; - case 'mod': - output("".concat(commands_1.Perm.mod.color, "-- Mod commands --\n") + formatList(commands_2.mod, commands_1.Perm.mod.color)); - break; - case 'member': - output("".concat(commands_1.Perm.member.color, "-- Member commands --\n") + formatList(commands_2.member, commands_1.Perm.member.color)); - break; - default: { - var pageNumber = args.name != undefined ? parseInt(args.name) : 1; - var page = (_b = chunkedPlayerCommands[pageNumber - 1]) !== null && _b !== void 0 ? _b : (0, commands_1.fail)("\"".concat(args.name, "\" is an invalid page number.")); - output("[sky]-- Commands page [lightgrey]".concat(pageNumber, "/").concat(chunkedPlayerCommands.length, "[sky] --\n") + formatList(page, '[sky]')); - } - } - } - }, - }, msg: { - args: ['player:player', 'message:string'], - description: 'Send a message to only one player.', - perm: commands_1.Perm.chat, - handler: function (_a) { - var args = _a.args, sender = _a.sender, output = _a.output, f = _a.f; - globals_1.recentWhispers[args.player.uuid] = sender.uuid; - args.player.sendMessage("".concat(sender.prefixedName, "[lightgray] whispered:[#BBBBBB] ").concat(args.message)); - output(f(templateObject_7 || (templateObject_7 = __makeTemplateObject(["[lightgray]Whispered to ", "[lightgray]:[#BBBBBB] ", ""], ["[lightgray]Whispered to ", "[lightgray]:[#BBBBBB] ", ""])), args.player, args.message)); - }, - }, r: { - args: ['message:string'], - description: 'Reply to the most recent message.', - perm: commands_1.Perm.chat, - handler: function (_a) { - var _b; - var args = _a.args, sender = _a.sender, output = _a.output, f = _a.f; - var recipient = players_1.FishPlayer.getById((_b = globals_1.recentWhispers[sender.uuid]) !== null && _b !== void 0 ? _b : (0, commands_1.fail)("It doesn't look like someone has messaged you recently. Try whispering to them with [white]\"/msg \"")); - if (!(recipient === null || recipient === void 0 ? void 0 : recipient.connected())) - (0, commands_1.fail)("The person who last messaged you doesn't seem to exist anymore. Try whispering to someone with [white]\"/msg \""); - globals_1.recentWhispers[globals_1.recentWhispers[sender.uuid]] = sender.uuid; - recipient.sendMessage("".concat(sender.name, "[lightgray] whispered:[#BBBBBB] ").concat(args.message)); - output(f(templateObject_8 || (templateObject_8 = __makeTemplateObject(["[lightgray]Whispered to ", "[lightgray]:[#BBBBBB] ", ""], ["[lightgray]Whispered to ", "[lightgray]:[#BBBBBB] ", ""])), recipient, args.message)); - }, - }, trail: { - args: ['type:string?', 'color:string?'], - description: 'Use command to see options and toggle trail on/off.', - perm: commands_1.Perm.none, - handler: function (_a) { - var args = _a.args, sender = _a.sender, output = _a.output, outputFail = _a.outputFail, outputSuccess = _a.outputSuccess; - //overload 1: type not specified - if (!args.type) { - if (sender.trail != null) { - sender.trail = null; - outputSuccess("Trail turned off."); - } - else { - output("Available types:[yellow]\n1 - fluxVapor (flowing smoke, long lasting)\n2 - overclocked (diamonds)\n3 - overdriven (squares)\n4 - shieldBreak (smol)\n5 - upgradeCoreBloom (square, long lasting, only orange)\n6 - electrified (tiny spiratic diamonds, but only green)\n7 - unitDust (same as above but round, and can change colors)\n[white]Usage: [orange]/trail [lightgrey] [color/#hex/r,g,b]"); - } - return; - } - //overload 2: type specified - var trailTypes = { - "1": 'fluxVapor', - "2": 'overclocked', - "3": 'overdriven', - "4": 'shieldBreak', - "5": 'upgradeCoreBloom', - "6": 'electrified', - "7": 'unitDust', - }; - var selectedType = trailTypes[args.type]; - if (!selectedType) { - if (Object.values(trailTypes).includes(args.type)) - (0, commands_1.fail)("Please use the numeric id to refer to a trail type."); - else - (0, commands_1.fail)("\"".concat(args.type, "\" is not an available type.")); - } - var color = args.color ? (0, utils_1.getColor)(args.color) : Color.white; - if (color instanceof Color) { - sender.trail = { - type: selectedType, - color: color, - }; - } - else { - outputFail("[scarlet]Sorry, \"".concat(args.color, "\" is not a valid color.\n[yellow]Color can be in the following formats:\n[pink]pink [white]| [gray]#696969 [white]| 255,0,0.")); - } - }, - }, ohno: (0, commands_1.command)({ - args: [], - description: 'Spawns an ohno.', - perm: commands_1.Perm.play, - init: function () { - var Ohnos = { - enabled: true, - ohnos: new Array(), - makeOhno: function (team, x, y) { - var ohno = UnitTypes.atrax.create(team); - ohno.set(x, y); - ohno.type = UnitTypes.alpha; - ohno.apply(StatusEffects.disarmed, Number.MAX_SAFE_INTEGER); - ohno.resetController(); //does this work? - ohno.add(); - this.ohnos.push(ohno); - return ohno; - }, - updateLength: function () { - this.ohnos = this.ohnos.filter(function (o) { return o && o.isAdded() && !o.dead; }); - }, - checkAchievement: function () { - var e_1, _a; - try { - for (var _b = __values(this.ohnos), _c = _b.next(); !_c.done; _c = _b.next()) { - var ohno = _c.value; - var player = ohno.getPlayer(); - if (player) - achievements_1.Achievements.ohno.grantTo(players_1.FishPlayer.get(player), false); - } - } - catch (e_1_1) { e_1 = { error: e_1_1 }; } - finally { - try { - if (_c && !_c.done && (_a = _b.return)) _a.call(_b); - } - finally { if (e_1) throw e_1.error; } - } - }, - killAll: function () { - this.ohnos.forEach(function (ohno) { var _a; return (_a = ohno === null || ohno === void 0 ? void 0 : ohno.kill) === null || _a === void 0 ? void 0 : _a.call(ohno); }); - this.ohnos = []; - }, - amount: function () { - return this.ohnos.length; - }, - }; - Events.on(EventType.GameOverEvent, function (e) { - Ohnos.killAll(); - }); - Timer.schedule(function () { return Ohnos.checkAchievement(); }, 1, 2); - return Ohnos; - }, - requirements: [ - commands_1.Req.gameRunning, commands_1.Req.modeNot("pvp"), - commands_1.Req.unitExists("You cannot spawn ohnos while dead.") - ], - handler: function (_a) { - var sender = _a.sender, Ohnos = _a.data; - if (!Ohnos.enabled) - (0, commands_1.fail)("Ohnos have been temporarily disabled."); - Ohnos.updateLength(); - if (Ohnos.ohnos.length >= (Groups.player.size() + 1) || - sender.team().data().countType(UnitTypes.alpha) >= Units.getCap(sender.team())) - (0, commands_1.fail)("Sorry, the max number of ohno units has been reached."); - if ((0, utils_1.nearbyEnemyTile)((sender.unit()), 6) != null) - (0, commands_1.fail)("Too close to an enemy building!"); - if (!UnitTypes.alpha.supportsEnv(Vars.state.rules.env)) - (0, commands_1.fail)("Ohnos cannot survive in this map."); - Ohnos.makeOhno(sender.team(), sender.player.x, sender.player.y); - }, - }), ranks: { - args: [], - description: 'Displays information about all ranks.', - perm: commands_1.Perm.none, - handler: function (_a) { - var output = _a.output; - output("List of ranks:\n" + - Object.values(ranks_1.Rank.ranks) - .map(function (rank) { return "".concat(rank.prefix, " ").concat(rank.color).concat((0, funcs_1.capitalizeText)(rank.name), "[]: ").concat(rank.color).concat(rank.description, "[]\n"); }) - .join("") + - "List of flags:\n" + - Object.values(ranks_1.RoleFlag.flags) - .map(function (flag) { return "".concat(flag.prefix, " ").concat(flag.color).concat((0, funcs_1.capitalizeText)(flag.name), "[]: ").concat(flag.color).concat(flag.description, "[]\n"); }) - .join("")); - }, - }, rules: { - args: ['player:player?'], - description: 'Displays the server rules.', - perm: commands_1.Perm.none, - handler: function (_a) { - var _b; - var args = _a.args, sender = _a.sender, output = _a.output, outputSuccess = _a.outputSuccess, f = _a.f; - var target = (_b = args.player) !== null && _b !== void 0 ? _b : sender; - if (target !== sender) { - if (!sender.hasPerm("warn")) - (0, commands_1.fail)("You do not have permission to show rules to other players."); - if (!sender.canModerate(target)) - commands_1.Req.cooldown(funcs_1.Duration.minutes(10)); - if (target.hasPerm("blockTrolling")) - (0, commands_1.fail)(f(templateObject_9 || (templateObject_9 = __makeTemplateObject(["Player ", " is insufficiently trollable."], ["Player ", " is insufficiently trollable."])), args.player)); - } - void menus_1.Menu.menu("Rules for [#0000ff]>|||> FISH [white]servers", config_1.rules.join("\n\n"), ["[green]I agree to abide by these rules[]", "No"], target, { onCancel: "null" }).then(function (option) { - if (option == "No") { - target.kick("You must agree to the rules to play on this server. Rejoin to agree to the rules.", 1); - outputSuccess('Player rejected the rules and was kicked.'); - } - else if (option == null) { - output('Player closed the menu.'); - } - else { - outputSuccess('Player acknowledged the rules.'); - } - }); - if (target !== sender) - outputSuccess(f(templateObject_10 || (templateObject_10 = __makeTemplateObject(["Reminded ", " of the rules."], ["Reminded ", " of the rules."])), target)); - }, - }, void: { - args: ["player:player?"], - description: 'Warns other players about power voids.', - perm: commands_1.Perm.play, - requirements: function (_a) { - var args = _a.args; - return [ - commands_1.Req.mode("attack"), - args.player ? commands_1.Req.cooldown(20000) : commands_1.Req.cooldownGlobal(10000) - ]; - }, - handler: function (_a) { - var args = _a.args, sender = _a.sender, outputSuccess = _a.outputSuccess, f = _a.f; - if (args.player) { - if (!sender.hasPerm("trusted")) - (0, commands_1.fail)("You do not have permission to show popups to other players, please run /void with no arguments to send a chat message to everyone."); - if (args.player !== sender && args.player.hasPerm("blockTrolling")) - (0, commands_1.fail)("Target player is insufficiently trollable."); - void menus_1.Menu.menu("\uf83f [scarlet]WARNING[] \uf83f", "[white]Don't break the Power Void (\uF83F), it's a trap!\nPower voids disable anything they are connected to.\nIf you break it, [scarlet]you will get attacked[] by enemy units.\nPlease stop attacking and [lime]build defenses[] first!", ["I understand"], args.player, { onCancel: 'null' }).then(function () { return outputSuccess(f(templateObject_11 || (templateObject_11 = __makeTemplateObject(["Player ", " acknowledged the warning."], ["Player ", " acknowledged the warning."])), args.player)); }); - (0, utils_1.logAction)("showed void warning", sender, args.player); - outputSuccess(f(templateObject_12 || (templateObject_12 = __makeTemplateObject(["Warned ", " about power voids with a popup message."], ["Warned ", " about power voids with a popup message."])), args.player)); - } - else { - Call.sendMessage("[white]Don't break the Power Void (\uF83F), it's a trap!\nPower voids disable anything they are connected to. If you break it, [scarlet]you will get attacked[] by enemy units.\nPlease stop attacking and [lime]build defenses[] first!"); - } - }, - }, team: { - args: ['team:team', 'reason:string?'], - description: 'Changes your team.', - perm: commands_1.Perm.changeTeam, - handler: function (_a) { - var _b; - var sender = _a.sender, _c = _a.args, team = _c.team, reason = _c.reason, outputSuccess = _a.outputSuccess, f = _a.f; - if (config_1.Gamemode.sandbox() && globals_1.fishState.peacefulMode && !sender.hasPerm("admin")) - (0, commands_1.fail)("You do not have permission to change teams because peaceful mode is on."); - if (config_1.Gamemode.sandbox() && team === Vars.state.rules.waveTeam && !sender.hasPerm("admin")) - (0, commands_1.fail)("You do not have permission to change to the wave team on sandbox."); - if (!(config_1.Gamemode.sandbox() || config_1.Gamemode.testsrv()) && !sender.hasPerm("mod") && !reason) - (0, commands_1.fail)("Please specify a reason for changing teams."); - if (!sender.hasPerm("changeTeamExternal")) { - if (team.data().cores.size <= 0) - (0, commands_1.fail)("You do not have permission to change to a team with no cores."); - if (!sender.player.dead() && !((_b = sender.unit()) === null || _b === void 0 ? void 0 : _b.spawnedByCore)) - sender.forceRespawn(); - } - if (!sender.hasPerm("mod")) - sender.changedTeam = true; - sender.setTeam(team); - outputSuccess(f(templateObject_13 || (templateObject_13 = __makeTemplateObject(["Changed your team to ", "."], ["Changed your team to ", "."])), team)); - if (reason && !config_1.Gamemode.sandbox()) - (0, utils_1.logAction)("changed team to ".concat(team.name, " on ").concat((0, funcs_1.escapeTextDiscord)(Vars.state.map.plainName()), " with reason ").concat((0, funcs_1.escapeTextDiscord)(reason)), sender); - }, - }, teamp: { - args: ['team:team', 'target:player'], - description: 'Changes the team of a player.', - perm: commands_1.Perm.changeTeam, - handler: function (_a) { - var _b; - var sender = _a.sender, _c = _a.args, team = _c.team, target = _c.target, outputSuccess = _a.outputSuccess, f = _a.f; - if (!sender.canModerate(target, true, "mod", true)) - (0, commands_1.fail)(f(templateObject_14 || (templateObject_14 = __makeTemplateObject(["You do not have permission to change the team of ", ""], ["You do not have permission to change the team of ", ""])), target)); - if (config_1.Gamemode.sandbox() && globals_1.fishState.peacefulMode && !sender.hasPerm("admin")) - (0, commands_1.fail)("You do not have permission to change teams because peaceful mode is on."); - if (!sender.hasPerm("changeTeamExternal")) { - if (team.data().cores.size <= 0) - (0, commands_1.fail)("You do not have permission to change to a team with no cores."); - if (!target.player.dead() && !((_b = target.unit()) === null || _b === void 0 ? void 0 : _b.spawnedByCore)) - target.forceRespawn(); - } - target.setTeam(team); - outputSuccess(f(templateObject_15 || (templateObject_15 = __makeTemplateObject(["Changed team of player ", " to ", "."], ["Changed team of player ", " to ", "."])), target, team)); - }, - }, rank: { - args: ['player:player'], - description: 'Displays the rank of a player.', - perm: commands_1.Perm.none, - handler: function (_a) { - var args = _a.args, output = _a.output, f = _a.f; - output(f(templateObject_16 || (templateObject_16 = __makeTemplateObject(["Player ", "'s rank is ", "."], ["Player ", "'s rank is ", "."])), args.player, args.player.rank)); - }, - }, forcevnw: { - args: ["force:boolean?"], - description: 'Force skip to the next wave.', - perm: commands_1.Perm.admin, - handler: function (_a) { - var allCommands = _a.allCommands, sender = _a.sender, _b = _a.args.force, force = _b === void 0 ? true : _b; - if (allCommands.vnw.data.manager.session == null) { - if (force == false) - (0, commands_1.fail)("Cannot clear votes for VNW because no vote is currently ongoing."); - (0, utils_1.skipWaves)(1, true); - } - else { - if (force) - Call.sendMessage("VNW: [green]Vote was forced by admin [yellow]".concat(sender.name, "[green], skipping wave.")); - else - Call.sendMessage("VNW: [red]Votes cleared by admin [yellow]".concat(sender.name, "[red].")); - allCommands.vnw.data.manager.forceVote(force); - } - }, - }, vnw: (0, commands_1.command)({ - args: ["waves:number?"], - description: "Vote to start the next wave.", - perm: commands_1.Perm.play, - init: function () { return ({ - manager: new votes_1.VoteManager(funcs_1.Duration.minutes(1.5)) - .on("success", function (t) { return (0, utils_1.skipWaves)(t.session.data, true); }) - .on("vote passed", function () { return Call.sendMessage('VNW: [green]Vote passed, skipping to next wave.'); }) - .on("vote failed", function () { return Call.sendMessage('VNW: [red]Vote failed.'); }) - .on("player vote change", function (t, player) { return Call.sendMessage("VNW: ".concat(player.name, " [white] has voted on skipping [accent]").concat(t.session.data, "[white] wave(s). [green]").concat(t.currentVotes(), "[white] votes, [green]").concat(t.requiredVotes(), "[white] required.")); }) - .on("player vote removed", function (t, player) { return Call.sendMessage("VNW: ".concat(player.name, " [white] has left. [green]").concat(t.currentVotes(), "[white] votes, [green]").concat(t.requiredVotes(), "[white] required.")); }) - }); }, - requirements: [commands_1.Req.cooldown(3000), commands_1.Req.integerRange("waves", 1, 15), commands_1.Req.mode("survival", "testsrv"), commands_1.Req.gameRunning], - handler: function (_a) { - return __awaiter(this, arguments, void 0, function (_b) { - var _c; - var sender = _b.sender, waves = _b.args.waves, manager = _b.data.manager; - return __generator(this, function (_d) { - switch (_d.label) { - case 0: - if (!!manager.session) return [3 /*break*/, 4]; - if (!(waves !== null && waves !== void 0)) return [3 /*break*/, 1]; - _c = waves; - return [3 /*break*/, 3]; - case 1: return [4 /*yield*/, menus_1.Menu.menu("Start a Next Wave Vote", "Select the amount of waves you would like to skip.", [1, 5, 10], sender, { - includeCancel: true, - optionStringifier: function (n) { return "".concat(n, " waves"); } - })]; - case 2: - _c = (waves = _d.sent()); - _d.label = 3; - case 3: - _c; - if (manager.session) { - //Someone else started a vote - if (manager.session.data != waves) - (0, commands_1.fail)("Someone else started a vote with a different number of waves to skip."); - else - manager.vote(sender, sender.voteWeight(), waves); - } - else { - manager.start(sender, sender.voteWeight(), waves); - } - return [3 /*break*/, 5]; - case 4: - manager.vote(sender, sender.voteWeight(), null); - _d.label = 5; - case 5: return [2 /*return*/]; - } - }); - }); - } - }), forcertv: { - args: ["force:boolean?"], - description: 'Force skip to the next map.', - perm: commands_1.Perm.admin, - handler: function (_a) { - var _b = _a.args.force, force = _b === void 0 ? true : _b, sender = _a.sender, allCommands = _a.allCommands; - if (allCommands.rtv.data.manager.session == null) { - if (force == false) - (0, commands_1.fail)("Cannot clear votes for RTV because no vote is currently ongoing."); - allCommands.rtv.data.manager.forceVote(true); - } - else { - if (force) - Call.sendMessage("RTV: [green]Vote was forced by admin [yellow]".concat(sender.name, "[green].")); - else - Call.sendMessage("RTV: [red]Votes cleared by admin [yellow]".concat(sender.name, "[red].")); - allCommands.rtv.data.manager.forceVote(force); - } - } - }, rtv: (0, commands_1.command)({ - args: [], - description: 'Rock the vote to change map.', - perm: commands_1.Perm.play, - init: function () { return ({ - manager: new votes_1.VoteManager(funcs_1.Duration.minutes(1.5), config_1.Gamemode.hexed() ? ["fractionOfVoters", 1] : undefined) //Require unanimity in Hexed, as it is often 1 v everyone - .on("success", function () { return (0, utils_1.neutralGameover)(); }) - .on("vote passed", function () { return Call.sendMessage("RTV: [green]Vote has passed, changing map."); }) - .on("vote failed", function () { return Call.sendMessage("RTV: [red]Vote failed."); }) - .on("player vote change", function (t, player, oldVote, newVote) { return Call.sendMessage("RTV: ".concat(player.name, "[white] ").concat(oldVote == newVote ? "still " : "", "wants to change the map. [green]").concat(t.currentVotes(), "[white] votes, [green]").concat(t.requiredVotes(), "[white] required.")); }) - .on("player vote removed", function (t, player) { return Call.sendMessage("RTV: ".concat(player.name, "[white] has left the game. [green]").concat(t.currentVotes(), "[white] votes, [green]").concat(t.requiredVotes(), "[white] required.")); }) - }); }, - requirements: [commands_1.Req.cooldown(3000), commands_1.Req.gameRunning], - handler: function (_a) { - var sender = _a.sender, manager = _a.data.manager; - manager.vote(sender, 1, 0); //No weighting for RTV except for removing AFK players - } - }), - // votekick: command({ - // args: ["target:player"], - // description: "Starts a vote to kick a player.", - // perm: Perm.play, - // data: new VoteManager( - // Duration.seconds(20), - // ["absolute", 3], - // (fishP, target) => fishP.team() == target.team() || fishP.hasPerm("voteOtherTeams") - // ), - // handler({args, sender, data: votekickmanager}){ - // if(votekickmanager.session) fail(`There is already a votekick in progress.`); - // votekickmanager.start(sender, 1, args.target); - // } - // }), - // vote: { - // args: ["vote:boolean"], - // description: "Use /votekick instead.", - // perm: Perm.play, - // handler({sender, args, allCommands}){ - // const votekickmanager = allCommands.votekick.data; - // votekickmanager.handleVote(sender, args ? 1 : -1); - // } - // }, - forcenextmap: { - args: ["map:mapOrRandom"], - description: 'Override the next map in queue.', - perm: commands_1.Perm.admin.exceptModes({ - testsrv: commands_1.Perm.play - }), - handler: function (_a) { - var allCommands = _a.allCommands, args = _a.args, sender = _a.sender, outputSuccess = _a.outputSuccess, f = _a.f; - Vars.maps.setNextMapOverride(args.map == "random" ? null : args.map); - if (allCommands.nextmap.data.voteEndTime() > -1) { - //Cancel /nextmap vote if it's ongoing - allCommands.nextmap.data.resetVotes(); - Call.sendMessage("[red]Admin ".concat(sender.name, "[red] has cancelled the vote. The next map will be ").concat(args.map == "random" ? "random" : "[yellow]".concat(args.map.name()), ".")); - } - else { - outputSuccess(f(templateObject_17 || (templateObject_17 = __makeTemplateObject(["Forced the next map to be ", "."], ["Forced the next map to be ", "."])), args.map == "random" ? "random" : "\"".concat(args.map.name(), "\" by ").concat(args.map.author()))); - } - }, - }, maps: { - args: [], - description: 'Lists the available maps.', - perm: commands_1.Perm.none, - handler: function (_a) { - var output = _a.output; - output("[yellow]Use [white]/nextmap [lightgray][[map name] [yellow]to vote on a map.\n\n[blue]Available maps:\n_________________________\n".concat(Vars.maps.customMaps().toArray().map(function (map) { - return "[yellow]".concat(map.name()); - }).join("\n"))); - } - }, nextmap: (0, commands_1.command)(function () { - var random = { - name: function () { return "[lightgray]Random"; }, - plainName: function () { return "random"; } - }; - var votes = new Map(); - var lastVoteCount = 0; - var lastVoteTime = 0; - var voteEndTime = -1; - var voteDuration = funcs_1.Duration.minutes(1.5); - var task = null; - var isThrowbackFile = function (file) { return config_1.throwbackMapFileNames.includes(file.name()); }; - var throwbackMaps = function () { return config_1.throwbackMapFileNames - .map(function (f) { return Vars.maps.customMaps().find(function (m) { return m.file.name() == f; }); }) - .filter(function (m) { return m != null; }); }; - var normalMaps = function () { return Vars.maps.customMaps().toArray().filter(function (m) { return !isThrowbackFile(m.file); }); }; - function showMapDetails(target, map) { - return __awaiter(this, void 0, void 0, function () { - var res; - return __generator(this, function (_a) { - switch (_a.label) { - case 0: return [4 /*yield*/, menus_1.Menu.buttons(target, map.name(), "[accent]Description: [white]".concat(map.description(), "\n[accent]Author: [white]").concat(map.author(), "\n[accent]Fastest Time: [white]").concat((0, utils_1.formatTime)(maps_1.FMap.getCreate(map).stats().shortestTime), "\n[accent]Runs: [white]").concat(maps_1.FMap.getCreate(map).stats().allRunCount, "\n[accent]Winrate: [white]").concat((maps_1.FMap.getCreate(map).stats().winRate * 100).toFixed(2), "%"), [[ - { data: "vote", text: "[green]Vote for this Map" } - ], [ - { data: "back", text: "[red]Back" } - ]], { onCancel: "null" })]; - case 1: - res = _a.sent(); - return [2 /*return*/, res !== null && res !== void 0 ? res : "back"]; - } - }); - }); - } - function pickMapFromList(target, title, maps) { - return __awaiter(this, void 0, void 0, function () { - var map, action; - return __generator(this, function (_a) { - switch (_a.label) { - case 0: return [4 /*yield*/, menus_1.Menu.pagedList(target, title, "Select a map to view more information.", maps, { optionStringifier: function (map) { return map.name(); }, rowsPerPage: 10, columns: 1 })]; - case 1: - map = _a.sent(); - return [4 /*yield*/, showMapDetails(target, map)]; - case 2: - action = _a.sent(); - if (action === "vote") - return [2 /*return*/, map]; - return [2 /*return*/, null]; - } - }); - }); - } - function currentMenu(target) { - return __awaiter(this, void 0, void 0, function () { - var result, _a, picked, throwback, picked, campaign, campaignMaps, picked; - var _b, _c, _d, _e; - return __generator(this, function (_f) { - switch (_f.label) { - case 0: return [4 /*yield*/, menus_1.Menu.menu("Select a map", "[accent]---Current Map---\nMap Name: [white]".concat(Vars.state.map.name(), "\n[accent]Map Author: [white]").concat(Vars.state.map.author(), "\nFastest Time: [white]").concat((0, utils_1.formatTime)(maps_1.FMap.getCreate(Vars.state.map).stats().shortestTime), "\nCurrent Time: [white]").concat((0, utils_1.formatTime)((_e = (_c = (_b = maps_1.PartialMapRun.current) === null || _b === void 0 ? void 0 : _b.duration()) !== null && _c !== void 0 ? _c : (_d = maps_1.FMap.getCreate(Vars.state.map).runs.at(-1)) === null || _d === void 0 ? void 0 : _d.duration()) !== null && _e !== void 0 ? _e : 0)), ["[green]Current Maps", "[yellow]Throwback Maps", "[orange]Campaigns"], target, { columns: 1, includeCancel: "Close" })]; - case 1: - result = _f.sent(); - _a = result; - switch (_a) { - case '[green]Current Maps': return [3 /*break*/, 2]; - case '[yellow]Throwback Maps': return [3 /*break*/, 4]; - case '[orange]Campaigns': return [3 /*break*/, 6]; - } - return [3 /*break*/, 9]; - case 2: return [4 /*yield*/, pickMapFromList(target, "Current Maps", normalMaps())]; - case 3: - picked = _f.sent(); - if (picked) - return [2 /*return*/, picked]; - return [2 /*return*/, currentMenu(target)]; - case 4: - throwback = throwbackMaps(); - if (throwback.length == 0) - (0, commands_1.fail)("No throwback maps are currently available."); - return [4 /*yield*/, pickMapFromList(target, "Throwback Maps", throwback)]; - case 5: - picked = _f.sent(); - if (picked) - return [2 /*return*/, picked]; - return [2 /*return*/, currentMenu(target)]; - case 6: - if (config_1.campaigns.length == 0) - (0, commands_1.fail)("No campaigns are currently available."); - return [4 /*yield*/, menus_1.Menu.pagedList(target, "Campaigns", "Select a campaign to view its maps.", config_1.campaigns, { optionStringifier: function (c) { return c.name; }, rowsPerPage: 10, columns: 1 })]; - case 7: - campaign = _f.sent(); - campaignMaps = campaign.mapFileNames - .map(function (f) { return Vars.maps.customMaps().find(function (m) { return m.file.name() == f; }); }) - .filter(function (m) { return m != null; }); - if (campaignMaps.length == 0) - (0, commands_1.fail)("Campaign \"".concat(campaign.name, "\" has no available maps.")); - return [4 /*yield*/, pickMapFromList(target, "Campaign: ".concat(campaign.name), campaignMaps)]; - case 8: - picked = _f.sent(); - if (picked) - return [2 /*return*/, picked]; - return [2 /*return*/, currentMenu(target)]; - case 9: - ; - return [2 /*return*/]; - } - }); - }); - } - function sendVote(sender, map) { - votes.set(sender, map); - if (voteEndTime == -1) { - if ((Date.now() - lastVoteTime) < funcs_1.Duration.minutes(1)) - (0, commands_1.fail)("Please wait 1 minute before starting a new map vote."); - startVote(); - Call.sendMessage("[cyan]Next Map Vote: ".concat(sender.name, "[cyan] started a map vote, and voted for [yellow]").concat(map.name(), "[cyan]. Use [white]/nextmap ").concat(map.plainName(), "[] to add your vote, or run [white]/maps[] to see other available maps.")); - } - else { - Call.sendMessage("[cyan]Next Map Vote: ".concat(sender.name, "[cyan] voted for [yellow]").concat(map.name(), "[cyan]. Time left: [scarlet]").concat((0, utils_1.formatTimeRelative)(voteEndTime, true))); - showVotes(); - } - } - function resetVotes() { - votes.clear(); - voteEndTime = -1; - task === null || task === void 0 ? void 0 : task.cancel(); - } - function getMapData() { - return __spreadArray([], __read(votes.values()), false).reduce(function (acc, map) { return (acc.increment(map), acc); }, new ObjectIntMap()).entries().toArray(); - } - function showVotes() { - Call.sendMessage("[green]Current votes:\n-----------------------------\n".concat(getMapData().map(function (_a) { - var map = _a.key, votes = _a.value; - return "[cyan]".concat(map.name(), "[yellow]: ").concat(votes); - }).toString("\n"))); - } - function startVote() { - voteEndTime = Date.now() + voteDuration; - task = Timer.schedule(endVote, voteDuration / 1000); - } - function endVote() { - if (voteEndTime == -1) - return; //aborted somehow - if (votes.size == 0) - return; //no votes? - if (votes.size + 2 <= lastVoteCount && (Date.now() - lastVoteTime) < funcs_1.Duration.minutes(10)) { - //If the number of votes is 2 less than the previous number of votes for a vote in the past 10 minutes, abor - Call.sendMessage("[cyan]Next Map Vote: [scarlet]Vote aborted because a previous vote had significantly higher turnout"); - resetVotes(); - return; - } - else { - lastVoteTime = Date.now(); - lastVoteCount = votes.size; - } - var mapData = getMapData(); - var highestVoteCount = mapData.max(floatf(function (e) { return e.value; })).value; - var highestVotedMaps = mapData.select(function (e) { return e.value == highestVoteCount; }); - var winner; - if (highestVotedMaps.size > 1) { - winner = highestVotedMaps.random().key; - Call.sendMessage("[green]There was a tie between the following maps:\n".concat(highestVotedMaps.map(function (_a) { - var map = _a.key, votes = _a.value; - return "[cyan]".concat(map.name(), "[yellow]: ").concat(votes); - }).toString("\n"), "\n[green]Picking random winner: [yellow]").concat(winner.name())); - } - else { - winner = highestVotedMaps.get(0).key; - Call.sendMessage("[green]Map voting complete! The next map will be [yellow]".concat(winner.name(), " [green]with [yellow]").concat(highestVoteCount, "[green] votes.")); - } - Vars.maps.setNextMapOverride(winner == random ? null : winner); - resetVotes(); - } - Events.on(EventType.GameOverEvent, resetVotes); - Events.on(EventType.ServerLoadEvent, resetVotes); - return { - args: ['map:mapOrRandom?'], - description: 'Allows you to vote for the next map.', - perm: commands_1.Perm.play, - data: { votes: votes, voteEndTime: function () { return voteEndTime; }, resetVotes: resetVotes, endVote: endVote }, - requirements: [commands_1.Req.cooldown(10000)], - handler: function (_a) { - return __awaiter(this, arguments, void 0, function (_b) { - var _c, _d, map; - var _e; - var args = _b.args, sender = _b.sender; - return __generator(this, function (_f) { - switch (_f.label) { - case 0: - if (config_1.Gamemode.testsrv()) - (0, commands_1.fail)("Please use /forcenextmap instead."); - if (votes.get(sender)) - (0, commands_1.fail)("You have already voted."); - if (!((_e = args.map) !== null && _e !== void 0)) return [3 /*break*/, 1]; - _c = _e; - return [3 /*break*/, 3]; - case 1: - _d = args; - return [4 /*yield*/, currentMenu(sender)]; - case 2: - _c = (_d.map = _f.sent()); - _f.label = 3; - case 3: - _c; - map = args.map === "random" ? random : args.map; - sendVote(sender, map); - return [2 /*return*/]; - } - }); - }); - } - }; - }), surrender: (0, commands_1.command)(function () { - var prefix = "[orange]Surrender[white]: "; - var managers = Team.all.map(function (team) { - return new votes_1.VoteManager(funcs_1.Duration.minutes(1.5), ["fractionOfVoters", config_1.Gamemode.hexed() ? 1 : 3 / 4], function (p) { return p.team() == team; }) - .on("success", function () { return team.cores().copy().each(function (c) { return c.kill(); }); }) - .on("vote passed", function () { return Call.sendMessage(prefix + "Team ".concat(team.coloredName(), " has voted to forfeit this match.")); }) - .on("vote failed", function (t) { return t.messageEligibleVoters(prefix + "Team ".concat(team.coloredName(), " has chosen not to forfeit this match.")); }) - .on("player vote change", function (t, player, oldVote, newVote) { return t.messageEligibleVoters(prefix + "".concat(player.name, "[white] ").concat(oldVote == newVote ? "still " : "", "wants to forfeit this match. [orange]").concat(t.currentVotes(), "[white] votes, [orange]").concat(t.requiredVotes(), "[white] required.")); }) - .on("player vote removed", function (t, player) { return t.messageEligibleVoters(prefix + "Player ".concat(player.name, "[white] has left the game. [orange]").concat(t.currentVotes(), "[white] votes, [orange]").concat(t.requiredVotes(), "[white] required.")); }); - }); - globals_1.FishEvents.on("playerTeamChange", function (_, fishP, previous) { - managers[previous.id].unvote(fishP); - }); - return { - args: ["force:boolean?", "team:team?"], - description: "Vote to surrender to the enemy team.", - perm: commands_1.Perm.play, - requirements: [commands_1.Req.mode("pvp"), commands_1.Req.teamAlive], - data: { managers: managers }, - handler: function (_a) { - return __awaiter(this, arguments, void 0, function (_b) { - var t, manager; - var sender = _b.sender, _c = _b.args, force = _c.force, team = _c.team; - return __generator(this, function (_d) { - switch (_d.label) { - case 0: - t = sender.hasPerm("admin") && team ? team : sender.team(); - manager = managers[t.id]; - if (!(sender.hasPerm("admin") && force != undefined)) return [3 /*break*/, 4]; - if (!force) return [3 /*break*/, 2]; - return [4 /*yield*/, menus_1.Menu.confirmDangerous(sender, "Are you sure you want to force team ".concat(t.coloredName(), "[] to lose?"))]; - case 1: - _d.sent(); - manager.messageEligibleVoters(prefix + "Vote forced by admin ".concat(sender.name, "[white].")); - Call.sendMessage(prefix + "Team ".concat(t.coloredName(), " has voted to forfeit this match.")); - return [3 /*break*/, 3]; - case 2: - manager.messageEligibleVoters(prefix + "Votes cleared by admin ".concat(sender.name, "[white].")); - _d.label = 3; - case 3: - manager.forceVote(force); - return [2 /*return*/]; - case 4: - if (sender.ranksAtLeast("mod")) - commands_1.Req.cooldown(5000); - else - commands_1.Req.cooldown(20000); - manager.vote(sender, 1, 0); - return [2 /*return*/]; - } - }); - }); - }, - }; - }), stats: { - args: ["target:player", "global:boolean?"], - perm: commands_1.Perm.none, - description: "Views a player's stats.", - handler: function (_a) { - var _b = _a.args, target = _b.target, _c = _b.global, global = _c === void 0 ? false : _c, output = _a.output, f = _a.f; - var stats = global ? target.globalStats : target.stats; - output(f(templateObject_18 || (templateObject_18 = __makeTemplateObject(["[accent]Statistics for player ", " ", ":\n(note: we started recording statistics on 22 Jan 2024)\n[white]--------------[]\nBlocks broken: ", "\nBlocks placed: ", "\nChat messages sent: ", "\nGames finished: ", "\nTime in-game: ", "\nWin rate: ", ""], ["[accent]\\\nStatistics for player ", " ", ":\n(note: we started recording statistics on 22 Jan 2024)\n[white]--------------[]\nBlocks broken: ", "\nBlocks placed: ", "\nChat messages sent: ", "\nGames finished: ", "\nTime in-game: ", "\nWin rate: ", ""])), target, global ? "across all servers" : "on this server", stats.blocksBroken, stats.blocksPlaced, stats.chatMessagesSent, stats.gamesFinished, (0, utils_1.formatTime)(stats.timeInGame), stats.gamesWon / stats.gamesFinished)); - } - }, showworld: { - args: ["x:number?", "y:number?", "size:number?"], - perm: commands_1.Perm.none, - description: "Views the world as a 2D scrollable menu.", - requirements: [commands_1.Req.cooldown(4000), commands_1.Req.integerRange("size", 1, 20)], - handler: function (_a) { - var sender = _a.sender, _b = _a.args, _c = _b.size, size = _c === void 0 ? 7 : _c, x = _b.x, y = _b.y; - if (Vars.state.rules.fog) - (0, commands_1.fail)("This command is disabled when fog is enabled."); - var options = (0, funcs_1.to2DArray)(Reflect.get(Vars.world.tiles, "array").map(function (tile) { return ({ - text: tile.block().emoji(), - data: null, - }); }), Vars.world.width()).reverse(); - var height = Vars.world.height(); - void menus_1.Menu.scroll2D(sender, "The World", "Use the arrow keys to navigate around the world. Click a blank square to exit.", options, { - columns: size, - rows: size, - x: x ? x - Math.trunc(size / 2) : 0, - y: height - (y ? y + 1 + Math.trunc(size / 2) : size), - getCenterText: function (x, y) { return "".concat(x, ",").concat(height - y - size); } - }); - } - }, mapinfo: { - args: ["map:map?"], - perm: commands_1.Perm.none, - description: "Displays information about a map.", - handler: function (_a) { - var _b; - var output = _a.output, map = _a.args.map, f = _a.f, sender = _a.sender; - if (map) { - var fmap = (_b = maps_1.FMap.getCreate(map)) !== null && _b !== void 0 ? _b : (0, commands_1.fail)("Map data is still being loaded, try again later."); - output(fmap.displayStats(f)); - } - else { - void menus_1.Menu.textPages(sender, Vars.maps.customMaps().map(function (m) { - return ["Map information", function () { var _a, _b; return (_b = (_a = maps_1.FMap.getCreate(m)) === null || _a === void 0 ? void 0 : _a.displayStats(f)) !== null && _b !== void 0 ? _b : (0, commands_1.fail)("Map data is still being loaded, try again later."); }]; - }).toArray(), [], { - startPage: Vars.maps.customMaps().toArray().indexOf(Vars.state.map), - }); - } - } - }, gamemode: { - args: ["mode:string"], - perm: new commands_1.Perm("changeGamemode", "manager").exceptModes({ - testsrv: commands_1.Perm.play, - }), - description: "Sets the gamemode.", - requirements: [commands_1.Req.cooldownGlobal(10000)], - handler: function (_a) { - var args = _a.args, sender = _a.sender, outputSuccess = _a.outputSuccess, lastUsedSuccessfully = _a.lastUsedSuccessfully; - if (!sender.hasPerm('trusted')) - commands_1.Req.cooldownGlobal(30000)({ lastUsedSuccessfully: lastUsedSuccessfully }); - //Unpause - Vars.state.set(GameState.State.playing); - switch (args.mode) { - case "attack": - Vars.state.rules.attackMode = true; - Vars.state.rules.pvp = false; - Vars.state.rules.infiniteResources = false; - break; - case "survival": - Vars.state.rules.attackMode = false; - Vars.state.rules.waves = true; - Vars.state.rules.pvp = false; - Vars.state.rules.infiniteResources = false; - break; - case "pvp": - Vars.state.rules.attackMode = true; - Vars.state.rules.pvp = true; - Vars.state.rules.waves = false; - Vars.state.rules.infiniteResources = false; - break; - case "sandbox": - Vars.state.rules.attackMode = true; - Vars.state.rules.pvp = false; - Vars.state.rules.waves = false; - Vars.state.rules.infiniteResources = true; - break; - default: (0, commands_1.fail)("Invalid mode, valid modes are: attack, survival, pvp"); - } - var reloader = new WorldReloader(); - Reflect.set(reloader, "wasServer", true); - Reflect.set(reloader, "players", Groups.player.copy()); - Call.worldDataBegin(); - reloader.end(); - Call.sendMessage("[orange]Player ".concat(sender.cleanedName, " changed the gamemode to ").concat(args.mode)); - outputSuccess("Changed mode to ".concat(args.mode)); - } - }, mixunit: { - args: ["type:unittype", "base:unittype"], - description: "Spawns a unit that is made of two unit types mixed together.", - perm: commands_1.Perm.admin.exceptModes({ - sandbox: commands_1.Perm.play - }), - requirements: function (_a) { - var sender = _a.sender; - return [!sender.hasPerm("admin") && commands_1.Req.cooldown(1500), commands_1.Req.unitExists()].filter(Boolean); - }, - handler: function (_a) { - var args = _a.args, sender = _a.sender, f = _a.f, outputSuccess = _a.outputSuccess; - var _b = sender.unit(), team = _b.team, x = _b.x, y = _b.y; - var unit = args.base.create(team); - unit.type = args.type; - unit.maxHealth = args.type.health; //because half-dead units aren't fun - unit.set(x, y); - unit.add(); - outputSuccess(f(templateObject_19 || (templateObject_19 = __makeTemplateObject(["Spawned a ", " that is partly a ", "."], ["Spawned a ", " that is partly a ", "."])), args.type, args.base)); - } - }, achievement: { - args: ["name:string?", "verbose:boolean?"], - description: "Displays information on a specific achievement.", - perm: commands_1.Perm.none, - handler: function (_a) { - return __awaiter(this, arguments, void 0, function (_b) { - var matching, achievement, _c; - var _d = _b.args, _e = _d.name, name = _e === void 0 ? "" : _e, _f = _d.verbose, verbose = _f === void 0 ? false : _f, sender = _b.sender, f = _b.f, output = _b.output; - return __generator(this, function (_g) { - switch (_g.label) { - case 0: - name = Strings.stripColors(name.toLowerCase()); - matching = achievements_1.Achievement.all.filter(function (a) { return Strings.stripColors(a.name).toLowerCase().includes(name); }); - if (matching.length == 0) - (0, commands_1.fail)(f(templateObject_20 || (templateObject_20 = __makeTemplateObject(["No achievements found with name ", ". To view all achievements, run [accent]/achievements[]."], ["No achievements found with name ", ". To view all achievements, run [accent]/achievements[]."])), name)); - if (!(matching.length > 2)) return [3 /*break*/, 2]; - return [4 /*yield*/, menus_1.Menu.pagedList(sender, "Achievement", "Select an achievement to view", matching, { - onCancel: "reject", - columns: 2, - optionStringifier: function (a) { return "".concat(a.icon, "[] ").concat(a.name); } - })]; - case 1: - _c = _g.sent(); - return [3 /*break*/, 3]; - case 2: - _c = matching[0]; - _g.label = 3; - case 3: - achievement = _c; - output(config_1.FColor.achievement(templateObject_21 || (templateObject_21 = __makeTemplateObject(["Achievement ", " ", "\n[white]--------------[]\n", "\nAllowed modes: ", "\nUnlocked: ", "\n", "", "", ""], ["\\\nAchievement ", " ", "\n[white]--------------[]\n", "\nAllowed modes: ", "\nUnlocked: ", "\n", "\\\n", "\\\n", "\\\n"])), achievement.icon, achievement.name, achievement.description + (achievement.extendedDescription ? ("\n" + "[gray]".concat(achievement.extendedDescription)) : ""), achievement.modesText, f.boolGood(achievement.has(sender)), verbose ? "[gray]ID: (".concat(achievement.nid, ")").concat(achievement.sid, "\n") : "", verbose ? "[gray]Notifies: ".concat(achievement.notify, "\n") : "", achievement.hidden ? "This achievement is secret." : "")); - return [2 /*return*/]; - } - }); - }); - } - }, achievementlist: { - args: ["target:player?"], - description: "Shows all achievements in a paged menu.", - perm: commands_1.Perm.none, - handler: function (_a) { - return __awaiter(this, arguments, void 0, function (_b) { - var sender = _b.sender, _c = _b.args.target, target = _c === void 0 ? sender : _c, f = _b.f; - return __generator(this, function (_d) { - switch (_d.label) { - case 0: return [4 /*yield*/, menus_1.Menu.textPages(sender, achievements_1.Achievement.all.filter(function (a) { return !a.hidden || a.has(target); }) - .map(function (a) { return [ - "".concat(a.icon, "[] ").concat(a.name), - function () { return config_1.FColor.achievement(templateObject_22 || (templateObject_22 = __makeTemplateObject(["", "\nAllowed modes: ", "\nUnlocked: ", "\n", ""], ["\\\n", "\nAllowed modes: ", "\nUnlocked: ", "\n", "\\\n"])), a.description + (a.extendedDescription ? ("\n" + "[gray]".concat(a.extendedDescription)) : ""), a.modesText, f.boolGood(a.has(target)), a.hidden ? "This achievement is secret." : ""); } - ]; }))]; - case 1: - _d.sent(); - return [2 /*return*/]; - } - }); - }); - } - }, achievementgrid: { - args: ["target:player?"], - description: "Shows all achievements in a 2D scrolling menu.", - perm: commands_1.Perm.none, - handler: function (_a) { - return __awaiter(this, arguments, void 0, function (_b) { - var visibleAchievements, options, numberAchievements, totalAchievements, x, y, a; - var _c; - var sender = _b.sender, _d = _b.args.target, target = _d === void 0 ? sender : _d, f = _b.f; - return __generator(this, function (_e) { - switch (_e.label) { - case 0: - visibleAchievements = achievements_1.Achievement.all.filter(function (a) { return !a.hidden || a.has(target); }); - options = (0, funcs_1.to2DArray)(visibleAchievements, 7).map(function (row) { return row.map(function (a) { return ({ - data: a, - text: a.has(target) ? a.icon : "[gray]".concat(Strings.stripColors(a.icon)), - }); }); }); - numberAchievements = achievements_1.Achievement.all.filter(function (a) { return a.has(target); }).length; - totalAchievements = visibleAchievements.length; - x = 0, y = 0; - a = null; - _e.label = 1; - case 1: - if (!true) return [3 /*break*/, 3]; - return [4 /*yield*/, menus_1.Menu.scroll2D(sender, "Achievements", a ? config_1.FColor.achievement(templateObject_23 || (templateObject_23 = __makeTemplateObject(["", " ", "\n\n", "\n\nAllowed modes: ", "\nUnlocked: ", "\n", ""], ["\\\n", " ", "\n\n", "\n\nAllowed modes: ", "\nUnlocked: ", "\n", "\\\n"])), a.icon, a.name, a.description + (a.extendedDescription ? ("\n" + "[gray]".concat(a.extendedDescription)) : ""), a.modesText, f.boolGood(a.has(target)), a.hidden ? "This achievement is secret." : "") : - (target == sender ? "You have ".concat(numberAchievements, "/").concat(totalAchievements, " achievements.") - : config_1.FColor.achievement(templateObject_24 || (templateObject_24 = __makeTemplateObject(["Player ", " has ", "/", " achievements."], ["Player ", " has ", "/", " achievements."])), target.prefixedName, numberAchievements, totalAchievements)) - + "\nClick an achievement icon to show more information.", options, { onCancel: "reject", columns: 5, rows: 4, getCenterText: function () { return String.fromCharCode(Iconc.settings); }, x: x, y: y })]; - case 2: - //the loop will be aborted if the menu is cancelled (promise will reject) - _c = __read.apply(void 0, [_e.sent(), 3]), a = _c[0], x = _c[1], y = _c[2]; - if (a == achievements_1.Achievements.click_me && target == sender) - a.grantTo(sender); - return [3 /*break*/, 1]; - case 3: return [2 /*return*/]; - } - }); - }); - } - } })); -var templateObject_1, templateObject_2, templateObject_3, templateObject_4, templateObject_5, templateObject_6, templateObject_7, templateObject_8, templateObject_9, templateObject_10, templateObject_11, templateObject_12, templateObject_13, templateObject_14, templateObject_15, templateObject_16, templateObject_17, templateObject_18, templateObject_19, templateObject_20, templateObject_21, templateObject_22, templateObject_23, templateObject_24; +"use strict"; +/* +Copyright © BalaM314, 2026. All Rights Reserved. +This file contains most in-game chat commands that can be run by untrusted players. +*/ +var __makeTemplateObject = (this && this.__makeTemplateObject) || function (cooked, raw) { + if (Object.defineProperty) { Object.defineProperty(cooked, "raw", { value: raw }); } else { cooked.raw = raw; } + return cooked; +}; +var __assign = (this && this.__assign) || function () { + __assign = Object.assign || function(t) { + for (var s, i = 1, n = arguments.length; i < n; i++) { + s = arguments[i]; + for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p)) + t[p] = s[p]; + } + return t; + }; + return __assign.apply(this, arguments); +}; +var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + var desc = Object.getOwnPropertyDescriptor(m, k); + if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { + desc = { enumerable: true, get: function() { return m[k]; } }; + } + Object.defineProperty(o, k2, desc); +}) : (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + o[k2] = m[k]; +})); +var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { + Object.defineProperty(o, "default", { enumerable: true, value: v }); +}) : function(o, v) { + o["default"] = v; +}); +var __importStar = (this && this.__importStar) || (function () { + var ownKeys = function(o) { + ownKeys = Object.getOwnPropertyNames || function (o) { + var ar = []; + for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k; + return ar; + }; + return ownKeys(o); + }; + return function (mod) { + if (mod && mod.__esModule) return mod; + var result = {}; + if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]); + __setModuleDefault(result, mod); + return result; + }; +})(); +var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { + function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } + return new (P || (P = Promise))(function (resolve, reject) { + function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } + function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } + function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } + step((generator = generator.apply(thisArg, _arguments || [])).next()); + }); +}; +var __generator = (this && this.__generator) || function (thisArg, body) { + var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g = Object.create((typeof Iterator === "function" ? Iterator : Object).prototype); + return g.next = verb(0), g["throw"] = verb(1), g["return"] = verb(2), typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g; + function verb(n) { return function (v) { return step([n, v]); }; } + function step(op) { + if (f) throw new TypeError("Generator is already executing."); + while (g && (g = 0, op[0] && (_ = 0)), _) try { + if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t; + if (y = 0, t) op = [op[0] & 2, t.value]; + switch (op[0]) { + case 0: case 1: t = op; break; + case 4: _.label++; return { value: op[1], done: false }; + case 5: _.label++; y = op[1]; op = [0]; continue; + case 7: op = _.ops.pop(); _.trys.pop(); continue; + default: + if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; } + if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; } + if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; } + if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; } + if (t[2]) _.ops.pop(); + _.trys.pop(); continue; + } + op = body.call(thisArg, _); + } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; } + if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true }; + } +}; +var __read = (this && this.__read) || function (o, n) { + var m = typeof Symbol === "function" && o[Symbol.iterator]; + if (!m) return o; + var i = m.call(o), r, ar = [], e; + try { + while ((n === void 0 || n-- > 0) && !(r = i.next()).done) ar.push(r.value); + } + catch (error) { e = { error: error }; } + finally { + try { + if (r && !r.done && (m = i["return"])) m.call(i); + } + finally { if (e) throw e.error; } + } + return ar; +}; +var __spreadArray = (this && this.__spreadArray) || function (to, from, pack) { + if (pack || arguments.length === 2) for (var i = 0, l = from.length, ar; i < l; i++) { + if (ar || !(i in from)) { + if (!ar) ar = Array.prototype.slice.call(from, 0, i); + ar[i] = from[i]; + } + } + return to.concat(ar || Array.prototype.slice.call(from)); +}; +var __values = (this && this.__values) || function(o) { + var s = typeof Symbol === "function" && Symbol.iterator, m = s && o[s], i = 0; + if (m) return m.call(o); + if (o && typeof o.length === "number") return { + next: function () { + if (o && i >= o.length) o = void 0; + return { value: o && o[i++], done: !o }; + } + }; + throw new TypeError(s ? "Object is not iterable." : "Symbol.iterator is not defined."); +}; +var _a; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.commands = void 0; +var achievements_1 = require("/achievements"); +var api = __importStar(require("/api")); +var config_1 = require("/config"); +var commands_1 = require("/frameworks/commands"); +var menus_1 = require("/frameworks/menus"); +var funcs_1 = require("/funcs"); +var globals_1 = require("/globals"); +var maps_1 = require("/maps"); +var players_1 = require("/players"); +var ranks_1 = require("/ranks"); +var utils_1 = require("/utils"); +var votes_1 = require("/votes"); +exports.commands = (0, commands_1.commandList)(__assign(__assign({ about: { + args: [], + description: 'Prints information about the plugin.', + perm: commands_1.Perm.none, + handler: function (_a) { + var _b, _c; + var output = _a.output; + output("[accent][cyan]fish-commands[] is the monolithic plugin used for the Fish servers' features.\n[accent]==========\n[accent]Source code available at: [cyan]https://github.com/Fish-Community/fish-commands/\n[accent]Current plugin version: [cyan]".concat((_c = (_b = globals_1.fishPlugin.version) === null || _b === void 0 ? void 0 : _b.slice(0, 8)) !== null && _c !== void 0 ? _c : "[scarlet]null[]", "[]")); + } + }, unpause: (0, commands_1.command)({ + args: [], + description: 'Unpauses the game.', + perm: commands_1.Perm.trusted, + requirements: [commands_1.Req.mode('pvp')], + init: function () { + var data = { unpaused: false }; + Events.on(EventType.PlayEvent, function () { + if (data.unpaused) { + data.unpaused = false; + Vars.state.rules.pvpAutoPause = true; + } + }); + return data; + }, + handler: function (_a) { + var data = _a.data, outputSuccess = _a.outputSuccess; + Vars.state.rules.pvpAutoPause = false; + data.unpaused = true; + Core.app.post(function () { return Vars.state.set(GameState.State.playing); }); + outputSuccess("Unpaused."); + }, + }), tp: { + args: ['player:player'], + description: 'Teleport to another player.', + perm: commands_1.Perm.play, + requirements: [commands_1.Req.modeNot("pvp")], + handler: function (_a) { + var _b, _c, _d; + var args = _a.args, sender = _a.sender; + if (!((_b = sender.unit()) === null || _b === void 0 ? void 0 : _b.spawnedByCore)) + (0, commands_1.fail)("Can only teleport while in a core unit."); + if (sender.team() !== args.player.team()) + (0, commands_1.fail)("Cannot teleport to players on another team."); + if ((_d = (_c = sender.unit()).hasPayload) === null || _d === void 0 ? void 0 : _d.call(_c)) + (0, commands_1.fail)("Cannot teleport to players while holding a payload."); + (0, utils_1.teleportPlayer)(sender.player, args.player.player); + }, + }, clean: (0, commands_1.command)({ + args: [], + description: 'Removes all boulders from the map.', + perm: commands_1.Perm.play, + requirements: [], + data: { lastRanMapStartTime: (_a = maps_1.PartialMapRun.current) === null || _a === void 0 ? void 0 : _a.startTime }, + handler: function (_a) { + return __awaiter(this, arguments, void 0, function (_b) { + var array, removed, i, t; + var sender = _b.sender, outputSuccess = _b.outputSuccess, data = _b.data; + return __generator(this, function (_c) { + switch (_c.label) { + case 0: + if (!maps_1.PartialMapRun.current) + (0, commands_1.fail)("This game is already over."); + if (data.lastRanMapStartTime == maps_1.PartialMapRun.current.startTime) + (0, commands_1.fail)("This command was already run on this map."); + data.lastRanMapStartTime = maps_1.PartialMapRun.current.startTime; + Timer.schedule(function () { return Call.sound(sender.con, Sounds.rockBreak, 1, 1, 0); }, 0, 0.05, 10); + array = ArcReflect.get(Vars.world.tiles, "array"); + removed = 0; + i = 0; + _c.label = 1; + case 1: + if (!(i < array.length)) return [3 /*break*/, 4]; + t = array[i]; + if (!(t.breakable() && t.block() instanceof Prop)) return [3 /*break*/, 3]; + t.removeNet(); + removed++; + if (!(removed % 500 == 0)) return [3 /*break*/, 3]; + return [4 /*yield*/, (0, funcs_1.delay)(100)]; + case 2: + _c.sent(); + _c.label = 3; + case 3: + i++; + return [3 /*break*/, 1]; + case 4: + outputSuccess("Cleared the map of boulders."); + return [2 /*return*/]; + } + }); + }); + } + }), die: { + args: [], + description: 'Kills your unit.', + perm: commands_1.Perm.mod.exceptModes({ + sandbox: commands_1.Perm.play + }, "You do not have permission to die."), + handler: function (_a) { + var _b; + var sender = _a.sender; + (_b = sender.unit()) === null || _b === void 0 ? void 0 : _b.kill(); + }, + }, discord: { + args: [], + description: 'Takes you to our discord.', + perm: commands_1.Perm.none, + handler: function (_a) { + var sender = _a.sender; + Call.openURI(sender.con, config_1.text.discordURL); + }, + }, tilelog: (0, commands_1.command)({ + args: ['persist:boolean?', 'showUUID:boolean?'], + description: 'Checks the history of a tile.', + perm: commands_1.Perm.none, + data: { showUUID: true }, + handler: function (_a) { + var args = _a.args, output = _a.output, outputSuccess = _a.outputSuccess, currentTapMode = _a.currentTapMode, handleTaps = _a.handleTaps, sender = _a.sender, data = _a.data; + var changed = args.showUUID !== undefined && args.showUUID != data.showUUID; + if (args.showUUID !== undefined) { + if (!sender.hasPerm("viewUUIDs")) + (0, commands_1.fail)("You do not have permission to show UUIDs."); + data.showUUID = args.showUUID; + } + if (args.persist && currentTapMode !== "on") { + outputSuccess("Tilelog mode enabled. Click tiles to check their recent history. Run /tilelog to disable."); + handleTaps("on"); + } + else if (args.persist && changed) { + outputSuccess("".concat(data.showUUID ? "Now showing UUIDs." : "No longer showing UUIDs.", " Click tiles to check their recent history. Run /tilelog to disable.")); + handleTaps("on"); + } + else if (currentTapMode == "off" || changed) { + handleTaps("once"); + output("Click on a tile to check its recent history..."); + } + else { + handleTaps("off"); + outputSuccess("Tilelog disabled."); + } + }, + tapped: function (_a) { + var _b; + var tile = _a.tile, x = _a.x, y = _a.y, output = _a.output, sender = _a.sender, admins = _a.admins, data = _a.data; + var historyData = (_b = globals_1.tileHistory["".concat(x, ",").concat(y)]) !== null && _b !== void 0 ? _b : (0, commands_1.fail)("There is no recorded history for the selected tile (".concat(tile.x, ", ").concat(tile.y, ").")); + var history = funcs_1.StringIO.read(historyData, function (str) { return str.readArray(function (d) { return ({ + action: d.readString(2), + uuid: d.readString(3), + time: d.readNumber(16), + type: d.readString(2), + }); }, 1); }); + output("[yellow]Tile history for tile (".concat(tile.x, ", ").concat(tile.y, "):\n") + history.map(function (e) { + var _a, _b; + return globals_1.uuidPattern.test(e.uuid) + ? (sender.hasPerm("viewUUIDs") && data.showUUID + ? "[yellow]".concat((_a = admins.getInfoOptional(e.uuid)) === null || _a === void 0 ? void 0 : _a.plainLastName(), "[lightgray](").concat(e.uuid, ")[yellow] ").concat(e.action, " a [cyan]").concat(e.type, "[] ").concat((0, utils_1.formatTimeRelative)(e.time)) + : "[yellow]".concat((_b = admins.getInfoOptional(e.uuid)) === null || _b === void 0 ? void 0 : _b.plainLastName(), " ").concat(e.action, " a [cyan]").concat(e.type, "[] ").concat((0, utils_1.formatTimeRelative)(e.time))) + : "[yellow]".concat(e.uuid, "[yellow] ").concat(e.action, " a [cyan]").concat(e.type, "[] ").concat((0, utils_1.formatTimeRelative)(e.time)); + }).join('\n')); + } + }), aoelog: (0, commands_1.command)(function () { + var allowedActions = [ + "built", "broke", "rotated", "killed", "configured", "pay-dropped", "picked up", "controlled" + ]; + var cachedPointMap = Object.create(null); + return { + args: ['persist:boolean?', 'amount:number?', 'action:string?'], + description: 'Checks the history of all tiles in the selected region. Can be filtered by action.', + perm: commands_1.Perm.none, + handler: function (_a) { + var args = _a.args, sender = _a.sender, outputSuccess = _a.outputSuccess, currentTapMode = _a.currentTapMode, handleTaps = _a.handleTaps; + if (currentTapMode === "off" || args.action || args.amount) { + if (args.action && !allowedActions.includes(args.action)) + (0, commands_1.fail)("Invalid action. Allowed actions: ".concat(allowedActions.join(", "))); + if (args.amount && args.amount > 100) + (0, commands_1.fail)("Limit cannot be greater than 100."); + cachedPointMap[sender.uuid] = undefined; + handleTaps("on"); + outputSuccess("Aoelog mode enabled. To see the recent history of all tiles in a rectangular region, tap opposite corners of the rectangle. Run /aoelog with no arguments to disable."); + } + else { + handleTaps("off"); + outputSuccess("Aoelog disabled."); + } + }, + tapped: function (_a) { + var x = _a.x, y = _a.y, output = _a.output, outputFail = _a.outputFail, sender = _a.sender, admins = _a.admins, handleTaps = _a.handleTaps, args = _a.args; + function handleArea(p1, p2) { + var minX = Math.min(p1[0], p2[0]); + var maxX = Math.max(p1[0], p2[0]); + var minY = Math.min(p1[1], p2[1]); + var maxY = Math.max(p1[1], p2[1]); + var limitTiles = 0; + var amount = args.amount != null ? Math.floor(Math.abs(args.amount)) : 10; + outer: for (var i = minX; i <= maxX; i++) { + for (var j = minY; j <= maxY; j++) { + var tileData = globals_1.tileHistory["".concat(i, ",").concat(j)]; + if (!tileData) + continue; + var history = funcs_1.StringIO.read(globals_1.tileHistory["".concat(i, ",").concat(j)], function (str) { return str.readArray(function (d) { + var _a, _b, _c; + return ({ + action: (_a = d.readString(2)) !== null && _a !== void 0 ? _a : "??", + uuid: (_b = d.readString(3)) !== null && _b !== void 0 ? _b : "??", + time: d.readNumber(16), + type: (_c = d.readString(2)) !== null && _c !== void 0 ? _c : "??", + }); + }, 1); }); + if (args.action) + history = history.filter(function (e) { return e.action === args.action; }); + if (history.length == 0) + continue; + output("[yellow]Tile history for tile (".concat(i, ", ").concat(j, "):\n") + history.map(function (e) { + var _a, _b; + if (globals_1.uuidPattern.test(e.uuid)) { + if (sender.hasPerm("viewUUIDs")) + return "[yellow]".concat((_a = admins.getInfoOptional(e.uuid)) === null || _a === void 0 ? void 0 : _a.plainLastName(), "[lightgray](").concat(e.uuid, ")[yellow] ").concat(e.action, " a [cyan]").concat(e.type, "[] ").concat((0, utils_1.formatTimeRelative)(e.time)); + else + return "[yellow]".concat((_b = admins.getInfoOptional(e.uuid)) === null || _b === void 0 ? void 0 : _b.plainLastName(), " ").concat(e.action, " a [cyan]").concat(e.type, "[] ").concat((0, utils_1.formatTimeRelative)(e.time)); + } + else + return "[yellow]".concat(e.uuid, "[yellow] ").concat(e.action, " a [cyan]").concat(e.type, "[] ").concat((0, utils_1.formatTimeRelative)(e.time)); + }).join('\n')); + limitTiles++; + if (limitTiles === amount) + break outer; + } + } + if (limitTiles == 0) { + if (args.action) + outputFail("There is no recorded history for the selected region matching the provided filters."); + else + outputFail("There is no recorded history for the selected region."); + } + if (limitTiles == amount) + output("Displaying first ".concat(limitTiles, " entries. To show other entries, increase the limit or select a smaller area.")); + } + var p1 = cachedPointMap[sender.uuid]; + if (!p1) { + cachedPointMap[sender.uuid] = [x, y]; + output("1st point set at (".concat(x, ",").concat(y, ")")); + } + else { + var p2 = [x, y]; + output("2nd point set at (".concat(x, ", ").concat(y, ")")); + var width = Math.abs(p1[0] - p2[0]); + var height = Math.abs(p1[1] - p2[1]); + if (width > 50 || height > 50) + (0, commands_1.fail)("Selection too large: width/height cannot be more than 50."); + handleArea(p1, p2); + cachedPointMap[sender.uuid] = undefined; + if (!args.persist) + handleTaps("off"); + } + }, + }; + }), afk: { + args: [], + description: 'Toggles your afk status.', + perm: commands_1.Perm.none, + handler: function (_a) { + var sender = _a.sender, outputSuccess = _a.outputSuccess; + sender.manualAfk = !sender.manualAfk; + sender.updateName(); + if (sender.manualAfk) + outputSuccess("You are now marked as AFK."); + else + outputSuccess("You are no longer marked as AFK."); + }, + }, vanish: { + args: ['target:player?'], + description: "Toggles visibility of your rank and flags.", + perm: commands_1.Perm.vanish, + handler: function (_a) { + var sender = _a.sender, _b = _a.args.target, target = _b === void 0 ? sender : _b, outputSuccess = _a.outputSuccess; + if (sender.stelled()) + (0, commands_1.fail)("Marked players may not hide flags."); + if (sender.muted) + (0, commands_1.fail)("Muted players may not hide flags."); + if (sender != target && target.hasPerm("blockTrolling")) + (0, commands_1.fail)("Target is insufficentlly trollable."); + if (sender != target && !sender.ranksAtLeast("mod")) + (0, commands_1.fail)("You do not have permission to vanish other players."); + target.showRankPrefix = !target.showRankPrefix; + outputSuccess("".concat(target == sender ? "Your" : "".concat(target.name, "'s"), " rank prefix is now ").concat(target.showRankPrefix ? "visible" : "hidden", ".")); + }, + }, tileid: { + args: [], + description: 'Checks id of a tile.', + perm: commands_1.Perm.none, + handler: function (_a) { + var output = _a.output, handleTaps = _a.handleTaps; + handleTaps("once"); + output("Click a tile to see its id..."); + }, + tapped: function (_a) { + var output = _a.output, f = _a.f, tile = _a.tile; + output(f(templateObject_1 || (templateObject_1 = __makeTemplateObject(["ID is ", ""], ["ID is ", ""])), tile.block().id)); + } + } }, Object.fromEntries(config_1.FishServer.all.map(function (server) { return [ + server.name, + { + args: [], + description: "Switches to the ".concat(server.name, " server."), + perm: server.requiredPerm ? commands_1.Perm.getByName(server.requiredPerm) : commands_1.Perm.none, + isHidden: true, + handler: function (_a) { + var sender = _a.sender, lastUsedSuccessfullySender = _a.lastUsedSuccessfullySender; + if (Date.now() - lastUsedSuccessfullySender > funcs_1.Duration.minutes(1)) + players_1.FishPlayer.messageAllWithPerm(server.requiredPerm, "".concat(sender.name, "[magenta] has gone to the ").concat(server.name, " server. Use [cyan]/").concat(server.name, " [magenta]to join them!")); + Call.connect(sender.con, server.ip, server.port); + }, + }, +]; }))), { switch: { + args: ["server:string", "target:player?"], + description: "Switches to another server.", + perm: commands_1.Perm.play, + handler: function (_a) { + var _b, _c; + var args = _a.args, sender = _a.sender, f = _a.f, lastUsedSuccessfullySender = _a.lastUsedSuccessfullySender; + if (args.target != null && args.target != sender && !sender.canModerate(args.target, true, "admin", true)) + (0, commands_1.fail)(f(templateObject_2 || (templateObject_2 = __makeTemplateObject(["You do not have permission to switch player ", "."], ["You do not have permission to switch player ", "."])), args.target)); + var target = (_b = args.target) !== null && _b !== void 0 ? _b : sender; + if (globals_1.ipPortPattern.test(args.server) && sender.hasPerm("admin")) { + //direct connect + Call.connect.apply(Call, __spreadArray([target.con], __read(args.server.split(":")), false)); + } + else { + var unknownServerMessage = "Unknown server ".concat(args.server, ". Valid options: ").concat(config_1.FishServer.all.filter(function (s) { return !s.requiredPerm || sender.hasPerm(s.requiredPerm); }).map(function (s) { return s.name; }).join(", ")); + var server = (_c = config_1.FishServer.byName(args.server)) !== null && _c !== void 0 ? _c : (0, commands_1.fail)(unknownServerMessage); + //Pretend the server doesn't exist + if (server.requiredPerm && !sender.hasPerm(server.requiredPerm)) + (0, commands_1.fail)(unknownServerMessage); + if (target == sender && Date.now() - lastUsedSuccessfullySender > funcs_1.Duration.minutes(1)) + players_1.FishPlayer.messageAllWithPerm(server.requiredPerm, "".concat(sender.name, "[magenta] has gone to the ").concat(server.name, " server. Use [cyan]/").concat(server.name, " [magenta]to join them!")); + Call.connect(target.con, server.ip, server.port); + } + } + }, s: { + args: ['message:string'], + description: "Sends a message to staff only.", + perm: commands_1.Perm.chat, + handler: function (_a) { + var sender = _a.sender, args = _a.args, outputSuccess = _a.outputSuccess, outputFail = _a.outputFail, lastUsedSender = _a.lastUsedSender; + if (!sender.hasPerm("mod")) { + if (Date.now() - lastUsedSender < 4000) + (0, commands_1.fail)("This command was used recently and is on cooldown. [orange]Misuse of this command may result in a mute."); + } + api.sendStaffMessage(args.message, sender.name, sender.hasPerm("mod"), function (sent) { + if (!sender.hasPerm("mod")) { + if (sent) { + outputSuccess("Message sent to [orange]all online staff."); + } + else { + var wasReceived = players_1.FishPlayer.messageStaff(sender.prefixedName, args.message); + if (wasReceived) + outputSuccess("Message sent to staff."); + else + outputFail("No staff were online to receive your message."); + } + } + }); + }, + }, + /** + * This command is mostly for mobile (or players without foos). + * + * Since the player's unit follows the camera and we are moving the + * camera, we need to keep setting the players real position to the + * spot the command was made. This is pretty buggy but otherwise the + * player will be up the target player's butt + */ + watch: (0, commands_1.command)({ + args: ['player:player?'], + description: "Watch/unwatch a player.", + perm: commands_1.Perm.none, + data: new Set, + handler: function (_a) { + var _b; + var args = _a.args, data = _a.data, sender = _a.sender, outputSuccess = _a.outputSuccess, outputFail = _a.outputFail; + if (data.has(sender.uuid)) { + outputSuccess("No longer watching a player."); + data.delete(sender.uuid); + } + else if (args.player) { + data.add(sender.uuid); + var senderUnit_1 = (_b = sender.unit()) !== null && _b !== void 0 ? _b : (0, commands_1.fail)("You do not have a unit."); + var stayX_1 = senderUnit_1.x; + var stayY_1 = senderUnit_1.y; + var target_1 = args.player.player; + (function watch() { + var _a, _b; + var unit = target_1.unit(); + if (data.has(sender.uuid) && unit) { + // Self.X+(172.5-Self.X)/10 + Call.setCameraPosition(sender.con, unit.x, unit.y); + if (senderUnit_1) + (_b = (_a = sender.unit()) === null || _a === void 0 ? void 0 : _a.set) === null || _b === void 0 ? void 0 : _b.call(_a, stayX_1, stayY_1); + Timer.schedule(function () { return watch(); }, 0.1, 0.1, 0); + } + else { + Call.setCameraPosition(sender.con, stayX_1, stayY_1); + } + })(); + } + else { + outputFail("No player to unwatch."); + } + }, + }), spectate: (0, commands_1.command)(function () { + //TODO revise code + /** Mapping between player and original team */ + var spectators = new Map(); + function spectate(target) { + spectators.set(target, target.team()); + target.forceRespawn(); + target.setTeam(Team.derelict); + target.forceRespawn(); + } + function resume(target) { + if (spectators.get(target) == null) + return; // this state is possible for a person who left not in spectate + target.setTeam(spectators.get(target)); + spectators.delete(target); + target.forceRespawn(); + } + Events.on(EventType.GameOverEvent, function () { return spectators.clear(); }); + Events.on(EventType.PlayerLeave, function (_a) { + var player = _a.player; + return resume(players_1.FishPlayer.get(player)); + }); + return { + args: ["target:player?"], + description: "Toggles spectator mode in PVP games.", + perm: commands_1.Perm.play, + requirements: [commands_1.Req.gameRunning], + handler: function (_a) { + var sender = _a.sender, _b = _a.args.target, target = _b === void 0 ? sender : _b, outputSuccess = _a.outputSuccess, f = _a.f; + if (!config_1.Gamemode.pvp() && !sender.hasPerm("mod")) + (0, commands_1.fail)("You do not have permission to spectate on a non-pvp server."); + if (target !== sender && target.hasPerm("blockTrolling")) + (0, commands_1.fail)("Target player is insufficiently trollable."); + if (target !== sender && !sender.ranksAtLeast("admin")) + (0, commands_1.fail)("You do not have permission to force other players to spectate."); + if (spectators.has(target)) { + resume(target); + outputSuccess(target == sender + ? f(templateObject_3 || (templateObject_3 = __makeTemplateObject(["Rejoining game as team ", "."], ["Rejoining game as team ", "."])), target.team()) : f(templateObject_4 || (templateObject_4 = __makeTemplateObject(["Forced ", " out of spectator mode."], ["Forced ", " out of spectator mode."])), target)); + } + else { + spectate(target); + outputSuccess(target == sender + ? f(templateObject_5 || (templateObject_5 = __makeTemplateObject(["Now spectating. Run /spectate again to resume gameplay."], ["Now spectating. Run /spectate again to resume gameplay."]))) : f(templateObject_6 || (templateObject_6 = __makeTemplateObject(["Forced ", " into spectator mode."], ["Forced ", " into spectator mode."])), target)); + } + } + }; + }), help: { + args: ['name:string?'], + description: 'Displays a list of all commands.', + perm: commands_1.Perm.none, + handler: function (_a) { + var _b; + var args = _a.args, output = _a.output, sender = _a.sender, allCommands = _a.allCommands; + var formatCommand = function (name, color) { + return new funcs_1.StringBuilder() + .add("".concat(color, "/").concat(name)) + .chunk("[white]".concat(allCommands[name].args.map(commands_1.formatArg).join(' '))) + .chunk("[lightgray]- ".concat(allCommands[name].description)).str; + }; + var formatList = function (commandList, color) { return commandList.map(function (c) { return formatCommand(c, color); }).join('\n'); }; + if (args.name && isNaN(parseInt(args.name)) && !['mod', 'admin', 'member'].includes(args.name)) { + //name is not a number or a category, therefore it is probably a command name + if (args.name in allCommands && (!allCommands[args.name].isHidden || allCommands[args.name].perm.check(sender))) { + if (args.name == "help") + achievements_1.Achievements.help_help.grantTo(sender, false); + output("Help for command ".concat(args.name, ":\n\t").concat(allCommands[args.name].description, "\n\tUsage: [sky]/").concat(args.name, " [white]").concat(allCommands[args.name].args.map(commands_1.formatArg).join(' '), "\n\tPermission required: ").concat(allCommands[args.name].perm.name)); + } + else + (0, commands_1.fail)("Command \"".concat(args.name, "\" does not exist.")); + } + else { + var commands_2 = { + player: [], + mod: [], + admin: [], + member: [], + }; + //TODO change this to category, not perm + Object.entries(allCommands).forEach(function (_a) { + var _b = __read(_a, 2), name = _b[0], data = _b[1]; + return (data.perm === commands_1.Perm.admin ? commands_2.admin : data.perm === commands_1.Perm.mod ? commands_2.mod : data.perm === commands_1.Perm.member ? commands_2.member : commands_2.player).push(name); + }); + var chunkedPlayerCommands = (0, funcs_1.to2DArray)(commands_2.player, 15); + switch (args.name) { + case 'admin': + output("".concat(commands_1.Perm.admin.color, "-- Admin commands --\n") + formatList(commands_2.admin, commands_1.Perm.admin.color)); + break; + case 'mod': + output("".concat(commands_1.Perm.mod.color, "-- Mod commands --\n") + formatList(commands_2.mod, commands_1.Perm.mod.color)); + break; + case 'member': + output("".concat(commands_1.Perm.member.color, "-- Member commands --\n") + formatList(commands_2.member, commands_1.Perm.member.color)); + break; + default: { + var pageNumber = args.name != undefined ? parseInt(args.name) : 1; + var page = (_b = chunkedPlayerCommands[pageNumber - 1]) !== null && _b !== void 0 ? _b : (0, commands_1.fail)("\"".concat(args.name, "\" is an invalid page number.")); + output("[sky]-- Commands page [lightgrey]".concat(pageNumber, "/").concat(chunkedPlayerCommands.length, "[sky] --\n") + formatList(page, '[sky]')); + } + } + } + }, + }, msg: { + args: ['player:player', 'message:string'], + description: 'Send a message to only one player.', + perm: commands_1.Perm.chat, + handler: function (_a) { + var args = _a.args, sender = _a.sender, output = _a.output, f = _a.f; + globals_1.recentWhispers[args.player.uuid] = sender.uuid; + args.player.sendMessage("".concat(sender.prefixedName, "[lightgray] whispered:[#BBBBBB] ").concat(args.message)); + output(f(templateObject_7 || (templateObject_7 = __makeTemplateObject(["[lightgray]Whispered to ", "[lightgray]:[#BBBBBB] ", ""], ["[lightgray]Whispered to ", "[lightgray]:[#BBBBBB] ", ""])), args.player, args.message)); + }, + }, r: { + args: ['message:string'], + description: 'Reply to the most recent message.', + perm: commands_1.Perm.chat, + handler: function (_a) { + var _b; + var args = _a.args, sender = _a.sender, output = _a.output, f = _a.f; + var recipient = players_1.FishPlayer.getById((_b = globals_1.recentWhispers[sender.uuid]) !== null && _b !== void 0 ? _b : (0, commands_1.fail)("It doesn't look like someone has messaged you recently. Try whispering to them with [white]\"/msg \"")); + if (!(recipient === null || recipient === void 0 ? void 0 : recipient.connected())) + (0, commands_1.fail)("The person who last messaged you doesn't seem to exist anymore. Try whispering to someone with [white]\"/msg \""); + globals_1.recentWhispers[globals_1.recentWhispers[sender.uuid]] = sender.uuid; + recipient.sendMessage("".concat(sender.name, "[lightgray] whispered:[#BBBBBB] ").concat(args.message)); + output(f(templateObject_8 || (templateObject_8 = __makeTemplateObject(["[lightgray]Whispered to ", "[lightgray]:[#BBBBBB] ", ""], ["[lightgray]Whispered to ", "[lightgray]:[#BBBBBB] ", ""])), recipient, args.message)); + }, + }, trail: { + args: ['type:string?', 'color:string?'], + description: 'Use command to see options and toggle trail on/off.', + perm: commands_1.Perm.none, + handler: function (_a) { + var args = _a.args, sender = _a.sender, output = _a.output, outputFail = _a.outputFail, outputSuccess = _a.outputSuccess; + //overload 1: type not specified + if (!args.type) { + if (sender.trail != null) { + sender.trail = null; + outputSuccess("Trail turned off."); + } + else { + output("Available types:[yellow]\n1 - fluxVapor (flowing smoke, long lasting)\n2 - overclocked (diamonds)\n3 - overdriven (squares)\n4 - shieldBreak (smol)\n5 - upgradeCoreBloom (square, long lasting, only orange)\n6 - electrified (tiny spiratic diamonds, but only green)\n7 - unitDust (same as above but round, and can change colors)\n[white]Usage: [orange]/trail [lightgrey] [color/#hex/r,g,b]"); + } + return; + } + //overload 2: type specified + var trailTypes = { + "1": 'fluxVapor', + "2": 'overclocked', + "3": 'overdriven', + "4": 'shieldBreak', + "5": 'upgradeCoreBloom', + "6": 'electrified', + "7": 'unitDust', + }; + var selectedType = trailTypes[args.type]; + if (!selectedType) { + if (Object.values(trailTypes).includes(args.type)) + (0, commands_1.fail)("Please use the numeric id to refer to a trail type."); + else + (0, commands_1.fail)("\"".concat(args.type, "\" is not an available type.")); + } + var color = args.color ? (0, utils_1.getColor)(args.color) : Color.white; + if (color instanceof Color) { + sender.trail = { + type: selectedType, + color: color, + }; + } + else { + outputFail("[scarlet]Sorry, \"".concat(args.color, "\" is not a valid color.\n[yellow]Color can be in the following formats:\n[pink]pink [white]| [gray]#696969 [white]| 255,0,0.")); + } + }, + }, ohno: (0, commands_1.command)({ + args: [], + description: 'Spawns an ohno.', + perm: commands_1.Perm.play, + init: function () { + var Ohnos = { + enabled: true, + ohnos: new Array(), + makeOhno: function (team, x, y) { + var ohno = UnitTypes.atrax.create(team); + ohno.set(x, y); + ohno.type = UnitTypes.alpha; + ohno.apply(StatusEffects.disarmed, Number.MAX_SAFE_INTEGER); + ohno.resetController(); //does this work? + ohno.add(); + this.ohnos.push(ohno); + return ohno; + }, + updateLength: function () { + this.ohnos = this.ohnos.filter(function (o) { return o && o.isAdded() && !o.dead; }); + }, + checkAchievement: function () { + var e_1, _a; + try { + for (var _b = __values(this.ohnos), _c = _b.next(); !_c.done; _c = _b.next()) { + var ohno = _c.value; + var player = ohno.getPlayer(); + if (player) + achievements_1.Achievements.ohno.grantTo(players_1.FishPlayer.get(player), false); + } + } + catch (e_1_1) { e_1 = { error: e_1_1 }; } + finally { + try { + if (_c && !_c.done && (_a = _b.return)) _a.call(_b); + } + finally { if (e_1) throw e_1.error; } + } + }, + killAll: function () { + this.ohnos.forEach(function (ohno) { var _a; return (_a = ohno === null || ohno === void 0 ? void 0 : ohno.kill) === null || _a === void 0 ? void 0 : _a.call(ohno); }); + this.ohnos = []; + }, + amount: function () { + return this.ohnos.length; + }, + }; + Events.on(EventType.GameOverEvent, function (e) { + Ohnos.killAll(); + }); + Timer.schedule(function () { return Ohnos.checkAchievement(); }, 1, 2); + return Ohnos; + }, + requirements: [ + commands_1.Req.gameRunning, commands_1.Req.modeNot("pvp"), + commands_1.Req.unitExists("You cannot spawn ohnos while dead.") + ], + handler: function (_a) { + var sender = _a.sender, Ohnos = _a.data; + if (!Ohnos.enabled) + (0, commands_1.fail)("Ohnos have been temporarily disabled."); + Ohnos.updateLength(); + if (Ohnos.ohnos.length >= (Groups.player.size() + 1) || + sender.team().data().countType(UnitTypes.alpha) >= Units.getCap(sender.team())) + (0, commands_1.fail)("Sorry, the max number of ohno units has been reached."); + if ((0, utils_1.nearbyEnemyTile)((sender.unit()), 6) != null) + (0, commands_1.fail)("Too close to an enemy building!"); + if (!UnitTypes.alpha.supportsEnv(Vars.state.rules.env)) + (0, commands_1.fail)("Ohnos cannot survive in this map."); + Ohnos.makeOhno(sender.team(), sender.player.x, sender.player.y); + }, + }), ranks: { + args: [], + description: 'Displays information about all ranks.', + perm: commands_1.Perm.none, + handler: function (_a) { + var output = _a.output; + output("List of ranks:\n" + + Object.values(ranks_1.Rank.ranks) + .map(function (rank) { return "".concat(rank.prefix, " ").concat(rank.color).concat((0, funcs_1.capitalizeText)(rank.name), "[]: ").concat(rank.color).concat(rank.description, "[]\n"); }) + .join("") + + "List of flags:\n" + + Object.values(ranks_1.RoleFlag.flags) + .map(function (flag) { return "".concat(flag.prefix, " ").concat(flag.color).concat((0, funcs_1.capitalizeText)(flag.name), "[]: ").concat(flag.color).concat(flag.description, "[]\n"); }) + .join("")); + }, + }, rules: { + args: ['player:player?'], + description: 'Displays the server rules.', + perm: commands_1.Perm.none, + handler: function (_a) { + var _b; + var args = _a.args, sender = _a.sender, output = _a.output, outputSuccess = _a.outputSuccess, f = _a.f; + var target = (_b = args.player) !== null && _b !== void 0 ? _b : sender; + if (target !== sender) { + if (!sender.hasPerm("warn")) + (0, commands_1.fail)("You do not have permission to show rules to other players."); + if (!sender.canModerate(target)) + commands_1.Req.cooldown(funcs_1.Duration.minutes(10)); + if (target.hasPerm("blockTrolling")) + (0, commands_1.fail)(f(templateObject_9 || (templateObject_9 = __makeTemplateObject(["Player ", " is insufficiently trollable."], ["Player ", " is insufficiently trollable."])), args.player)); + } + void menus_1.Menu.menu("Rules for [#0000ff]>|||> FISH [white]servers", config_1.rules.join("\n\n"), ["[green]I agree to abide by these rules[]", "No"], target, { onCancel: "null" }).then(function (option) { + if (option == "No") { + target.kick("You must agree to the rules to play on this server. Rejoin to agree to the rules.", 1); + outputSuccess('Player rejected the rules and was kicked.'); + } + else if (option == null) { + output('Player closed the menu.'); + } + else { + outputSuccess('Player acknowledged the rules.'); + } + }); + if (target !== sender) + outputSuccess(f(templateObject_10 || (templateObject_10 = __makeTemplateObject(["Reminded ", " of the rules."], ["Reminded ", " of the rules."])), target)); + }, + }, void: { + args: ["player:player?"], + description: 'Warns other players about power voids.', + perm: commands_1.Perm.play, + requirements: function (_a) { + var args = _a.args; + return [ + commands_1.Req.mode("attack"), + args.player ? commands_1.Req.cooldown(20000) : commands_1.Req.cooldownGlobal(10000) + ]; + }, + handler: function (_a) { + var args = _a.args, sender = _a.sender, outputSuccess = _a.outputSuccess, f = _a.f; + if (args.player) { + if (!sender.hasPerm("trusted")) + (0, commands_1.fail)("You do not have permission to show popups to other players, please run /void with no arguments to send a chat message to everyone."); + if (args.player !== sender && args.player.hasPerm("blockTrolling")) + (0, commands_1.fail)("Target player is insufficiently trollable."); + void menus_1.Menu.menu("\uf83f [scarlet]WARNING[] \uf83f", "[white]Don't break the Power Void (\uF83F), it's a trap!\nPower voids disable anything they are connected to.\nIf you break it, [scarlet]you will get attacked[] by enemy units.\nPlease stop attacking and [lime]build defenses[] first!", ["I understand"], args.player, { onCancel: 'null' }).then(function () { return outputSuccess(f(templateObject_11 || (templateObject_11 = __makeTemplateObject(["Player ", " acknowledged the warning."], ["Player ", " acknowledged the warning."])), args.player)); }); + (0, utils_1.logAction)("showed void warning", sender, args.player); + outputSuccess(f(templateObject_12 || (templateObject_12 = __makeTemplateObject(["Warned ", " about power voids with a popup message."], ["Warned ", " about power voids with a popup message."])), args.player)); + } + else { + Call.sendMessage("[white]Don't break the Power Void (\uF83F), it's a trap!\nPower voids disable anything they are connected to. If you break it, [scarlet]you will get attacked[] by enemy units.\nPlease stop attacking and [lime]build defenses[] first!"); + } + }, + }, team: { + args: ['team:team', 'reason:string?'], + description: 'Changes your team.', + perm: commands_1.Perm.changeTeam, + handler: function (_a) { + var _b; + var sender = _a.sender, _c = _a.args, team = _c.team, reason = _c.reason, outputSuccess = _a.outputSuccess, f = _a.f; + if (config_1.Gamemode.sandbox() && globals_1.fishState.peacefulMode && !sender.hasPerm("admin")) + (0, commands_1.fail)("You do not have permission to change teams because peaceful mode is on."); + if (config_1.Gamemode.sandbox() && team === Vars.state.rules.waveTeam && !sender.hasPerm("admin")) + (0, commands_1.fail)("You do not have permission to change to the wave team on sandbox."); + if (!(config_1.Gamemode.sandbox() || config_1.Gamemode.testsrv()) && !sender.hasPerm("mod") && !reason) + (0, commands_1.fail)("Please specify a reason for changing teams."); + if (!sender.hasPerm("changeTeamExternal")) { + if (team.data().cores.size <= 0) + (0, commands_1.fail)("You do not have permission to change to a team with no cores."); + if (!sender.player.dead() && !((_b = sender.unit()) === null || _b === void 0 ? void 0 : _b.spawnedByCore)) + sender.forceRespawn(); + } + if (!sender.hasPerm("mod")) + sender.changedTeam = true; + sender.setTeam(team); + outputSuccess(f(templateObject_13 || (templateObject_13 = __makeTemplateObject(["Changed your team to ", "."], ["Changed your team to ", "."])), team)); + if (reason && !config_1.Gamemode.sandbox()) + (0, utils_1.logAction)("changed team to ".concat(team.name, " on ").concat((0, funcs_1.escapeTextDiscord)(Vars.state.map.plainName()), " with reason ").concat((0, funcs_1.escapeTextDiscord)(reason)), sender); + }, + }, teamp: { + args: ['team:team', 'target:player'], + description: 'Changes the team of a player.', + perm: commands_1.Perm.changeTeam, + handler: function (_a) { + var _b; + var sender = _a.sender, _c = _a.args, team = _c.team, target = _c.target, outputSuccess = _a.outputSuccess, f = _a.f; + if (!sender.canModerate(target, true, "mod", true)) + (0, commands_1.fail)(f(templateObject_14 || (templateObject_14 = __makeTemplateObject(["You do not have permission to change the team of ", ""], ["You do not have permission to change the team of ", ""])), target)); + if (config_1.Gamemode.sandbox() && globals_1.fishState.peacefulMode && !sender.hasPerm("admin")) + (0, commands_1.fail)("You do not have permission to change teams because peaceful mode is on."); + if (!sender.hasPerm("changeTeamExternal")) { + if (team.data().cores.size <= 0) + (0, commands_1.fail)("You do not have permission to change to a team with no cores."); + if (!target.player.dead() && !((_b = target.unit()) === null || _b === void 0 ? void 0 : _b.spawnedByCore)) + target.forceRespawn(); + } + target.setTeam(team); + outputSuccess(f(templateObject_15 || (templateObject_15 = __makeTemplateObject(["Changed team of player ", " to ", "."], ["Changed team of player ", " to ", "."])), target, team)); + }, + }, rank: { + args: ['player:player'], + description: 'Displays the rank of a player.', + perm: commands_1.Perm.none, + handler: function (_a) { + var args = _a.args, output = _a.output, f = _a.f; + output(f(templateObject_16 || (templateObject_16 = __makeTemplateObject(["Player ", "'s rank is ", "."], ["Player ", "'s rank is ", "."])), args.player, args.player.rank)); + }, + }, forcevnw: { + args: ["force:boolean?"], + description: 'Force skip to the next wave.', + perm: commands_1.Perm.admin, + handler: function (_a) { + var allCommands = _a.allCommands, sender = _a.sender, _b = _a.args.force, force = _b === void 0 ? true : _b; + if (allCommands.vnw.data.manager.session == null) { + if (force == false) + (0, commands_1.fail)("Cannot clear votes for VNW because no vote is currently ongoing."); + (0, utils_1.skipWaves)(1, true); + } + else { + if (force) + Call.sendMessage("VNW: [green]Vote was forced by admin [yellow]".concat(sender.name, "[green], skipping wave.")); + else + Call.sendMessage("VNW: [red]Votes cleared by admin [yellow]".concat(sender.name, "[red].")); + allCommands.vnw.data.manager.forceVote(force); + } + }, + }, vnw: (0, commands_1.command)({ + args: ["waves:number?"], + description: "Vote to start the next wave.", + perm: commands_1.Perm.play, + init: function () { return ({ + manager: new votes_1.VoteManager(funcs_1.Duration.minutes(1.5)) + .on("success", function (t) { return (0, utils_1.skipWaves)(t.session.data, true); }) + .on("vote passed", function () { return Call.sendMessage('VNW: [green]Vote passed, skipping to next wave.'); }) + .on("vote failed", function () { return Call.sendMessage('VNW: [red]Vote failed.'); }) + .on("player vote change", function (t, player) { return Call.sendMessage("VNW: ".concat(player.name, " [white] has voted on skipping [accent]").concat(t.session.data, "[white] wave(s). [green]").concat(t.currentVotes(), "[white] votes, [green]").concat(t.requiredVotes(), "[white] required.")); }) + .on("player vote removed", function (t, player) { return Call.sendMessage("VNW: ".concat(player.name, " [white] has left. [green]").concat(t.currentVotes(), "[white] votes, [green]").concat(t.requiredVotes(), "[white] required.")); }) + }); }, + requirements: [commands_1.Req.cooldown(3000), commands_1.Req.integerRange("waves", 1, 15), commands_1.Req.mode("survival", "testsrv"), commands_1.Req.gameRunning], + handler: function (_a) { + return __awaiter(this, arguments, void 0, function (_b) { + var _c; + var sender = _b.sender, waves = _b.args.waves, manager = _b.data.manager; + return __generator(this, function (_d) { + switch (_d.label) { + case 0: + if (!!manager.session) return [3 /*break*/, 4]; + if (!(waves !== null && waves !== void 0)) return [3 /*break*/, 1]; + _c = waves; + return [3 /*break*/, 3]; + case 1: return [4 /*yield*/, menus_1.Menu.menu("Start a Next Wave Vote", "Select the amount of waves you would like to skip.", [1, 5, 10], sender, { + includeCancel: true, + optionStringifier: function (n) { return "".concat(n, " waves"); } + })]; + case 2: + _c = (waves = _d.sent()); + _d.label = 3; + case 3: + _c; + if (manager.session) { + //Someone else started a vote + if (manager.session.data != waves) + (0, commands_1.fail)("Someone else started a vote with a different number of waves to skip."); + else + manager.vote(sender, sender.voteWeight(), waves); + } + else { + manager.start(sender, sender.voteWeight(), waves); + } + return [3 /*break*/, 5]; + case 4: + manager.vote(sender, sender.voteWeight(), null); + _d.label = 5; + case 5: return [2 /*return*/]; + } + }); + }); + } + }), forcertv: { + args: ["force:boolean?"], + description: 'Force skip to the next map.', + perm: commands_1.Perm.admin, + handler: function (_a) { + var _b = _a.args.force, force = _b === void 0 ? true : _b, sender = _a.sender, allCommands = _a.allCommands; + if (allCommands.rtv.data.manager.session == null) { + if (force == false) + (0, commands_1.fail)("Cannot clear votes for RTV because no vote is currently ongoing."); + allCommands.rtv.data.manager.forceVote(true); + } + else { + if (force) + Call.sendMessage("RTV: [green]Vote was forced by admin [yellow]".concat(sender.name, "[green].")); + else + Call.sendMessage("RTV: [red]Votes cleared by admin [yellow]".concat(sender.name, "[red].")); + allCommands.rtv.data.manager.forceVote(force); + } + } + }, rtv: (0, commands_1.command)({ + args: [], + description: 'Rock the vote to change map.', + perm: commands_1.Perm.play, + init: function () { return ({ + manager: new votes_1.VoteManager(funcs_1.Duration.minutes(1.5), config_1.Gamemode.hexed() ? ["fractionOfVoters", 1] : undefined) //Require unanimity in Hexed, as it is often 1 v everyone + .on("success", function () { return (0, utils_1.neutralGameover)(); }) + .on("vote passed", function () { return Call.sendMessage("RTV: [green]Vote has passed, changing map."); }) + .on("vote failed", function () { return Call.sendMessage("RTV: [red]Vote failed."); }) + .on("player vote change", function (t, player, oldVote, newVote) { return Call.sendMessage("RTV: ".concat(player.name, "[white] ").concat(oldVote == newVote ? "still " : "", "wants to change the map. [green]").concat(t.currentVotes(), "[white] votes, [green]").concat(t.requiredVotes(), "[white] required.")); }) + .on("player vote removed", function (t, player) { return Call.sendMessage("RTV: ".concat(player.name, "[white] has left the game. [green]").concat(t.currentVotes(), "[white] votes, [green]").concat(t.requiredVotes(), "[white] required.")); }) + }); }, + requirements: [commands_1.Req.cooldown(3000), commands_1.Req.gameRunning], + handler: function (_a) { + var sender = _a.sender, manager = _a.data.manager; + manager.vote(sender, 1, 0); //No weighting for RTV except for removing AFK players + } + }), + // votekick: command({ + // args: ["target:player"], + // description: "Starts a vote to kick a player.", + // perm: Perm.play, + // data: new VoteManager( + // Duration.seconds(20), + // ["absolute", 3], + // (fishP, target) => fishP.team() == target.team() || fishP.hasPerm("voteOtherTeams") + // ), + // handler({args, sender, data: votekickmanager}){ + // if(votekickmanager.session) fail(`There is already a votekick in progress.`); + // votekickmanager.start(sender, 1, args.target); + // } + // }), + // vote: { + // args: ["vote:boolean"], + // description: "Use /votekick instead.", + // perm: Perm.play, + // handler({sender, args, allCommands}){ + // const votekickmanager = allCommands.votekick.data; + // votekickmanager.handleVote(sender, args ? 1 : -1); + // } + // }, + forcenextmap: { + args: ["map:mapOrRandom"], + description: 'Override the next map in queue.', + perm: commands_1.Perm.admin.exceptModes({ + testsrv: commands_1.Perm.play + }), + handler: function (_a) { + var allCommands = _a.allCommands, args = _a.args, sender = _a.sender, outputSuccess = _a.outputSuccess, f = _a.f; + Vars.maps.setNextMapOverride(args.map == "random" ? null : args.map); + if (allCommands.nextmap.data.voteEndTime() > -1) { + //Cancel /nextmap vote if it's ongoing + allCommands.nextmap.data.resetVotes(); + Call.sendMessage("[red]Admin ".concat(sender.name, "[red] has cancelled the vote. The next map will be ").concat(args.map == "random" ? "random" : "[yellow]".concat(args.map.name()), ".")); + } + else { + outputSuccess(f(templateObject_17 || (templateObject_17 = __makeTemplateObject(["Forced the next map to be ", "."], ["Forced the next map to be ", "."])), args.map == "random" ? "random" : "\"".concat(args.map.name(), "\" by ").concat(args.map.author()))); + } + }, + }, maps: { + args: [], + description: 'Lists the available maps.', + perm: commands_1.Perm.none, + handler: function (_a) { + var output = _a.output; + output("[yellow]Use [white]/nextmap [lightgray][[map name] [yellow]to vote on a map.\n\n[blue]Available maps:\n_________________________\n".concat(Vars.maps.customMaps().toArray().map(function (map) { + return "[yellow]".concat(map.name()); + }).join("\n"))); + } + }, nextmap: (0, commands_1.command)(function () { + var random = { + name: function () { return "[lightgray]Random"; }, + plainName: function () { return "random"; } + }; + var votes = new Map(); + var lastVoteCount = 0; + var lastVoteTime = 0; + var voteEndTime = -1; + var voteDuration = funcs_1.Duration.minutes(1.5); + var task = null; + var isThrowbackFile = function (file) { return config_1.throwbackMapFileNames.includes(file.name()); }; + var throwbackMaps = function () { return config_1.throwbackMapFileNames + .map(function (f) { return Vars.maps.customMaps().find(function (m) { return m.file.name() == f; }); }) + .filter(function (m) { return m != null; }); }; + var normalMaps = function () { return Vars.maps.customMaps().toArray().filter(function (m) { return !isThrowbackFile(m.file); }); }; + function showMapDetails(target, map) { + return __awaiter(this, void 0, void 0, function () { + var res; + return __generator(this, function (_a) { + switch (_a.label) { + case 0: return [4 /*yield*/, menus_1.Menu.buttons(target, map.name(), "[accent]Description: [white]".concat(map.description(), "\n[accent]Author: [white]").concat(map.author(), "\n[accent]Fastest Time: [white]").concat((0, utils_1.formatTime)(maps_1.FMap.getCreate(map).stats().shortestTime), "\n[accent]Runs: [white]").concat(maps_1.FMap.getCreate(map).stats().allRunCount, "\n[accent]Winrate: [white]").concat((maps_1.FMap.getCreate(map).stats().winRate * 100).toFixed(2), "%"), [[ + { data: "vote", text: "[green]Vote for this Map" } + ], [ + { data: "back", text: "[red]Back" } + ]], { onCancel: "null" })]; + case 1: + res = _a.sent(); + return [2 /*return*/, res !== null && res !== void 0 ? res : "back"]; + } + }); + }); + } + function pickMapFromList(target, title, maps) { + return __awaiter(this, void 0, void 0, function () { + var map, action; + return __generator(this, function (_a) { + switch (_a.label) { + case 0: return [4 /*yield*/, menus_1.Menu.pagedList(target, title, "Select a map to view more information.", maps, { optionStringifier: function (map) { return map.name(); }, rowsPerPage: 10, columns: 1 })]; + case 1: + map = _a.sent(); + return [4 /*yield*/, showMapDetails(target, map)]; + case 2: + action = _a.sent(); + if (action === "vote") + return [2 /*return*/, map]; + return [2 /*return*/, null]; + } + }); + }); + } + function currentMenu(target) { + return __awaiter(this, void 0, void 0, function () { + var result, _a, picked, throwback, picked, campaign, campaignMaps, picked; + var _b, _c, _d, _e; + return __generator(this, function (_f) { + switch (_f.label) { + case 0: return [4 /*yield*/, menus_1.Menu.menu("Select a map", "[accent]---Current Map---\nMap Name: [white]".concat(Vars.state.map.name(), "\n[accent]Map Author: [white]").concat(Vars.state.map.author(), "\nFastest Time: [white]").concat((0, utils_1.formatTime)(maps_1.FMap.getCreate(Vars.state.map).stats().shortestTime), "\nCurrent Time: [white]").concat((0, utils_1.formatTime)((_e = (_c = (_b = maps_1.PartialMapRun.current) === null || _b === void 0 ? void 0 : _b.duration()) !== null && _c !== void 0 ? _c : (_d = maps_1.FMap.getCreate(Vars.state.map).runs.at(-1)) === null || _d === void 0 ? void 0 : _d.duration()) !== null && _e !== void 0 ? _e : 0)), ["[green]Current Maps", "[yellow]Throwback Maps", "[orange]Campaigns"], target, { columns: 1, includeCancel: "Close" })]; + case 1: + result = _f.sent(); + _a = result; + switch (_a) { + case '[green]Current Maps': return [3 /*break*/, 2]; + case '[yellow]Throwback Maps': return [3 /*break*/, 4]; + case '[orange]Campaigns': return [3 /*break*/, 6]; + } + return [3 /*break*/, 9]; + case 2: return [4 /*yield*/, pickMapFromList(target, "Current Maps", normalMaps())]; + case 3: + picked = _f.sent(); + if (picked) + return [2 /*return*/, picked]; + return [2 /*return*/, currentMenu(target)]; + case 4: + throwback = throwbackMaps(); + if (throwback.length == 0) + (0, commands_1.fail)("No throwback maps are currently available."); + return [4 /*yield*/, pickMapFromList(target, "Throwback Maps", throwback)]; + case 5: + picked = _f.sent(); + if (picked) + return [2 /*return*/, picked]; + return [2 /*return*/, currentMenu(target)]; + case 6: + if (config_1.campaigns.length == 0) + (0, commands_1.fail)("No campaigns are currently available."); + return [4 /*yield*/, menus_1.Menu.pagedList(target, "Campaigns", "Select a campaign to view its maps.", config_1.campaigns, { optionStringifier: function (c) { return c.name; }, rowsPerPage: 10, columns: 1 })]; + case 7: + campaign = _f.sent(); + campaignMaps = campaign.mapFileNames + .map(function (f) { return Vars.maps.customMaps().find(function (m) { return m.file.name() == f; }); }) + .filter(function (m) { return m != null; }); + if (campaignMaps.length == 0) + (0, commands_1.fail)("Campaign \"".concat(campaign.name, "\" has no available maps.")); + return [4 /*yield*/, pickMapFromList(target, "Campaign: ".concat(campaign.name), campaignMaps)]; + case 8: + picked = _f.sent(); + if (picked) + return [2 /*return*/, picked]; + return [2 /*return*/, currentMenu(target)]; + case 9: + ; + return [2 /*return*/]; + } + }); + }); + } + function sendVote(sender, map) { + votes.set(sender, map); + if (voteEndTime == -1) { + if ((Date.now() - lastVoteTime) < funcs_1.Duration.minutes(1)) + (0, commands_1.fail)("Please wait 1 minute before starting a new map vote."); + startVote(); + Call.sendMessage("[cyan]Next Map Vote: ".concat(sender.name, "[cyan] started a map vote, and voted for [yellow]").concat(map.name(), "[cyan]. Use [white]/nextmap ").concat(map.plainName(), "[] to add your vote, or run [white]/maps[] to see other available maps.")); + } + else { + Call.sendMessage("[cyan]Next Map Vote: ".concat(sender.name, "[cyan] voted for [yellow]").concat(map.name(), "[cyan]. Time left: [scarlet]").concat((0, utils_1.formatTimeRelative)(voteEndTime, true))); + showVotes(); + } + } + function resetVotes() { + votes.clear(); + voteEndTime = -1; + task === null || task === void 0 ? void 0 : task.cancel(); + } + function getMapData() { + return __spreadArray([], __read(votes.values()), false).reduce(function (acc, map) { return (acc.increment(map), acc); }, new ObjectIntMap()).entries().toArray(); + } + function showVotes() { + Call.sendMessage("[green]Current votes:\n-----------------------------\n".concat(getMapData().map(function (_a) { + var map = _a.key, votes = _a.value; + return "[cyan]".concat(map.name(), "[yellow]: ").concat(votes); + }).toString("\n"))); + } + function startVote() { + voteEndTime = Date.now() + voteDuration; + task = Timer.schedule(endVote, voteDuration / 1000); + } + function endVote() { + if (voteEndTime == -1) + return; //aborted somehow + if (votes.size == 0) + return; //no votes? + if (votes.size + 2 <= lastVoteCount && (Date.now() - lastVoteTime) < funcs_1.Duration.minutes(10)) { + //If the number of votes is 2 less than the previous number of votes for a vote in the past 10 minutes, abor + Call.sendMessage("[cyan]Next Map Vote: [scarlet]Vote aborted because a previous vote had significantly higher turnout"); + resetVotes(); + return; + } + else { + lastVoteTime = Date.now(); + lastVoteCount = votes.size; + } + var mapData = getMapData(); + var highestVoteCount = mapData.max(floatf(function (e) { return e.value; })).value; + var highestVotedMaps = mapData.select(function (e) { return e.value == highestVoteCount; }); + var winner; + if (highestVotedMaps.size > 1) { + winner = highestVotedMaps.random().key; + Call.sendMessage("[green]There was a tie between the following maps:\n".concat(highestVotedMaps.map(function (_a) { + var map = _a.key, votes = _a.value; + return "[cyan]".concat(map.name(), "[yellow]: ").concat(votes); + }).toString("\n"), "\n[green]Picking random winner: [yellow]").concat(winner.name())); + } + else { + winner = highestVotedMaps.get(0).key; + Call.sendMessage("[green]Map voting complete! The next map will be [yellow]".concat(winner.name(), " [green]with [yellow]").concat(highestVoteCount, "[green] votes.")); + } + Vars.maps.setNextMapOverride(winner == random ? null : winner); + resetVotes(); + } + Events.on(EventType.GameOverEvent, resetVotes); + Events.on(EventType.ServerLoadEvent, resetVotes); + return { + args: ['map:mapOrRandom?'], + description: 'Allows you to vote for the next map.', + perm: commands_1.Perm.play, + data: { votes: votes, voteEndTime: function () { return voteEndTime; }, resetVotes: resetVotes, endVote: endVote }, + requirements: [commands_1.Req.cooldown(10000)], + handler: function (_a) { + return __awaiter(this, arguments, void 0, function (_b) { + var _c, _d, map; + var _e; + var args = _b.args, sender = _b.sender; + return __generator(this, function (_f) { + switch (_f.label) { + case 0: + if (config_1.Gamemode.testsrv()) + (0, commands_1.fail)("Please use /forcenextmap instead."); + if (votes.get(sender)) + (0, commands_1.fail)("You have already voted."); + if (!((_e = args.map) !== null && _e !== void 0)) return [3 /*break*/, 1]; + _c = _e; + return [3 /*break*/, 3]; + case 1: + _d = args; + return [4 /*yield*/, currentMenu(sender)]; + case 2: + _c = (_d.map = _f.sent()); + _f.label = 3; + case 3: + _c; + map = args.map === "random" ? random : args.map; + sendVote(sender, map); + return [2 /*return*/]; + } + }); + }); + } + }; + }), surrender: (0, commands_1.command)(function () { + var prefix = "[orange]Surrender[white]: "; + var managers = Team.all.map(function (team) { + return new votes_1.VoteManager(funcs_1.Duration.minutes(1.5), ["fractionOfVoters", config_1.Gamemode.hexed() ? 1 : 3 / 4], function (p) { return p.team() == team; }) + .on("success", function () { return team.cores().copy().each(function (c) { return c.kill(); }); }) + .on("vote passed", function () { return Call.sendMessage(prefix + "Team ".concat(team.coloredName(), " has voted to forfeit this match.")); }) + .on("vote failed", function (t) { return t.messageEligibleVoters(prefix + "Team ".concat(team.coloredName(), " has chosen not to forfeit this match.")); }) + .on("player vote change", function (t, player, oldVote, newVote) { return t.messageEligibleVoters(prefix + "".concat(player.name, "[white] ").concat(oldVote == newVote ? "still " : "", "wants to forfeit this match. [orange]").concat(t.currentVotes(), "[white] votes, [orange]").concat(t.requiredVotes(), "[white] required.")); }) + .on("player vote removed", function (t, player) { return t.messageEligibleVoters(prefix + "Player ".concat(player.name, "[white] has left the game. [orange]").concat(t.currentVotes(), "[white] votes, [orange]").concat(t.requiredVotes(), "[white] required.")); }); + }); + globals_1.FishEvents.on("playerTeamChange", function (_, fishP, previous) { + managers[previous.id].unvote(fishP); + }); + return { + args: ["force:boolean?", "team:team?"], + description: "Vote to surrender to the enemy team.", + perm: commands_1.Perm.play, + requirements: [commands_1.Req.mode("pvp"), commands_1.Req.teamAlive], + data: { managers: managers }, + handler: function (_a) { + return __awaiter(this, arguments, void 0, function (_b) { + var t, manager; + var sender = _b.sender, _c = _b.args, force = _c.force, team = _c.team; + return __generator(this, function (_d) { + switch (_d.label) { + case 0: + t = sender.hasPerm("admin") && team ? team : sender.team(); + manager = managers[t.id]; + if (!(sender.hasPerm("admin") && force != undefined)) return [3 /*break*/, 4]; + if (!force) return [3 /*break*/, 2]; + return [4 /*yield*/, menus_1.Menu.confirmDangerous(sender, "Are you sure you want to force team ".concat(t.coloredName(), "[] to lose?"))]; + case 1: + _d.sent(); + manager.messageEligibleVoters(prefix + "Vote forced by admin ".concat(sender.name, "[white].")); + Call.sendMessage(prefix + "Team ".concat(t.coloredName(), " has voted to forfeit this match.")); + return [3 /*break*/, 3]; + case 2: + manager.messageEligibleVoters(prefix + "Votes cleared by admin ".concat(sender.name, "[white].")); + _d.label = 3; + case 3: + manager.forceVote(force); + return [2 /*return*/]; + case 4: + if (sender.ranksAtLeast("mod")) + commands_1.Req.cooldown(5000); + else + commands_1.Req.cooldown(20000); + manager.vote(sender, 1, 0); + return [2 /*return*/]; + } + }); + }); + }, + }; + }), stats: { + args: ["target:player", "global:boolean?"], + perm: commands_1.Perm.none, + description: "Views a player's stats.", + handler: function (_a) { + var _b = _a.args, target = _b.target, _c = _b.global, global = _c === void 0 ? false : _c, output = _a.output, f = _a.f; + var stats = global ? target.globalStats : target.stats; + output(f(templateObject_18 || (templateObject_18 = __makeTemplateObject(["[accent]Statistics for player ", " ", ":\n(note: we started recording statistics on 22 Jan 2024)\n[white]--------------[]\nBlocks broken: ", "\nBlocks placed: ", "\nChat messages sent: ", "\nGames finished: ", "\nTime in-game: ", "\nWin rate: ", ""], ["[accent]\\\nStatistics for player ", " ", ":\n(note: we started recording statistics on 22 Jan 2024)\n[white]--------------[]\nBlocks broken: ", "\nBlocks placed: ", "\nChat messages sent: ", "\nGames finished: ", "\nTime in-game: ", "\nWin rate: ", ""])), target, global ? "across all servers" : "on this server", stats.blocksBroken, stats.blocksPlaced, stats.chatMessagesSent, stats.gamesFinished, (0, utils_1.formatTime)(stats.timeInGame), stats.gamesWon / stats.gamesFinished)); + } + }, showworld: { + args: ["x:number?", "y:number?", "size:number?"], + perm: commands_1.Perm.none, + description: "Views the world as a 2D scrollable menu.", + requirements: [commands_1.Req.cooldown(4000), commands_1.Req.integerRange("size", 1, 20)], + handler: function (_a) { + var sender = _a.sender, _b = _a.args, _c = _b.size, size = _c === void 0 ? 7 : _c, x = _b.x, y = _b.y; + if (Vars.state.rules.fog) + (0, commands_1.fail)("This command is disabled when fog is enabled."); + var options = (0, funcs_1.to2DArray)(Reflect.get(Vars.world.tiles, "array").map(function (tile) { return ({ + text: tile.block().emoji(), + data: null, + }); }), Vars.world.width()).reverse(); + var height = Vars.world.height(); + void menus_1.Menu.scroll2D(sender, "The World", "Use the arrow keys to navigate around the world. Click a blank square to exit.", options, { + columns: size, + rows: size, + x: x ? x - Math.trunc(size / 2) : 0, + y: height - (y ? y + 1 + Math.trunc(size / 2) : size), + getCenterText: function (x, y) { return "".concat(x, ",").concat(height - y - size); } + }); + } + }, mapinfo: { + args: ["map:map?"], + perm: commands_1.Perm.none, + description: "Displays information about a map.", + handler: function (_a) { + var _b; + var output = _a.output, map = _a.args.map, f = _a.f, sender = _a.sender; + if (map) { + var fmap = (_b = maps_1.FMap.getCreate(map)) !== null && _b !== void 0 ? _b : (0, commands_1.fail)("Map data is still being loaded, try again later."); + output(fmap.displayStats(f)); + } + else { + void menus_1.Menu.textPages(sender, Vars.maps.customMaps().map(function (m) { + return ["Map information", function () { var _a, _b; return (_b = (_a = maps_1.FMap.getCreate(m)) === null || _a === void 0 ? void 0 : _a.displayStats(f)) !== null && _b !== void 0 ? _b : (0, commands_1.fail)("Map data is still being loaded, try again later."); }]; + }).toArray(), [], { + startPage: Vars.maps.customMaps().toArray().indexOf(Vars.state.map), + }); + } + } + }, gamemode: { + args: ["mode:string"], + perm: new commands_1.Perm("changeGamemode", "manager").exceptModes({ + testsrv: commands_1.Perm.play, + }), + description: "Sets the gamemode.", + requirements: [commands_1.Req.cooldownGlobal(10000)], + handler: function (_a) { + var args = _a.args, sender = _a.sender, outputSuccess = _a.outputSuccess, lastUsedSuccessfully = _a.lastUsedSuccessfully; + if (!sender.hasPerm('trusted')) + commands_1.Req.cooldownGlobal(30000)({ lastUsedSuccessfully: lastUsedSuccessfully }); + //Unpause + Vars.state.set(GameState.State.playing); + switch (args.mode) { + case "attack": + Vars.state.rules.attackMode = true; + Vars.state.rules.pvp = false; + Vars.state.rules.infiniteResources = false; + break; + case "survival": + Vars.state.rules.attackMode = false; + Vars.state.rules.waves = true; + Vars.state.rules.pvp = false; + Vars.state.rules.infiniteResources = false; + break; + case "pvp": + Vars.state.rules.attackMode = true; + Vars.state.rules.pvp = true; + Vars.state.rules.waves = false; + Vars.state.rules.infiniteResources = false; + break; + case "sandbox": + Vars.state.rules.attackMode = true; + Vars.state.rules.pvp = false; + Vars.state.rules.waves = false; + Vars.state.rules.infiniteResources = true; + break; + default: (0, commands_1.fail)("Invalid mode, valid modes are: attack, survival, pvp"); + } + var reloader = new WorldReloader(); + Reflect.set(reloader, "wasServer", true); + Reflect.set(reloader, "players", Groups.player.copy()); + Call.worldDataBegin(); + reloader.end(); + Call.sendMessage("[orange]Player ".concat(sender.cleanedName, " changed the gamemode to ").concat(args.mode)); + outputSuccess("Changed mode to ".concat(args.mode)); + } + }, mixunit: { + args: ["type:unittype", "base:unittype"], + description: "Spawns a unit that is made of two unit types mixed together.", + perm: commands_1.Perm.admin.exceptModes({ + sandbox: commands_1.Perm.play + }), + requirements: function (_a) { + var sender = _a.sender; + return [!sender.hasPerm("admin") && commands_1.Req.cooldown(1500), commands_1.Req.unitExists()].filter(Boolean); + }, + handler: function (_a) { + var args = _a.args, sender = _a.sender, f = _a.f, outputSuccess = _a.outputSuccess; + var _b = sender.unit(), team = _b.team, x = _b.x, y = _b.y; + var unit = args.base.create(team); + unit.type = args.type; + unit.maxHealth = args.type.health; //because half-dead units aren't fun + unit.set(x, y); + unit.add(); + outputSuccess(f(templateObject_19 || (templateObject_19 = __makeTemplateObject(["Spawned a ", " that is partly a ", "."], ["Spawned a ", " that is partly a ", "."])), args.type, args.base)); + } + }, achievement: { + args: ["name:string?", "verbose:boolean?"], + description: "Displays information on a specific achievement.", + perm: commands_1.Perm.none, + handler: function (_a) { + return __awaiter(this, arguments, void 0, function (_b) { + var matching, achievement, _c; + var _d = _b.args, _e = _d.name, name = _e === void 0 ? "" : _e, _f = _d.verbose, verbose = _f === void 0 ? false : _f, sender = _b.sender, f = _b.f, output = _b.output; + return __generator(this, function (_g) { + switch (_g.label) { + case 0: + name = Strings.stripColors(name.toLowerCase()); + matching = achievements_1.Achievement.all.filter(function (a) { return Strings.stripColors(a.name).toLowerCase().includes(name); }); + if (matching.length == 0) + (0, commands_1.fail)(f(templateObject_20 || (templateObject_20 = __makeTemplateObject(["No achievements found with name ", ". To view all achievements, run [accent]/achievements[]."], ["No achievements found with name ", ". To view all achievements, run [accent]/achievements[]."])), name)); + if (!(matching.length > 2)) return [3 /*break*/, 2]; + return [4 /*yield*/, menus_1.Menu.pagedList(sender, "Achievement", "Select an achievement to view", matching, { + onCancel: "reject", + columns: 2, + optionStringifier: function (a) { return "".concat(a.icon, "[] ").concat(a.name); } + })]; + case 1: + _c = _g.sent(); + return [3 /*break*/, 3]; + case 2: + _c = matching[0]; + _g.label = 3; + case 3: + achievement = _c; + output(config_1.FColor.achievement(templateObject_21 || (templateObject_21 = __makeTemplateObject(["Achievement ", " ", "\n[white]--------------[]\n", "\nAllowed modes: ", "\nUnlocked: ", "\n", "", "", ""], ["\\\nAchievement ", " ", "\n[white]--------------[]\n", "\nAllowed modes: ", "\nUnlocked: ", "\n", "\\\n", "\\\n", "\\\n"])), achievement.icon, achievement.name, achievement.description + (achievement.extendedDescription ? ("\n" + "[gray]".concat(achievement.extendedDescription)) : ""), achievement.modesText, f.boolGood(achievement.has(sender)), verbose ? "[gray]ID: (".concat(achievement.nid, ")").concat(achievement.sid, "\n") : "", verbose ? "[gray]Notifies: ".concat(achievement.notify, "\n") : "", achievement.hidden ? "This achievement is secret." : "")); + return [2 /*return*/]; + } + }); + }); + } + }, achievementlist: { + args: ["target:player?"], + description: "Shows all achievements in a paged menu.", + perm: commands_1.Perm.none, + handler: function (_a) { + return __awaiter(this, arguments, void 0, function (_b) { + var sender = _b.sender, _c = _b.args.target, target = _c === void 0 ? sender : _c, f = _b.f; + return __generator(this, function (_d) { + switch (_d.label) { + case 0: return [4 /*yield*/, menus_1.Menu.textPages(sender, achievements_1.Achievement.all.filter(function (a) { return !a.hidden || a.has(target); }) + .map(function (a) { return [ + "".concat(a.icon, "[] ").concat(a.name), + function () { return config_1.FColor.achievement(templateObject_22 || (templateObject_22 = __makeTemplateObject(["", "\nAllowed modes: ", "\nUnlocked: ", "\n", ""], ["\\\n", "\nAllowed modes: ", "\nUnlocked: ", "\n", "\\\n"])), a.description + (a.extendedDescription ? ("\n" + "[gray]".concat(a.extendedDescription)) : ""), a.modesText, f.boolGood(a.has(target)), a.hidden ? "This achievement is secret." : ""); } + ]; }))]; + case 1: + _d.sent(); + return [2 /*return*/]; + } + }); + }); + } + }, achievementgrid: { + args: ["target:player?"], + description: "Shows all achievements in a 2D scrolling menu.", + perm: commands_1.Perm.none, + handler: function (_a) { + return __awaiter(this, arguments, void 0, function (_b) { + var visibleAchievements, options, numberAchievements, totalAchievements, x, y, a; + var _c; + var sender = _b.sender, _d = _b.args.target, target = _d === void 0 ? sender : _d, f = _b.f; + return __generator(this, function (_e) { + switch (_e.label) { + case 0: + visibleAchievements = achievements_1.Achievement.all.filter(function (a) { return !a.hidden || a.has(target); }); + options = (0, funcs_1.to2DArray)(visibleAchievements, 7).map(function (row) { return row.map(function (a) { return ({ + data: a, + text: a.has(target) ? a.icon : "[gray]".concat(Strings.stripColors(a.icon)), + }); }); }); + numberAchievements = achievements_1.Achievement.all.filter(function (a) { return a.has(target); }).length; + totalAchievements = visibleAchievements.length; + x = 0, y = 0; + a = null; + _e.label = 1; + case 1: + if (!true) return [3 /*break*/, 3]; + return [4 /*yield*/, menus_1.Menu.scroll2D(sender, "Achievements", a ? config_1.FColor.achievement(templateObject_23 || (templateObject_23 = __makeTemplateObject(["", " ", "\n\n", "\n\nAllowed modes: ", "\nUnlocked: ", "\n", ""], ["\\\n", " ", "\n\n", "\n\nAllowed modes: ", "\nUnlocked: ", "\n", "\\\n"])), a.icon, a.name, a.description + (a.extendedDescription ? ("\n" + "[gray]".concat(a.extendedDescription)) : ""), a.modesText, f.boolGood(a.has(target)), a.hidden ? "This achievement is secret." : "") : + (target == sender ? "You have ".concat(numberAchievements, "/").concat(totalAchievements, " achievements.") + : config_1.FColor.achievement(templateObject_24 || (templateObject_24 = __makeTemplateObject(["Player ", " has ", "/", " achievements."], ["Player ", " has ", "/", " achievements."])), target.prefixedName, numberAchievements, totalAchievements)) + + "\nClick an achievement icon to show more information.", options, { onCancel: "reject", columns: 5, rows: 4, getCenterText: function () { return String.fromCharCode(Iconc.settings); }, x: x, y: y })]; + case 2: + //the loop will be aborted if the menu is cancelled (promise will reject) + _c = __read.apply(void 0, [_e.sent(), 3]), a = _c[0], x = _c[1], y = _c[2]; + if (a == achievements_1.Achievements.click_me && target == sender) + a.grantTo(sender); + return [3 /*break*/, 1]; + case 3: return [2 /*return*/]; + } + }); + }); + } + } })); +var templateObject_1, templateObject_2, templateObject_3, templateObject_4, templateObject_5, templateObject_6, templateObject_7, templateObject_8, templateObject_9, templateObject_10, templateObject_11, templateObject_12, templateObject_13, templateObject_14, templateObject_15, templateObject_16, templateObject_17, templateObject_18, templateObject_19, templateObject_20, templateObject_21, templateObject_22, templateObject_23, templateObject_24; diff --git a/build/scripts/commands/member.js b/build/scripts/commands/member.js index 90115b4d..b432bb30 100644 --- a/build/scripts/commands/member.js +++ b/build/scripts/commands/member.js @@ -1,137 +1,137 @@ -"use strict"; -/* -Copyright © BalaM314, 2026. All Rights Reserved. -This file contains member commands, which are fun cosmetics for donators. -*/ -Object.defineProperty(exports, "__esModule", { value: true }); -exports.commands = void 0; -var commands_1 = require("/frameworks/commands"); -exports.commands = (0, commands_1.commandList)({ - pet: (0, commands_1.command)({ - args: ["name:string?"], - description: 'Spawns a cool pet with a displayed name that follows you around.', - perm: commands_1.Perm.member, - data: {}, - handler: function (_a) { - var _b, _c; - var args = _a.args, sender = _a.sender, data = _a.data, outputSuccess = _a.outputSuccess; - if (!args.name) { - var pet_1 = data[sender.uuid]; - if (pet_1) { - pet_1.kill(); - delete data[sender.uuid]; - outputSuccess("Your pet has been removed."); - return; - } - } - if (sender.muted || !args.name) - args.name = "".concat(sender.name, "[white]'s pet"); - if (args.name.length > 500) - (0, commands_1.fail)("Name cannot be more than 500 characters."); - if (Strings.stripColors(args.name).length > 70) - (0, commands_1.fail)("Name cannot be more than 70 characters, not including color tags."); - (_b = data[sender.uuid]) === null || _b === void 0 ? void 0 : _b.kill(); - var unit = (_c = sender.unit()) !== null && _c !== void 0 ? _c : (0, commands_1.fail)("You do not have a unit for the pet to follow."); - var pet = UnitTypes.merui.spawn(sender.team(), unit.x, unit.y); - pet.apply(StatusEffects.disarmed, Number.MAX_SAFE_INTEGER); - data[sender.uuid] = pet; - Call.infoPopup('[#7FD7FD7f]\uE81B', 5, Align.topRight, 180, 0, 0, 10); - outputSuccess("Spawned a pet."); - var petName = args.name; - var vec = new Vec2(0, 0); - (function controlUnit() { - try { - var unit_1 = sender.unit(); - var currentPet = data[sender.uuid]; - if (pet != currentPet) - return; - if (currentPet.dead) { - delete data[sender.uuid]; - return; - } - if (!sender.connected()) { - currentPet === null || currentPet === void 0 ? void 0 : currentPet.kill(); - return; - } - if (unit_1 && currentPet) { - var distX = unit_1.x - currentPet.x; - var distY = unit_1.y - currentPet.y; - vec.set(distX, distY); - if (vec.len() > 50) { - currentPet.approach(vec); - } - if (vec.len() > 20 * 8) { - currentPet.apply(StatusEffects.fast, 60); - } - Call.label(petName, 0.07, currentPet.x, currentPet.y + 5); - //Pets share the sender's trail - if (sender.trail) { - Call.effect(Fx[sender.trail.type], currentPet.x, currentPet.y, 0, sender.trail.color); - } - } - return Timer.schedule(controlUnit, 0.05); - } - catch (err) { - Log.err(err); - } - })(); - } - }), - highlight: { - args: ['color:string?'], - description: 'Makes your chat text colored by default.', - perm: commands_1.Perm.member, - handler: function (_a) { - var args = _a.args, sender = _a.sender, outputFail = _a.outputFail, outputSuccess = _a.outputSuccess; - if (args.color == null || args.color.length == 0) { - if (sender.highlight != null) { - sender.highlight = null; - outputSuccess("Cleared your highlight."); - } - else { - outputFail("No highlight to clear."); - } - } - else if (Strings.stripColors(args.color) == "") { - sender.highlight = args.color; - outputSuccess("Set highlight to ".concat(args.color.replace("[", "").replace("]", ""), ".")); - } - else if (Strings.stripColors("[".concat(args.color, "]")) == "") { - sender.highlight = "[".concat(args.color, "]"); - outputSuccess("Set highlight to ".concat(args.color, ".")); - } - else { - outputFail("[yellow]\"".concat(args.color, "[yellow]\" was not a valid color!")); - } - } - }, - rainbow: { - args: ["speed:number?"], - description: 'Make your name change colors.', - perm: commands_1.Perm.member, - requirements: [commands_1.Req.integerRange("speed", 0, 10)], - handler: function (_a) { - var _b; - var args = _a.args, sender = _a.sender, outputSuccess = _a.outputSuccess; - var colors = ['[red]', '[orange]', '[yellow]', '[acid]', '[blue]', '[purple]']; - function rainbowLoop(index, fishP) { - Timer.schedule(function () { - if (!(fishP.rainbow && fishP.player && fishP.connected())) - return; - fishP.player.name = colors[index % colors.length] + Strings.stripColors(fishP.player.name); - rainbowLoop(index + 1, fishP); - }, args.speed / 5); - } - if (!args.speed) { - sender.rainbow = null; - sender.updateName(); - outputSuccess("Turned off rainbow."); - } - else { - (_b = sender.rainbow) !== null && _b !== void 0 ? _b : (sender.rainbow = { speed: args.speed }); - rainbowLoop(0, sender); - outputSuccess("Activated rainbow name mode with speed ".concat(args.speed)); - } - } - } -}); +"use strict"; +/* +Copyright © BalaM314, 2026. All Rights Reserved. +This file contains member commands, which are fun cosmetics for donators. +*/ +Object.defineProperty(exports, "__esModule", { value: true }); +exports.commands = void 0; +var commands_1 = require("/frameworks/commands"); +exports.commands = (0, commands_1.commandList)({ + pet: (0, commands_1.command)({ + args: ["name:string?"], + description: 'Spawns a cool pet with a displayed name that follows you around.', + perm: commands_1.Perm.member, + data: {}, + handler: function (_a) { + var _b, _c; + var args = _a.args, sender = _a.sender, data = _a.data, outputSuccess = _a.outputSuccess; + if (!args.name) { + var pet_1 = data[sender.uuid]; + if (pet_1) { + pet_1.kill(); + delete data[sender.uuid]; + outputSuccess("Your pet has been removed."); + return; + } + } + if (sender.muted || !args.name) + args.name = "".concat(sender.name, "[white]'s pet"); + if (args.name.length > 500) + (0, commands_1.fail)("Name cannot be more than 500 characters."); + if (Strings.stripColors(args.name).length > 70) + (0, commands_1.fail)("Name cannot be more than 70 characters, not including color tags."); + (_b = data[sender.uuid]) === null || _b === void 0 ? void 0 : _b.kill(); + var unit = (_c = sender.unit()) !== null && _c !== void 0 ? _c : (0, commands_1.fail)("You do not have a unit for the pet to follow."); + var pet = UnitTypes.merui.spawn(sender.team(), unit.x, unit.y); + pet.apply(StatusEffects.disarmed, Number.MAX_SAFE_INTEGER); + data[sender.uuid] = pet; + Call.infoPopup('[#7FD7FD7f]\uE81B', 5, Align.topRight, 180, 0, 0, 10); + outputSuccess("Spawned a pet."); + var petName = args.name; + var vec = new Vec2(0, 0); + (function controlUnit() { + try { + var unit_1 = sender.unit(); + var currentPet = data[sender.uuid]; + if (pet != currentPet) + return; + if (currentPet.dead) { + delete data[sender.uuid]; + return; + } + if (!sender.connected()) { + currentPet === null || currentPet === void 0 ? void 0 : currentPet.kill(); + return; + } + if (unit_1 && currentPet) { + var distX = unit_1.x - currentPet.x; + var distY = unit_1.y - currentPet.y; + vec.set(distX, distY); + if (vec.len() > 50) { + currentPet.approach(vec); + } + if (vec.len() > 20 * 8) { + currentPet.apply(StatusEffects.fast, 60); + } + Call.label(petName, 0.07, currentPet.x, currentPet.y + 5); + //Pets share the sender's trail + if (sender.trail) { + Call.effect(Fx[sender.trail.type], currentPet.x, currentPet.y, 0, sender.trail.color); + } + } + return Timer.schedule(controlUnit, 0.05); + } + catch (err) { + Log.err(err); + } + })(); + } + }), + highlight: { + args: ['color:string?'], + description: 'Makes your chat text colored by default.', + perm: commands_1.Perm.member, + handler: function (_a) { + var args = _a.args, sender = _a.sender, outputFail = _a.outputFail, outputSuccess = _a.outputSuccess; + if (args.color == null || args.color.length == 0) { + if (sender.highlight != null) { + sender.highlight = null; + outputSuccess("Cleared your highlight."); + } + else { + outputFail("No highlight to clear."); + } + } + else if (Strings.stripColors(args.color) == "") { + sender.highlight = args.color; + outputSuccess("Set highlight to ".concat(args.color.replace("[", "").replace("]", ""), ".")); + } + else if (Strings.stripColors("[".concat(args.color, "]")) == "") { + sender.highlight = "[".concat(args.color, "]"); + outputSuccess("Set highlight to ".concat(args.color, ".")); + } + else { + outputFail("[yellow]\"".concat(args.color, "[yellow]\" was not a valid color!")); + } + } + }, + rainbow: { + args: ["speed:number?"], + description: 'Make your name change colors.', + perm: commands_1.Perm.member, + requirements: [commands_1.Req.integerRange("speed", 0, 10)], + handler: function (_a) { + var _b; + var args = _a.args, sender = _a.sender, outputSuccess = _a.outputSuccess; + var colors = ['[red]', '[orange]', '[yellow]', '[acid]', '[blue]', '[purple]']; + function rainbowLoop(index, fishP) { + Timer.schedule(function () { + if (!(fishP.rainbow && fishP.player && fishP.connected())) + return; + fishP.player.name = colors[index % colors.length] + Strings.stripColors(fishP.player.name); + rainbowLoop(index + 1, fishP); + }, args.speed / 5); + } + if (!args.speed) { + sender.rainbow = null; + sender.updateName(); + outputSuccess("Turned off rainbow."); + } + else { + (_b = sender.rainbow) !== null && _b !== void 0 ? _b : (sender.rainbow = { speed: args.speed }); + rainbowLoop(0, sender); + outputSuccess("Activated rainbow name mode with speed ".concat(args.speed)); + } + } + } +}); diff --git a/build/scripts/commands/staff.js b/build/scripts/commands/staff.js index f79df91a..8516fb06 100644 --- a/build/scripts/commands/staff.js +++ b/build/scripts/commands/staff.js @@ -1,1679 +1,1679 @@ -"use strict"; -/* -Copyright © BalaM314, 2026. All Rights Reserved. -This file contains the in-game chat commands that can be run by trusted staff. -*/ -var __makeTemplateObject = (this && this.__makeTemplateObject) || function (cooked, raw) { - if (Object.defineProperty) { Object.defineProperty(cooked, "raw", { value: raw }); } else { cooked.raw = raw; } - return cooked; -}; -var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { - if (k2 === undefined) k2 = k; - var desc = Object.getOwnPropertyDescriptor(m, k); - if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { - desc = { enumerable: true, get: function() { return m[k]; } }; - } - Object.defineProperty(o, k2, desc); -}) : (function(o, m, k, k2) { - if (k2 === undefined) k2 = k; - o[k2] = m[k]; -})); -var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { - Object.defineProperty(o, "default", { enumerable: true, value: v }); -}) : function(o, v) { - o["default"] = v; -}); -var __importStar = (this && this.__importStar) || (function () { - var ownKeys = function(o) { - ownKeys = Object.getOwnPropertyNames || function (o) { - var ar = []; - for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k; - return ar; - }; - return ownKeys(o); - }; - return function (mod) { - if (mod && mod.__esModule) return mod; - var result = {}; - if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]); - __setModuleDefault(result, mod); - return result; - }; -})(); -var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { - function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } - return new (P || (P = Promise))(function (resolve, reject) { - function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } - function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } - function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } - step((generator = generator.apply(thisArg, _arguments || [])).next()); - }); -}; -var __generator = (this && this.__generator) || function (thisArg, body) { - var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g = Object.create((typeof Iterator === "function" ? Iterator : Object).prototype); - return g.next = verb(0), g["throw"] = verb(1), g["return"] = verb(2), typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g; - function verb(n) { return function (v) { return step([n, v]); }; } - function step(op) { - if (f) throw new TypeError("Generator is already executing."); - while (g && (g = 0, op[0] && (_ = 0)), _) try { - if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t; - if (y = 0, t) op = [op[0] & 2, t.value]; - switch (op[0]) { - case 0: case 1: t = op; break; - case 4: _.label++; return { value: op[1], done: false }; - case 5: _.label++; y = op[1]; op = [0]; continue; - case 7: op = _.ops.pop(); _.trys.pop(); continue; - default: - if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; } - if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; } - if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; } - if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; } - if (t[2]) _.ops.pop(); - _.trys.pop(); continue; - } - op = body.call(thisArg, _); - } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; } - if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true }; - } -}; -var __rest = (this && this.__rest) || function (s, e) { - var t = {}; - for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0) - t[p] = s[p]; - if (s != null && typeof Object.getOwnPropertySymbols === "function") - for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) { - if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i])) - t[p[i]] = s[p[i]]; - } - return t; -}; -var __read = (this && this.__read) || function (o, n) { - var m = typeof Symbol === "function" && o[Symbol.iterator]; - if (!m) return o; - var i = m.call(o), r, ar = [], e; - try { - while ((n === void 0 || n-- > 0) && !(r = i.next()).done) ar.push(r.value); - } - catch (error) { e = { error: error }; } - finally { - try { - if (r && !r.done && (m = i["return"])) m.call(i); - } - finally { if (e) throw e.error; } - } - return ar; -}; -var __spreadArray = (this && this.__spreadArray) || function (to, from, pack) { - if (pack || arguments.length === 2) for (var i = 0, l = from.length, ar; i < l; i++) { - if (ar || !(i in from)) { - if (!ar) ar = Array.prototype.slice.call(from, 0, i); - ar[i] = from[i]; - } - } - return to.concat(ar || Array.prototype.slice.call(from)); -}; -var __values = (this && this.__values) || function(o) { - var s = typeof Symbol === "function" && Symbol.iterator, m = s && o[s], i = 0; - if (m) return m.call(o); - if (o && typeof o.length === "number") return { - next: function () { - if (o && i >= o.length) o = void 0; - return { value: o && o[i++], done: !o }; - } - }; - throw new TypeError(s ? "Object is not iterable." : "Symbol.iterator is not defined."); -}; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.commands = void 0; -var api = __importStar(require("/api")); -var config_1 = require("/config"); -var files_1 = require("/files"); -var fjsContext = __importStar(require("/fjsContext")); -var commands_1 = require("/frameworks/commands"); -var menus_1 = require("/frameworks/menus"); -var funcs_1 = require("/funcs"); -var globals_1 = require("/globals"); -var maps_1 = require("/maps"); -var players_1 = require("/players"); -var ranks_1 = require("/ranks"); -var utils_1 = require("/utils"); -exports.commands = (0, commands_1.commandList)({ - warn: { - args: ['player:player', 'message:string?'], - description: 'Sends the player a warning (menu popup).', - perm: commands_1.Perm.warn, - requirements: [commands_1.Req.cooldown(3000)], - handler: function (_a) { - var _b; - var args = _a.args, sender = _a.sender, outputSuccess = _a.outputSuccess, f = _a.f; - if (args.player.hasPerm("blockTrolling")) - (0, commands_1.fail)(f(templateObject_1 || (templateObject_1 = __makeTemplateObject(["Player ", " is insufficiently trollable."], ["Player ", " is insufficiently trollable."])), args.player)); - var message = (_b = args.message) !== null && _b !== void 0 ? _b : "You have been warned. I suggest you stop what you're doing"; - void menus_1.Menu.menu('Warning', message, ["[green]Accept"], args.player, { onCancel: 'null' }) - .then(function () { return outputSuccess('Player acknowledged the warning.'); }); - (0, utils_1.logAction)('warned', sender, args.player, message); - outputSuccess(f(templateObject_2 || (templateObject_2 = __makeTemplateObject(["Warned player ", " for \"", "\""], ["Warned player ", " for \"", "\""])), args.player, message)); - } - }, - mute: { - args: ['player:player'], - description: 'Stops a player from chatting.', - perm: commands_1.Perm.mod, - requirements: [commands_1.Req.moderate("player")], - handler: function (_a) { - return __awaiter(this, arguments, void 0, function (_b) { - var args = _b.args, sender = _b.sender, outputSuccess = _b.outputSuccess, f = _b.f; - return __generator(this, function (_c) { - switch (_c.label) { - case 0: - if (args.player.muted) - (0, commands_1.fail)(f(templateObject_3 || (templateObject_3 = __makeTemplateObject(["Player ", " is already muted."], ["Player ", " is already muted."])), args.player)); - return [4 /*yield*/, args.player.mute(sender)]; - case 1: - _c.sent(); - (0, utils_1.logAction)('muted', sender, args.player); - outputSuccess(f(templateObject_4 || (templateObject_4 = __makeTemplateObject(["Muted player ", "."], ["Muted player ", "."])), args.player)); - return [2 /*return*/]; - } - }); - }); - } - }, - unmute: { - args: ['player:player'], - description: 'Unmutes a player', - perm: commands_1.Perm.mod, - handler: function (_a) { - return __awaiter(this, arguments, void 0, function (_b) { - var args = _b.args, sender = _b.sender, outputSuccess = _b.outputSuccess, f = _b.f; - return __generator(this, function (_c) { - switch (_c.label) { - case 0: - if (!args.player.muted && args.player.autoflagged) - (0, commands_1.fail)(f(templateObject_5 || (templateObject_5 = __makeTemplateObject(["Player ", " is not muted, but they are autoflagged. You probably want to free them with /free."], ["Player ", " is not muted, but they are autoflagged. You probably want to free them with /free."])), args.player)); - if (!args.player.muted) - (0, commands_1.fail)(f(templateObject_6 || (templateObject_6 = __makeTemplateObject(["Player ", " is not muted."], ["Player ", " is not muted."])), args.player)); - return [4 /*yield*/, args.player.unmute(sender)]; - case 1: - _c.sent(); - (0, utils_1.logAction)('unmuted', sender, args.player); - outputSuccess(f(templateObject_7 || (templateObject_7 = __makeTemplateObject(["Unmuted player ", "."], ["Unmuted player ", "."])), args.player)); - return [2 /*return*/]; - } - }); - }); - } - }, - kick: { - args: ["player:player", "duration:time?", "reason:string?"], - description: 'Kick a player with optional reason.', - perm: commands_1.Perm.mod, - requirements: [commands_1.Req.moderate("player")], - handler: function (_a) { - var _b, _c, _d; - var args = _a.args, outputSuccess = _a.outputSuccess, f = _a.f, sender = _a.sender; - if (!sender.hasPerm("admin") && args.duration && args.duration > funcs_1.Duration.hours(6)) - (0, commands_1.fail)("Maximum kick duration is 6 hours."); - var reason = (_b = args.reason) !== null && _b !== void 0 ? _b : "A staff member did not like your actions."; - var duration = (_c = args.duration) !== null && _c !== void 0 ? _c : 60000; - args.player.kick(reason, duration); - (0, utils_1.logAction)("kicked", sender, args.player, (_d = args.reason) !== null && _d !== void 0 ? _d : undefined, duration); - if (duration > 60000) - args.player.setPunishedIP(config_1.stopAntiEvadeTime); - outputSuccess(f(templateObject_8 || (templateObject_8 = __makeTemplateObject(["Kicked player ", " for ", " with reason \"", "\""], ["Kicked player ", " for ", " with reason \"", "\""])), args.player, (0, utils_1.formatTime)(duration), reason)); - } - }, - pardon: { - args: ["player:offlinePlayer"], - description: 'Pardons a votekicked player.', - perm: commands_1.Perm.mod, - requirements: [commands_1.Req.moderate("player")], - handler: function (_a) { - var player = _a.args.player, admins = _a.admins, outputSuccess = _a.outputSuccess, f = _a.f; - var info = admins.getInfo(player.uuid); - if (Time.millis() > info.lastKicked && !admins.kickedIPs.containsKey(info.lastIP)) - (0, commands_1.fail)("That player is not kicked."); - info.lastKicked = 0; - admins.kickedIPs.remove(info.lastIP); - outputSuccess(f(templateObject_9 || (templateObject_9 = __makeTemplateObject(["Pardoned player ", "."], ["Pardoned player ", "."])), player)); - } - }, - stop: { - args: ['player:player', "time:time?", "message:string?"], - description: 'Stops a player.', - perm: commands_1.Perm.mod, - requirements: [commands_1.Req.moderate("player", true)], - handler: function (_a) { - return __awaiter(this, arguments, void 0, function (_b) { - var previousTime, time; - var _c, _d, _e, _f; - var args = _b.args, sender = _b.sender, outputSuccess = _b.outputSuccess, f = _b.f; - return __generator(this, function (_g) { - switch (_g.label) { - case 0: - if (!args.player.marked()) return [3 /*break*/, 2]; - //overload: overwrite stoptime - if (!args.time) - (0, commands_1.fail)(f(templateObject_10 || (templateObject_10 = __makeTemplateObject(["Player ", " is already marked."], ["Player ", " is already marked."])), args.player)); - previousTime = (0, utils_1.formatTimeRelative)(args.player.unmarkTime, true); - return [4 /*yield*/, args.player.updateStopTime(args.time)]; - case 1: - _g.sent(); - outputSuccess(f(templateObject_11 || (templateObject_11 = __makeTemplateObject(["Player ", "'s stop time has been updated to ", " (was ", ")."], ["Player ", "'s stop time has been updated to ", " (was ", ")."])), args.player, (0, utils_1.formatTime)(args.time), previousTime)); - (0, utils_1.logAction)("updated stop time of", sender, args.player, (_c = args.message) !== null && _c !== void 0 ? _c : undefined, args.time); - return [3 /*break*/, 4]; - case 2: - time = (_d = args.time) !== null && _d !== void 0 ? _d : (0, utils_1.untilForever)(); - if (time + Date.now() > globals_1.maxTime) - (0, commands_1.fail)("Error: time too high."); - return [4 /*yield*/, args.player.stop(sender, time, (_e = args.message) !== null && _e !== void 0 ? _e : undefined)]; - case 3: - _g.sent(); - (0, utils_1.logAction)('stopped', sender, args.player, (_f = args.message) !== null && _f !== void 0 ? _f : undefined, time); - //TODO outputGlobal() - Call.sendMessage("[orange]Player \"".concat(args.player.prefixedName, "[orange]\" has been marked for ").concat((0, utils_1.formatTime)(time)).concat(args.message ? " with reason: [white]".concat(args.message, "[]") : "", ".")); - _g.label = 4; - case 4: return [2 /*return*/]; - } - }); - }); - } - }, - free: { - args: ['player:player'], - description: 'Frees a player.', - perm: commands_1.Perm.mod, - handler: function (_a) { - return __awaiter(this, arguments, void 0, function (_b) { - var args = _b.args, sender = _b.sender, outputSuccess = _b.outputSuccess, outputFail = _b.outputFail, f = _b.f; - return __generator(this, function (_c) { - switch (_c.label) { - case 0: - if (!args.player.marked()) return [3 /*break*/, 2]; - return [4 /*yield*/, args.player.free(sender)]; - case 1: - _c.sent(); - (0, utils_1.logAction)('freed', sender, args.player); - outputSuccess(f(templateObject_12 || (templateObject_12 = __makeTemplateObject(["Player ", " has been unmarked."], ["Player ", " has been unmarked."])), args.player)); - return [3 /*break*/, 3]; - case 2: - if (args.player.autoflagged) { - args.player.autoflagged = false; - args.player.sendMessage("[yellow]You have been freed! Enjoy!"); - args.player.updateName(); - args.player.forceRespawn(); - outputSuccess(f(templateObject_13 || (templateObject_13 = __makeTemplateObject(["Player ", " has been unflagged."], ["Player ", " has been unflagged."])), args.player)); - } - else { - outputFail(f(templateObject_14 || (templateObject_14 = __makeTemplateObject(["Player ", " is not marked or autoflagged."], ["Player ", " is not marked or autoflagged."])), args.player)); - } - _c.label = 3; - case 3: return [2 /*return*/]; - } - }); - }); - } - }, - setrank: { - args: ["player:player", "rank:rank"], - description: "Set a player's rank.", - perm: commands_1.Perm.mod, - requirements: [commands_1.Req.moderate("player")], - handler: function (_a) { - return __awaiter(this, arguments, void 0, function (_b) { - var _c = _b.args, rank = _c.rank, player = _c.player, outputSuccess = _b.outputSuccess, f = _b.f, sender = _b.sender; - return __generator(this, function (_d) { - switch (_d.label) { - case 0: - if (rank.level >= sender.rank.level) - (0, commands_1.fail)(f(templateObject_15 || (templateObject_15 = __makeTemplateObject(["You do not have permission to promote players to rank ", ", because your current rank is ", ""], ["You do not have permission to promote players to rank ", ", because your current rank is ", ""])), rank, sender.rank)); - if (rank == ranks_1.Rank.pi && !config_1.Mode.localDebug) - (0, commands_1.fail)(f(templateObject_16 || (templateObject_16 = __makeTemplateObject(["Rank ", " is immutable."], ["Rank ", " is immutable."])), rank)); - if (player.immutable() && !config_1.Mode.localDebug) - (0, commands_1.fail)(f(templateObject_17 || (templateObject_17 = __makeTemplateObject(["Player ", " is immutable."], ["Player ", " is immutable."])), player)); - if (rank == player.rank) { - outputSuccess(f(templateObject_18 || (templateObject_18 = __makeTemplateObject(["Player ", " is already at rank ", "."], ["Player ", " is already at rank ", "."])), player, rank)); - return [2 /*return*/]; - } - if (!(player == sender && rank.level < sender.rank.level)) return [3 /*break*/, 2]; - return [4 /*yield*/, menus_1.Menu.confirmDangerous(sender, "[red] ARE YOU SURE YOU WANT TO SELF DEMOTE. THIS ACTION CANNOT BE UNDONE!")]; - case 1: - _d.sent(); - _d.label = 2; - case 2: return [4 /*yield*/, player.setRank(rank)]; - case 3: - _d.sent(); - (0, utils_1.logAction)("set rank to ".concat(rank.name, " for"), sender, player); - outputSuccess(f(templateObject_19 || (templateObject_19 = __makeTemplateObject(["Set rank of player ", " to ", ""], ["Set rank of player ", " to ", ""])), player, rank)); - if (player !== sender) - player.sendMessage("[royal]Your rank has been set to ".concat(rank.coloredName(), ".")); - return [2 /*return*/]; - } - }); - }); - } - }, - setflag: { - args: ["player:player", "flag:roleflag", "value:boolean"], - description: "Set a player's role flags.", - perm: commands_1.Perm.mod, - requirements: [commands_1.Req.moderate("player")], - handler: function (_a) { - return __awaiter(this, arguments, void 0, function (_b) { - var _c = _b.args, flag = _c.flag, player = _c.player, value = _c.value, sender = _b.sender, outputSuccess = _b.outputSuccess, f = _b.f; - return __generator(this, function (_d) { - switch (_d.label) { - case 0: - if (!sender.hasPerm("admin") && !flag.assignableByModerators) - (0, commands_1.fail)(f(templateObject_20 || (templateObject_20 = __makeTemplateObject(["You do not have permission to change the value of role flag ", ""], ["You do not have permission to change the value of role flag ", ""])), flag)); - return [4 /*yield*/, player.setFlag(flag, value)]; - case 1: - _d.sent(); - (0, utils_1.logAction)("set roleflag ".concat(flag.name, " to ").concat(value, " for"), sender, player); - outputSuccess(f(templateObject_21 || (templateObject_21 = __makeTemplateObject(["Set role flag ", " of player ", " to ", ""], ["Set role flag ", " of player ", " to ", ""])), flag, player, value)); - return [2 /*return*/]; - } - }); - }); - } - }, - murder: { - args: [], - description: 'Kills all ohno units', - perm: commands_1.Perm.mod, - customUnauthorizedMessage: "[yellow]You're a [scarlet]monster[].", - handler: function (_a) { - var output = _a.output, f = _a.f, allCommands = _a.allCommands; - var Ohnos = allCommands["ohno"].data; //this is not ideal... TODO commit omega shenanigans - var numOhnos = Ohnos.amount(); - Ohnos.killAll(); - output(f(templateObject_22 || (templateObject_22 = __makeTemplateObject(["[orange]You massacred ", " helpless ohno crawlers."], ["[orange]You massacred ", " helpless ohno crawlers."])), numOhnos)); - } - }, - stop_offline: { - args: ["time:time?", "name:string?"], - description: "Stops an offline player.", - perm: commands_1.Perm.mod, - handler: function (_a) { - return __awaiter(this, arguments, void 0, function (_b) { - function stop(option, time) { - return __awaiter(this, void 0, void 0, function () { - var fishP; - return __generator(this, function (_a) { - switch (_a.label) { - case 0: - fishP = players_1.FishPlayer.getFromInfo(option); - if (!sender.canModerate(fishP, true)) return [3 /*break*/, 2]; - (0, utils_1.logAction)(fishP.marked() ? time == 1000 ? "freed" : "updated stop time of" : "stopped", sender, option, undefined, time); - return [4 /*yield*/, fishP.stop(sender, time)]; - case 1: - _a.sent(); - outputSuccess(f(templateObject_23 || (templateObject_23 = __makeTemplateObject(["Player ", " was marked for ", "."], ["Player ", " was marked for ", "."])), option, (0, utils_1.formatTime)(time))); - return [3 /*break*/, 3]; - case 2: - outputFail("You do not have permission to stop this player."); - _a.label = 3; - case 3: return [2 /*return*/]; - } - }); - }); - } - var maxPlayers, info, possiblePlayers, exactPlayers, score_1, optionPlayer, _c, _d, _e; - var _f, _g; - var args = _b.args, sender = _b.sender, outputFail = _b.outputFail, outputSuccess = _b.outputSuccess, f = _b.f, admins = _b.admins; - return __generator(this, function (_h) { - switch (_h.label) { - case 0: - maxPlayers = 60; - if (!(args.name && globals_1.uuidPattern.test(args.name))) return [3 /*break*/, 4]; - info = admins.getInfoOptional(args.name); - if (!(info != null)) return [3 /*break*/, 2]; - return [4 /*yield*/, stop(info, (_f = args.time) !== null && _f !== void 0 ? _f : (0, utils_1.untilForever)())]; - case 1: - _h.sent(); - return [3 /*break*/, 3]; - case 2: - outputFail(f(templateObject_24 || (templateObject_24 = __makeTemplateObject(["Unknown UUID ", ""], ["Unknown UUID ", ""])), args.name)); - _h.label = 3; - case 3: return [2 /*return*/]; - case 4: - if (args.name) { - possiblePlayers = (0, funcs_1.setToArray)(admins.searchNames(args.name)); - if (possiblePlayers.length > maxPlayers) { - exactPlayers = (0, funcs_1.setToArray)(admins.findByName(args.name)); - if (exactPlayers.length > 0) { - possiblePlayers = exactPlayers; - } - else { - (0, commands_1.fail)("Too many players with that name."); - } - } - else if (possiblePlayers.length == 0) { - (0, commands_1.fail)("No players with that name were found."); - } - score_1 = function (data) { - var fishP = players_1.FishPlayer.getById(data.id); - if (fishP) - return fishP.lastJoined; - return -data.timesJoined; - }; - possiblePlayers.sort(function (a, b) { return score_1(b) - score_1(a); }); - } - else { - possiblePlayers = players_1.FishPlayer.recentLeaves.map(function (p) { return p.info(); }); - } - return [4 /*yield*/, menus_1.Menu.menu("Stop", "Choose a player to mark", possiblePlayers, sender, { - includeCancel: true, - optionStringifier: function (p) { return p.lastName; } - })]; - case 5: - optionPlayer = _h.sent(); - if (!((_g = args.time) !== null && _g !== void 0)) return [3 /*break*/, 6]; - _c = _g; - return [3 /*break*/, 8]; - case 6: - _d = args; - _e = utils_1.match; - return [4 /*yield*/, menus_1.Menu.menu("Stop", "Select stop time", ["2 days", "7 days", "30 days", "forever"], sender)]; - case 7: - _c = (_d.time = _e.apply(void 0, [_h.sent(), { - "2 days": funcs_1.Duration.days(2), - "7 days": funcs_1.Duration.days(7), - "30 days": funcs_1.Duration.days(30), - "forever": globals_1.maxTime - Date.now() - 10000, - }])); - _h.label = 8; - case 8: - _c; - return [4 /*yield*/, stop(optionPlayer, args.time)]; - case 9: - _h.sent(); - return [2 /*return*/]; - } - }); - }); - } - }, - mute_offline: { - args: ["name:string?"], - description: "Mutes an offline player.", - perm: commands_1.Perm.mod, - handler: function (_a) { - return __awaiter(this, arguments, void 0, function (_b) { - function mute(option) { - return __awaiter(this, void 0, void 0, function () { - var fishP; - return __generator(this, function (_a) { - switch (_a.label) { - case 0: - fishP = players_1.FishPlayer.getFromInfo(option); - if (!sender.canModerate(fishP, true)) - (0, commands_1.fail)("You do not have permission to mute this player."); - return [4 /*yield*/, menus_1.Menu.confirm(sender, "Are you sure you want to ".concat(fishP.muted ? "unmute" : "mute", " player ").concat(option.lastName, "?"), { - title: "Mute Offine Confirmation", - confirmText: "[green]Yes, ".concat(fishP.muted ? "unmute" : "mute", " them"), - })]; - case 1: - _a.sent(); - (0, utils_1.logAction)(fishP.muted ? "unmuted" : "muted", sender, fishP); - if (!fishP.muted) return [3 /*break*/, 3]; - return [4 /*yield*/, fishP.unmute(sender)]; - case 2: - _a.sent(); - return [3 /*break*/, 5]; - case 3: return [4 /*yield*/, fishP.mute(sender)]; - case 4: - _a.sent(); - _a.label = 5; - case 5: - outputSuccess("".concat(fishP.muted ? "Muted" : "Unmuted", " ").concat(option.lastName, ".")); - return [2 /*return*/]; - } - }); - }); - } - var maxPlayers, info, possiblePlayers, exactPlayers, score_2, option; - var _c; - var args = _b.args, sender = _b.sender, outputSuccess = _b.outputSuccess, f = _b.f, admins = _b.admins; - return __generator(this, function (_d) { - switch (_d.label) { - case 0: - maxPlayers = 300; - if (!(args.name && globals_1.uuidPattern.test(args.name))) return [3 /*break*/, 2]; - info = (_c = admins.getInfoOptional(args.name)) !== null && _c !== void 0 ? _c : (0, commands_1.fail)(f(templateObject_25 || (templateObject_25 = __makeTemplateObject(["Unknown UUID ", ""], ["Unknown UUID ", ""])), args.name)); - return [4 /*yield*/, mute(info)]; - case 1: - _d.sent(); - return [2 /*return*/]; - case 2: - if (args.name) { - possiblePlayers = (0, funcs_1.setToArray)(admins.searchNames(args.name)); - if (possiblePlayers.length > maxPlayers) { - exactPlayers = (0, funcs_1.setToArray)(admins.findByName(args.name)); - if (exactPlayers.length > 0) { - possiblePlayers = exactPlayers; - } - else { - (0, commands_1.fail)("Too many players with that name."); - } - } - else if (possiblePlayers.length == 0) { - (0, commands_1.fail)("No players with that name were found."); - } - score_2 = function (data) { - var fishP = players_1.FishPlayer.getById(data.id); - if (fishP) - return fishP.lastJoined; - return -data.timesJoined; - }; - possiblePlayers.sort(function (a, b) { return score_2(b) - score_2(a); }); - } - else { - possiblePlayers = players_1.FishPlayer.recentLeaves.map(function (p) { return p.info(); }); - } - return [4 /*yield*/, menus_1.Menu.pagedList(sender, "Mute", "Choose a player to mute", possiblePlayers, { - optionStringifier: function (p) { return p.lastName; } - })]; - case 3: - option = _d.sent(); - return [4 /*yield*/, mute(option)]; - case 4: - _d.sent(); - return [2 /*return*/]; - } - }); - }); - } - }, - restart: { - args: ["time:number?"], - perm: commands_1.Perm.admin, - description: "Restarts the server.", - handler: function (_a) { - var _b; - var time = _a.args.time; - (_b = globals_1.fishState.restartLoopTask) === null || _b === void 0 ? void 0 : _b.cancel(); - if (Groups.player.isEmpty()) { - if (time == undefined) { - Log.info("Restarting immediately as no players are online."); - time !== null && time !== void 0 ? time : (time = 0); - } - } - else if (config_1.Gamemode.pvp()) { - time !== null && time !== void 0 ? time : (time = -1); - Log.info("PVP: restart will occur at the end of the current match. Specify a time to override, but &rthat would interrupt the current pvp match, and players would lose their teams.&fr"); - } - else { - time !== null && time !== void 0 ? time : (time = 60); - } - if (time == -1) { - Call.sendMessage("[accent]---[[[coral]+++[]]---\n[accent]Server restart queued. The server will restart after the current match is over.[]\n[accent]---[[[coral]+++[]]---"); - globals_1.fishState.restartQueued = true; - } - else { - if (time < 0 || time > 100) - (0, commands_1.fail)("Invalid time: out of valid range."); - (0, utils_1.serverRestartLoop)(time); - if (config_1.Gamemode.pvp()) { - Call.sendMessage("[accent]---[[[coral]+++[]]---\n[accent]Server restart imminent. [green]We'll be back after 15 seconds.[]\n[accent]---[[[coral]+++[]]---"); - } - else { - Call.sendMessage("[accent]---[[[coral]+++[]]---\n[accent]Server restart imminent. [green]We'll be back with 20 seconds of downtime, and all progress will be saved.[]\n[accent]---[[[coral]+++[]]---"); - } - } - } - }, - history: { - args: ["player:player"], - description: "Shows moderation history for a player.", - perm: commands_1.Perm.mod, - handler: function (_a) { - var args = _a.args, output = _a.output, f = _a.f; - if (args.player.history && args.player.history.length > 0) { - output("[yellow]_______________Player history_______________\n\n" + - (args.player).history.sort(function (a, b) { return a.time - b.time; }).map(function (e) { - return "".concat(e.by, " [yellow]").concat(e.action, " ").concat(args.player.prefixedName, " [white]").concat((0, utils_1.formatTimeRelative)(e.time)); - }).join("\n")); - } - else { - output(f(templateObject_26 || (templateObject_26 = __makeTemplateObject(["[yellow]No history was found for player ", "."], ["[yellow]No history was found for player ", "."])), args.player)); - } - } - }, - save: { - args: [], - description: "Saves the game state.", - perm: commands_1.Perm.mod, - handler: function (_a) { - var outputSuccess = _a.outputSuccess; - players_1.FishPlayer.saveAll(); - players_1.FishPlayer.uploadAll(); - globals_1.FishEvents.fire("saveData", []); - var file = Vars.saveDirectory.child("1.".concat(Vars.saveExtension)); - SaveIO.save(file); - outputSuccess("Game saved."); - } - }, - wave: { - args: ["wave:number"], - description: "Sets the wave number.", - perm: commands_1.Perm.admin, - requirements: [commands_1.Req.positiveInteger("wave")], - handler: function (_a) { - var args = _a.args, outputSuccess = _a.outputSuccess, f = _a.f; - Vars.state.wave = args.wave; - outputSuccess(f(templateObject_27 || (templateObject_27 = __makeTemplateObject(["Set wave to ", ""], ["Set wave to ", ""])), Vars.state.wave)); - } - }, - label: { - args: ["time:time", "message:string"], - description: "Places a label at your position for a specified amount of time.", - perm: commands_1.Perm.mod, - handler: function (_a) { - var _b; - var args = _a.args, sender = _a.sender, outputSuccess = _a.outputSuccess, f = _a.f; - if (args.time > funcs_1.Duration.hours(10)) - (0, commands_1.fail)("Time must be less than 10 hours."); - var unit = (_b = sender.unit()) !== null && _b !== void 0 ? _b : (0, commands_1.fail)("You must be in a unit to use this command."); - var timeRemaining = args.time / 1000; - var labelx = unit.x; - var labely = unit.y; - var task = Timer.schedule(function () { - if (timeRemaining > 0) { - var timeseconds = timeRemaining % 60; - var timeminutes = (timeRemaining - timeseconds) / 60; - Call.label("".concat(sender.name, "\n\n[white]").concat(args.message, "\n\n[acid]").concat(timeminutes.toString().padStart(2, "0"), ":").concat(timeseconds.toString().padStart(2, "0")), 1, labelx, labely); - timeRemaining--; - } - }, 0, 1, args.time); - globals_1.fishState.labels.push({ x: labelx, y: labely, task: task }); - outputSuccess(f(templateObject_28 || (templateObject_28 = __makeTemplateObject(["Placed label \"", "\" for ", " seconds."], ["Placed label \"", "\" for ", " seconds."])), args.message, timeRemaining)); - } - }, - labelsticky: { - args: ["time:time", "message:string"], - description: "Places a label at the bottom left corner of everyone's screen.", - perm: commands_1.Perm.admin, - handler: function (_a) { - var args = _a.args, outputSuccess = _a.outputSuccess, f = _a.f; - if (args.time > funcs_1.Duration.hours(10)) - (0, commands_1.fail)("Time must be less than 10 hours."); - var timeRemaining = args.time / 1000; - var task = Timer.schedule(function () { - if (timeRemaining > 0) { - Call.label(args.message, 5, NaN, NaN); - timeRemaining -= 5; - } - }, 0, 5, Math.ceil(args.time / 5)); - globals_1.fishState.labels.push({ task: task, x: null, y: null }); - outputSuccess(f(templateObject_29 || (templateObject_29 = __makeTemplateObject(["Placed label \"", "\" for ", " seconds."], ["Placed label \"", "\" for ", " seconds."])), args.message, timeRemaining)); - } - }, - clearlabels: { - args: [], - description: "Removes all labels.", - perm: commands_1.Perm.mod, - handler: function (_a) { - var outputSuccess = _a.outputSuccess; - if (globals_1.fishState.labels.length == 0) - (0, commands_1.fail)("No labels found."); - globals_1.fishState.labels.forEach(function (l) { return l.task.cancel(); }); - outputSuccess("Removed all labels."); - } - }, - clearlabel: { - args: ["sticky:boolean?"], - description: "Removes the closest label, or sticky label if specified", - perm: commands_1.Perm.mod, - handler: function (_a) { - var _b; - var _c = _a.args.sticky, sticky = _c === void 0 ? false : _c, sender = _a.sender, outputSuccess = _a.outputSuccess; - if (globals_1.fishState.labels.length == 0) - (0, commands_1.fail)("No labels found."); - var label; - if (sticky) { - var index = globals_1.fishState.labels.findIndex(function (l) { return l.x == null; }); - if (index == -1) - (0, commands_1.fail)("No sticky label found."); - label = globals_1.fishState.labels.splice(index, 1)[0]; - } - else { - var unit_1 = (_b = sender.unit()) !== null && _b !== void 0 ? _b : (0, commands_1.fail)("Cannot remove the closest label because you are dead."); - var dist_1 = function (label) { - if (label.x == null || label.y == null) - return Infinity; - return Mathf.dst(label.x, label.y, unit_1.x, unit_1.y); - }; - var index = __spreadArray([], __read(globals_1.fishState.labels.entries()), false).reduce(function (a, b) { return dist_1(a[1]) < dist_1(b[1]) ? a : b; })[0]; - label = globals_1.fishState.labels.splice(index, 1)[0]; - } - label.task.cancel(); - outputSuccess("Removed one label."); - } - }, - member: { - args: ["value:boolean", "player:player"], - description: "Sets a player's member status.", - perm: commands_1.Perm.admin, - handler: function (_a) { - return __awaiter(this, arguments, void 0, function (_b) { - var args = _b.args, outputSuccess = _b.outputSuccess, f = _b.f; - return __generator(this, function (_c) { - switch (_c.label) { - case 0: return [4 /*yield*/, args.player.setFlag("member", args.value)]; - case 1: - _c.sent(); - outputSuccess(f(templateObject_30 || (templateObject_30 = __makeTemplateObject(["Set membership status of player ", " to ", "."], ["Set membership status of player ", " to ", "."])), args.player, args.value)); - return [2 /*return*/]; - } - }); - }); - } - }, - remind: { - args: ["rule:number", "target:player?"], - description: "Remind players in chat of a specific rule.", - perm: commands_1.Perm.mod, - handler: function (_a) { - var _b; - var args = _a.args, outputSuccess = _a.outputSuccess, f = _a.f; - var rule = (_b = config_1.rules[args.rule - 1]) !== null && _b !== void 0 ? _b : (0, commands_1.fail)("The rule you requested does not exist."); - if (args.target) { - args.target.sendMessage("A staff member wants to remind you of the following rule:\n" + rule); - outputSuccess(f(templateObject_31 || (templateObject_31 = __makeTemplateObject(["Reminded ", " of rule ", ""], ["Reminded ", " of rule ", ""])), args.target, args.rule)); - } - else { - Call.sendMessage("A staff member wants to remind everyone of the following rule:\n" + rule); - } - }, - }, - ban: { - args: ["uuid_or_ip:string?"], - description: "Bans a player by UUID and IP.", - perm: commands_1.Perm.admin, - handler: function (_a) { - return __awaiter(this, arguments, void 0, function (_b) { - var uuid, data, name, ip, ip, info, alreadyBanned, option; - var args = _b.args, sender = _b.sender, outputSuccess = _b.outputSuccess, f = _b.f, admins = _b.admins; - return __generator(this, function (_c) { - switch (_c.label) { - case 0: - if (!(args.uuid_or_ip && globals_1.uuidPattern.test(args.uuid_or_ip))) return [3 /*break*/, 2]; - uuid = args.uuid_or_ip; - data = void 0; - if ((data = admins.getInfoOptional(uuid)) != null && data.admin) - (0, commands_1.fail)("Cannot ban an admin."); - name = data ? "".concat((0, funcs_1.escapeStringColorsClient)(data.lastName), " (").concat(uuid, "/").concat(data.lastIP, ")") : uuid; - return [4 /*yield*/, menus_1.Menu.confirmDangerous(sender, "Are you sure you want to ban ".concat(name, "?"))]; - case 1: - _c.sent(); - admins.banPlayerID(uuid); - if (data) { - ip = data.lastIP; - admins.banPlayerIP(ip); - api.ban({ ip: ip, uuid: uuid }); - Log.info("".concat(uuid, "/").concat(ip, " was banned.")); - (0, utils_1.logAction)("banned", sender, data); - outputSuccess(f(templateObject_32 || (templateObject_32 = __makeTemplateObject(["Banned player ", " (", "/", ")"], ["Banned player ", " (", "/", ")"])), (0, funcs_1.escapeStringColorsClient)(data.lastName), uuid, ip)); - //TODO add way to specify whether to activate or escape color tags - } - else { - api.ban({ uuid: uuid }); - Log.info("".concat(uuid, " was banned.")); - (0, utils_1.logAction)("banned", sender, uuid); - outputSuccess(f(templateObject_33 || (templateObject_33 = __makeTemplateObject(["Banned player ", ". [yellow]Unable to determine IP.[]"], ["Banned player ", ". [yellow]Unable to determine IP.[]"])), uuid)); - } - (0, utils_1.updateBans)(function (player) { return "[scarlet]Player [yellow]".concat(player.name, "[scarlet] has been whacked by ").concat(sender.prefixedName, "."); }); - return [2 /*return*/]; - case 2: - if (!(args.uuid_or_ip && globals_1.ipPattern.test(args.uuid_or_ip))) return [3 /*break*/, 4]; - ip = args.uuid_or_ip; - return [4 /*yield*/, menus_1.Menu.confirmDangerous(sender, "Are you sure you want to ban IP ".concat(ip, "?"))]; - case 3: - _c.sent(); - api.ban({ ip: ip }); - info = admins.findByIP(ip); - if (info) - (0, utils_1.logAction)("banned", sender, info); - else - (0, utils_1.logAction)("banned ".concat(ip), sender); - alreadyBanned = admins.banPlayerIP(ip); - if (alreadyBanned) { - outputSuccess(f(templateObject_34 || (templateObject_34 = __makeTemplateObject(["IP ", " is already banned. Ban was synced to other servers."], ["IP ", " is already banned. Ban was synced to other servers."])), ip)); - } - else { - outputSuccess(f(templateObject_35 || (templateObject_35 = __makeTemplateObject(["IP ", " has been banned. Ban was synced to other servers."], ["IP ", " has been banned. Ban was synced to other servers."])), ip)); - } - (0, utils_1.updateBans)(function (player) { return "[scarlet]Player [yellow]".concat(player.name, "[scarlet] has been whacked by ").concat(sender.prefixedName, "."); }); - return [2 /*return*/]; - case 4: return [4 /*yield*/, menus_1.Menu.menu("[scarlet]BAN[]", "Choose a player to ban.", (0, funcs_1.setToArray)(Groups.player), sender, { - includeCancel: true, - optionStringifier: function (opt) { return opt.name; } - })]; - case 5: - option = _c.sent(); - if (option.admin) - (0, commands_1.fail)("Cannot ban an admin."); - return [4 /*yield*/, menus_1.Menu.confirmDangerous(sender, "Are you sure you want to ban ".concat(option.name, "?"))]; - case 6: - _c.sent(); - admins.banPlayerIP(option.ip()); //this also bans the UUID - api.ban({ ip: option.ip(), uuid: option.uuid() }); - Log.info("".concat(option.ip(), "/").concat(option.uuid(), " was banned.")); - (0, utils_1.logAction)("banned", sender, option.getInfo()); - outputSuccess(f(templateObject_36 || (templateObject_36 = __makeTemplateObject(["Banned player ", "."], ["Banned player ", "."])), option)); - (0, utils_1.updateBans)(function (player) { return "[scarlet]Player [yellow]".concat(player.name, "[scarlet] has been whacked by ").concat(sender.prefixedName, "."); }); - return [2 /*return*/]; - } - }); - }); - } - }, - kill: { - args: ["player:player"], - description: "Kills a player's unit.", - perm: commands_1.Perm.admin, - requirements: [commands_1.Req.moderate("player", true)], - handler: function (_a) { - var args = _a.args, outputFail = _a.outputFail, outputSuccess = _a.outputSuccess, f = _a.f; - var unit = args.player.unit(); - if (unit) { - unit.kill(); - outputSuccess(f(templateObject_37 || (templateObject_37 = __makeTemplateObject(["Killed the unit of player ", "."], ["Killed the unit of player ", "."])), args.player)); - } - else { - outputFail(f(templateObject_38 || (templateObject_38 = __makeTemplateObject(["Player ", " does not have a unit."], ["Player ", " does not have a unit."])), args.player)); - } - } - }, - killunits: { - args: ["team:team?", "unit:unittype?"], - description: "Kills all units, optionally specifying a team and unit type.", - perm: commands_1.Perm.massKill, - handler: function (_a) { - return __awaiter(this, arguments, void 0, function (_b) { - var i_1, before, i_2, before; - var _c = _b.args, team = _c.team, unit = _c.unit, sender = _b.sender, outputSuccess = _b.outputSuccess, f = _b.f; - return __generator(this, function (_d) { - switch (_d.label) { - case 0: - if (!team) return [3 /*break*/, 2]; - return [4 /*yield*/, menus_1.Menu.confirmDangerous(sender, "This will kill [scarlet]every ".concat(unit ? unit.localizedName : "unit", "[] on the team ").concat(team.coloredName(), "."), { confirmText: "[orange]Kill units[]" })]; - case 1: - _d.sent(); - if (unit) { - i_1 = 0; - team.data().units.each(function (u) { return u.type == unit; }, function (u) { - u.kill(); - i_1++; - }); - outputSuccess(f(templateObject_39 || (templateObject_39 = __makeTemplateObject(["Killed ", " units on ", "."], ["Killed ", " units on ", "."])), i_1, team)); - } - else { - before = team.data().units.size; - team.data().units.each(function (u) { return u.kill(); }); - outputSuccess(f(templateObject_40 || (templateObject_40 = __makeTemplateObject(["Killed ", " units on ", "."], ["Killed ", " units on ", "."])), before, team)); - } - return [3 /*break*/, 4]; - case 2: return [4 /*yield*/, menus_1.Menu.confirmDangerous(sender, "This will kill [scarlet]every single ".concat(unit ? unit.localizedName : "unit", "[]."), { confirmText: "[orange]Kill all units[]" })]; - case 3: - _d.sent(); - if (unit) { - i_2 = 0; - Groups.unit.each(function (u) { return u.type == unit; }, function (u) { - u.kill(); - i_2++; - }); - outputSuccess(f(templateObject_41 || (templateObject_41 = __makeTemplateObject(["Killed ", " units."], ["Killed ", " units."])), i_2)); - } - else { - before = Groups.unit.size(); - Groups.unit.each(function (u) { return u.kill(); }); - outputSuccess(f(templateObject_42 || (templateObject_42 = __makeTemplateObject(["Killed ", " units."], ["Killed ", " units."])), before)); - } - _d.label = 4; - case 4: return [2 /*return*/]; - } - }); - }); - } - }, - killbuildings: { - args: ["team:team?"], - description: "Kills all buildings (except cores), optionally specifying a team.", - perm: commands_1.Perm.massKill, - handler: function (_a) { - return __awaiter(this, arguments, void 0, function (_b) { - var count, count; - var team = _b.args.team, sender = _b.sender, outputSuccess = _b.outputSuccess, f = _b.f; - return __generator(this, function (_c) { - switch (_c.label) { - case 0: - if (!team) return [3 /*break*/, 2]; - return [4 /*yield*/, menus_1.Menu.confirmDangerous(sender, "This will kill [scarlet]every building[] on the team ".concat(team.coloredName(), ", except cores."), { confirmText: "[orange]Kill buildings[]" })]; - case 1: - _c.sent(); - count = team.data().buildings.size; - team.data().buildings.each(function (b) { return !(b.block instanceof CoreBlock); }, function (b) { return b.tile.remove(); }); - outputSuccess(f(templateObject_43 || (templateObject_43 = __makeTemplateObject(["Killed ", " buildings on ", "."], ["Killed ", " buildings on ", "."])), count, team)); - return [3 /*break*/, 4]; - case 2: return [4 /*yield*/, menus_1.Menu.confirmDangerous(sender, "This will kill [scarlet]every building[] except cores.", { confirmText: "[orange]Kill buildings[]" })]; - case 3: - _c.sent(); - count = Groups.build.size(); - Groups.build.each(function (b) { return !(b.block instanceof CoreBlock); }, function (b) { return b.tile.remove(); }); - outputSuccess(f(templateObject_44 || (templateObject_44 = __makeTemplateObject(["Killed ", " buildings."], ["Killed ", " buildings."])), count)); - _c.label = 4; - case 4: return [2 /*return*/]; - } - }); - }); - } - }, - respawn: { - args: ["player:player"], - description: "Forces a player to respawn.", - perm: commands_1.Perm.mod, - requirements: [commands_1.Req.moderate("player", true, "mod", true)], - handler: function (_a) { - var args = _a.args, outputSuccess = _a.outputSuccess, f = _a.f; - args.player.forceRespawn(); - outputSuccess(f(templateObject_45 || (templateObject_45 = __makeTemplateObject(["Respawned player ", "."], ["Respawned player ", "."])), args.player)); - } - }, - clearunit: { - args: ["target:player", "duration:time?"], - description: "Forces a player out of the unit they are controlling, and blocks them from possessing units for a specified duration.", - perm: commands_1.Perm.mod, - requirements: [commands_1.Req.moderate("target", false, "mod", false)], - handler: function (_a) { - var _b = _a.args, target = _b.target, duration = _b.duration, sender = _a.sender, outputSuccess = _a.outputSuccess, f = _a.f; - if (Date.now() > 1000 + target.blockedFromPossessingUnitsUntil) - duration !== null && duration !== void 0 ? duration : (duration = funcs_1.Duration.minutes(1)); - else - duration !== null && duration !== void 0 ? duration : (duration = 0); - if (duration == 0) { - target.blockedFromPossessingUnitsUntil = 0; - target.sendMessage("You are allowed to control units again."); - outputSuccess(f(templateObject_46 || (templateObject_46 = __makeTemplateObject(["Restored ", "'s ability to control units."], ["Restored ", "'s ability to control units."])), target)); - (0, utils_1.logAction)("restored unit possession for", sender, target); - } - else { - target.forceRespawn(); - target.blockedFromPossessingUnitsUntil = Date.now() + duration; - target.sendMessage("You have been blocked from controlling units for ".concat((0, utils_1.formatTime)(duration), ".")); - outputSuccess(f(templateObject_47 || (templateObject_47 = __makeTemplateObject(["Blocked ", " from controlling units for ", "."], ["Blocked ", " from controlling units for ", "."])), target, (0, utils_1.formatTime)(duration))); - (0, utils_1.logAction)("revoked unit possession for", sender, target, undefined, duration); - } - } - }, - clearcommand: { - args: ["target:player", "duration:time?"], - description: "Blocks a player from commanding units for a specified duration.", - perm: commands_1.Perm.mod, - requirements: [commands_1.Req.moderate("target", false, "mod", false)], - handler: function (_a) { - var _b = _a.args, target = _b.target, duration = _b.duration, sender = _a.sender, outputSuccess = _a.outputSuccess, f = _a.f; - if (Date.now() > 1000 + target.blockedFromCommandingUnitsUntil) - duration !== null && duration !== void 0 ? duration : (duration = funcs_1.Duration.minutes(1)); - else - duration !== null && duration !== void 0 ? duration : (duration = 0); - if (duration == 0) { - target.blockedFromCommandingUnitsUntil = 0; - target.sendMessage("You are allowed to command units again."); - outputSuccess(f(templateObject_48 || (templateObject_48 = __makeTemplateObject(["Restored ", "'s ability to command units."], ["Restored ", "'s ability to command units."])), target)); - (0, utils_1.logAction)("restored command mode for", sender, target, undefined, duration); - } - else { - target.blockedFromCommandingUnitsUntil = Date.now() + duration; - target.sendMessage("You have been blocked from commanding units for ".concat((0, utils_1.formatTime)(duration), ".")); - outputSuccess(f(templateObject_49 || (templateObject_49 = __makeTemplateObject(["Blocked ", " from commanding units for ", "."], ["Blocked ", " from commanding units for ", "."])), target, (0, utils_1.formatTime)(duration))); - (0, utils_1.logAction)("revoked command mode for", sender, target, undefined, duration); - } - } - }, - stealunit: { - args: ["target:player", "newcontroller:player?"], - description: "Steals the unit of a player, putting you in their unit and forcing them to respawn.", - perm: commands_1.Perm.mod, - requirements: [commands_1.Req.moderate("target", true, "mod", true), commands_1.Req.moderate("newcontroller", true, "mod", true)], - handler: function (_a) { - var _b; - var sender = _a.sender, _c = _a.args, target = _c.target, _d = _c.newcontroller, newcontroller = _d === void 0 ? sender : _d, outputSuccess = _a.outputSuccess, f = _a.f; - var unit = (_b = target.unit()) !== null && _b !== void 0 ? _b : (0, commands_1.fail)(f(templateObject_50 || (templateObject_50 = __makeTemplateObject(["Targeted player ", " is not in a unit."], ["Targeted player ", " is not in a unit."])), target)); - if (target.team() !== newcontroller.team()) { - if (!sender.hasPerm("changeTeamExternal")) { - if (!sender.hasPerm("changeTeam")) - (0, commands_1.fail)("You do not have permission to change teams."); - newcontroller.setTeam(unit.team); - } - } - target.forceRespawn(); - newcontroller.unit(unit); - if (newcontroller == sender) { - outputSuccess(f(templateObject_51 || (templateObject_51 = __makeTemplateObject(["Commandeered the unit of player ", "."], ["Commandeered the unit of player ", "."])), target)); - } - else { - outputSuccess(f(templateObject_52 || (templateObject_52 = __makeTemplateObject(["Transferred player ", " into the unit of ", "."], ["Transferred player ", " into the unit of ", "."])), newcontroller, target)); - newcontroller.sendMessage(f(templateObject_53 || (templateObject_53 = __makeTemplateObject(["[green]You were transferred to the unit of player ", " by ", "."], ["[green]You were transferred to the unit of player ", " by ", "."])), target, sender)('[green]')); - } - } - }, - m: { - args: ["message:string"], - description: "Sends a message to muted players only.", - perm: commands_1.Perm.mod, - handler: function (_a) { - var sender = _a.sender, args = _a.args; - players_1.FishPlayer.messageMuted(sender.prefixedName, args.message); - } - }, - info: { - args: ["target:player", "showColors:boolean?"], - description: "Displays information about an online player.", - perm: commands_1.Perm.none, - handler: function (_a) { - var sender = _a.sender, args = _a.args, output = _a.output, f = _a.f; - var info = args.target.info(); - var names = args.showColors - ? info.names.map(funcs_1.escapeStringColorsClient).toString(", ") - : __spreadArray([], __read(new Set(info.names.map(function (n) { return Strings.stripColors(n); }).toArray())), false).join(", "); - output(f(templateObject_54 || (templateObject_54 = __makeTemplateObject(["[accent]Info for player ", " [gray](", ") (#", ")\n\t[accent]Rank: ", "\n\t[accent]Role flags: ", "\n\t[accent]Stopped: ", "\n\t[accent]marked: ", "\n\t[accent]muted: ", "\n\t[accent]autoflagged: ", "\n\t[accent]VPN detected: ", "\n\t[accent]times joined / kicked: ", "/", "\n\t[accent]First joined: ", "\n\t[accent]Names used: [[", "]"], ["\\\n[accent]Info for player ", " [gray](", ") (#", ")\n\t[accent]Rank: ", "\n\t[accent]Role flags: ", "\n\t[accent]Stopped: ", "\n\t[accent]marked: ", "\n\t[accent]muted: ", "\n\t[accent]autoflagged: ", "\n\t[accent]VPN detected: ", "\n\t[accent]times joined / kicked: ", "/", "\n\t[accent]First joined: ", "\n\t[accent]Names used: [[", "]"])), args.target, (0, funcs_1.escapeStringColorsClient)(args.target.name), args.target.player.id.toString(), args.target.rank, Array.from(args.target.flags).map(function (f) { return f.coloredName(); }).join(" "), f.boolBad(!args.target.hasPerm("play")), args.target.marked() ? "until ".concat((0, utils_1.formatTimeRelative)(args.target.unmarkTime)) : "[green]false", f.boolBad(args.target.muted), f.boolBad(args.target.autoflagged), f.boolBad(args.target.ipDetectedVpn), info.timesJoined, info.timesKicked, args.target.firstJoined < 1 ? "unknown" : (0, utils_1.formatTimeRelative)(args.target.firstJoined), names)); - if (sender.hasPerm("viewUUIDs")) - output(f(templateObject_55 || (templateObject_55 = __makeTemplateObject(["\t[#FFAAAA]UUID: ", ""], ["\\t[#FFAAAA]UUID: ", ""])), args.target.uuid)); - if (sender.hasPerm("viewIPs")) - output(f(templateObject_56 || (templateObject_56 = __makeTemplateObject(["\t[#FFAAAA]IP: ", ""], ["\\t[#FFAAAA]IP: ", ""])), args.target.ip())); - } - }, - spawn: { - args: ["type:unittype", "x:number?", "y:number?", "count:number?", "team:team?", "effects:string?", "stack:boolean?"], - description: "Spawns a unit of specified type at your position. [scarlet]Usage will be logged.[]", - perm: commands_1.Perm.admin.exceptModes({ - testsrv: commands_1.Perm.trusted, - }), - data: [], - requirements: [commands_1.Req.positiveInteger("count")], - handler: function (_a) { - var _b, _c; - var sender = _a.sender, args = _a.args, data = _a.data, outputSuccess = _a.outputSuccess, f = _a.f; - var x = args.x ? (args.x * 8) : sender.player.x; - var y = args.y ? (args.y * 8) : sender.player.y; - var team = (_b = args.team) !== null && _b !== void 0 ? _b : sender.team(); - var count = Math.min((_c = args.count) !== null && _c !== void 0 ? _c : 1, 1000); - for (var i = 0; i < count; i++) { - var unit = args.type.create(team); - var xOffset = args.stack ? 0 : 0.01 * i; - var yOffset = args.stack ? 0 : 0.5 * (i % 10); - unit.set(x + xOffset, y + yOffset); - if (args.effects) - (0, utils_1.applyEffectMode)(args.effects, unit, 1e12); - unit.add(); - data.push(unit); - } - if (!(config_1.Gamemode.sandbox() || config_1.Gamemode.testsrv()) && args.effects !== 'paper') - (0, utils_1.logAction)("spawned unit ".concat(args.type.name).concat(count == 1 ? '' : " x".concat(count), " at ").concat(Math.round(x / 8), ", ").concat(Math.round(y / 8)) + (args.effects ? "with ".concat(args.effects, " effects") : ''), sender); - outputSuccess(f(templateObject_57 || (templateObject_57 = __makeTemplateObject(["Spawned unit ", " at (", ", ", ")"], ["Spawned unit ", " at (", ", ", ")"])), args.type, Math.round(x / 8), Math.round(y / 8))); - } - }, - setblock: { - args: ["x:number", "y:number", "block:block", "team:team?", "rotation:number?"], - description: "Sets the block at a location.", - perm: commands_1.Perm.admin.exceptModes({ - testsrv: commands_1.Perm.trusted, - }), - requirements: [commands_1.Req.integerRange("rotation", 0, 3)], - handler: function (_a) { - var _b, _c; - var args = _a.args, sender = _a.sender, outputSuccess = _a.outputSuccess, f = _a.f; - var team = (_b = args.team) !== null && _b !== void 0 ? _b : sender.team(); - var tile = Vars.world.tile(args.x, args.y); - if (tile == null) - (0, commands_1.fail)(f(templateObject_58 || (templateObject_58 = __makeTemplateObject(["Position (", ", ", ") is out of bounds."], ["Position (", ", ", ") is out of bounds."])), args.x, args.y)); - tile.setNet(args.block, team, (_c = args.rotation) !== null && _c !== void 0 ? _c : 0); - (0, utils_1.addToTileHistory)({ - pos: "".concat(args.x, ",").concat(args.y), - uuid: sender.uuid, - action: "setblocked", - type: args.block.localizedName - }); - if (!(config_1.Gamemode.sandbox() || config_1.Gamemode.testsrv())) - (0, utils_1.logAction)("set block to ".concat(args.block.localizedName, " at ").concat(args.x, ",").concat(args.y), sender); - outputSuccess(f(templateObject_59 || (templateObject_59 = __makeTemplateObject(["Set block at ", ", ", " to ", ""], ["Set block at ", ", ", " to ", ""])), args.x, args.y, args.block)); - } - }, - setblockr: { - args: ["block:block?", "team:team?", "rotation:number?"], - description: "Sets the block at tapped locations, repeatedly.", - perm: commands_1.Perm.admin, - requirements: [commands_1.Req.integerRange("rotation", 0, 3)], - tapped: function (_a) { - var _b, _c; - var args = _a.args, sender = _a.sender, f = _a.f, x = _a.x, y = _a.y, outputSuccess = _a.outputSuccess; - if (!args.block) - (0, funcs_1.crash)("uh oh"); - var team = (_b = args.team) !== null && _b !== void 0 ? _b : sender.team(); - var tile = Vars.world.tile(x, y); - if (tile == null) - (0, commands_1.fail)(f(templateObject_60 || (templateObject_60 = __makeTemplateObject(["Position (", ", ", ") is out of bounds."], ["Position (", ", ", ") is out of bounds."])), x, y)); - tile.setNet(args.block, team, (_c = args.rotation) !== null && _c !== void 0 ? _c : 0); - (0, utils_1.addToTileHistory)({ - pos: "".concat(x, ",").concat(y), - uuid: sender.uuid, - action: "setblocked", - type: args.block.localizedName - }); - if (!(config_1.Gamemode.sandbox() || config_1.Gamemode.testsrv())) - (0, utils_1.logAction)("set block to ".concat(args.block.localizedName, " at ").concat(x, ",").concat(y), sender); - outputSuccess(f(templateObject_61 || (templateObject_61 = __makeTemplateObject(["Set block at ", ", ", " to ", ""], ["Set block at ", ", ", " to ", ""])), x, y, args.block)); - }, - handler: function (_a) { - var args = _a.args, outputSuccess = _a.outputSuccess, handleTaps = _a.handleTaps, currentTapMode = _a.currentTapMode, f = _a.f; - if (args.block) { - handleTaps("on"); - if (currentTapMode == "off") { - outputSuccess("setblockr enabled.\n[scarlet]Be careful, you have the midas touch now![] Turn it off by running /setblockr again."); - } - else { - outputSuccess(f(templateObject_62 || (templateObject_62 = __makeTemplateObject(["Changed setblockr's block to ", ""], ["Changed setblockr's block to ", ""])), args.block)); - } - } - else { - if (currentTapMode == "off") { - (0, commands_1.fail)("Please specify the block to place."); - } - else { - handleTaps("off"); - outputSuccess("setblockr disabled."); - } - } - } - }, - exterminate: { - args: [], - description: "Removes all spawned units.", - perm: commands_1.Perm.admin.exceptModes({ - testsrv: commands_1.Perm.trusted, - }), - handler: function (_a) { - var sender = _a.sender, outputSuccess = _a.outputSuccess, f = _a.f, allCommands = _a.allCommands; - var numKilled = 0; - allCommands.spawn.data.forEach(function (u) { - if (u.isAdded() && !u.dead) { - u.kill(); - numKilled++; - } - }); - if (!config_1.Gamemode.sandbox()) - (0, utils_1.logAction)("exterminated ".concat(numKilled, " units"), sender); - outputSuccess(f(templateObject_63 || (templateObject_63 = __makeTemplateObject(["Exterminated ", " units."], ["Exterminated ", " units."])), numKilled)); - } - }, - js: { - args: ["javascript:string"], - description: "Run arbitrary javascript.", - perm: commands_1.Perm.runJS, - customUnauthorizedMessage: "[scarlet]You are not in the jsers file. This incident will be reported.[]", - handler: function (_a) { - var javascript = _a.args.javascript, output = _a.output, outputFail = _a.outputFail, sender = _a.sender; - //Additional validation couldn't hurt... - var playerInfo_AdminUsid = sender.info().adminUsid; - if (!playerInfo_AdminUsid || playerInfo_AdminUsid != sender.player.usid() || sender.usid != sender.player.usid()) { - api.sendModerationMessage("# !!!!! /js authentication failed !!!!!\nServer: ".concat(config_1.Gamemode.name(), " Player: ").concat((0, funcs_1.escapeTextDiscord)(sender.cleanedName), "/`").concat(sender.uuid, "`\n<@!709904412033810533>")); - (0, commands_1.fail)("Authentication failure"); - } - if (javascript == "Timer.instance().clear()") - (0, commands_1.fail)("Are you really sure you want to do that? If so, prepend \"void\" to your command."); - try { - var scripts = Vars.mods.getScripts(); - var out = scripts.context.evaluateString(scripts.scope, javascript, "fish-js-console.js", 1); - if (out instanceof Array) { - output("[cyan]Array: [[[]" + out.join(", ") + "[cyan]]"); - } - else if (out === undefined) { - output("[blue]undefined[]"); - } - else if (out === null) { - output("[blue]null[]"); - } - else if (out instanceof Error) { - outputFail((0, funcs_1.parseError)(out)); - } - else if (typeof out == "number") { - output("[blue]".concat(out, "[]")); - } - else { - output(out); - } - } - catch (err) { - outputFail((0, funcs_1.parseError)(err)); - } - } - }, - fjs: { - args: ["javascript:string"], - description: "Run arbitrary javascript in the fish-commands context.", - perm: commands_1.Perm.runJS, - customUnauthorizedMessage: "[scarlet]You are not in the jsers file. This incident will be reported.[]", - handler: function (_a) { - var javascript = _a.args.javascript, output = _a.output, outputFail = _a.outputFail, sender = _a.sender; - //Additional validation couldn't hurt... - var playerInfo_AdminUsid = sender.info().adminUsid; - if (!playerInfo_AdminUsid || playerInfo_AdminUsid != sender.player.usid() || sender.usid != sender.player.usid()) { - api.sendModerationMessage("# !!!!! /js authentication failed !!!!!\nServer: ".concat(config_1.Gamemode.name(), " Player: ").concat((0, funcs_1.escapeTextDiscord)(sender.cleanedName), "/`").concat(sender.uuid, "`\n<@!709904412033810533>")); - (0, commands_1.fail)("Authentication failure"); - } - fjsContext.runJS(javascript, output, outputFail, sender); - } - }, - antibot: { - args: ["timeout:time?"], - description: "Checks anti bot stats, or force enables anti bot mode.", - perm: commands_1.Perm.mod, - handler: function (_a) { - var args = _a.args, sender = _a.sender, outputSuccess = _a.outputSuccess, output = _a.output, f = _a.f; - if (args.timeout != undefined) { - args.timeout = Math.min(args.timeout, sender.hasPerm("admin") ? funcs_1.Duration.hours(1) : funcs_1.Duration.minutes(10)); - players_1.FishPlayer.triggerAntibot(args.timeout, "Manually triggered by player ".concat(sender.name), "manual"); - outputSuccess("Set antibot mode override for ".concat((0, utils_1.formatTime)(args.timeout), ".")); - } - else { - output("[acid]Antibot status:\n[acid]Enabled: ".concat(f.boolBad(players_1.FishPlayer.antiBotMode()), "\n").concat((0, utils_1.getAntiBotInfo)("client"))); - } - } - }, - chatstrictness: { - args: ["player:player", "value:string"], - description: "Sets chat strictness for a player.", - perm: commands_1.Perm.mod, - handler: function (_a) { - var _b = _a.args, player = _b.player, value = _b.value, sender = _a.sender, outputSuccess = _a.outputSuccess, f = _a.f; - if (!sender.canModerate(player, true)) - (0, commands_1.fail)("You do not have permission to set the chat strictness level of this player."); - if (!(value == "chat" || value == "strict")) - (0, commands_1.fail)("Invalid chat strictness level: valid levels are \"chat\", \"strict\""); - player.chatStrictness = value; - (0, utils_1.logAction)("set chat strictness to ".concat(value, " for"), sender, player); - outputSuccess(f(templateObject_64 || (templateObject_64 = __makeTemplateObject(["Set chat strictness for player ", " to \"", "\"."], ["Set chat strictness for player ", " to \"", "\"."])), player, value)); - } - }, - emanate: (0, commands_1.command)(function () { - var unitMapping = {}; - Timer.schedule(function () { - var e_1, _a; - try { - for (var _b = __values(Object.entries(unitMapping)), _c = _b.next(); !_c.done; _c = _b.next()) { - var _d = __read(_c.value, 2), uuid = _d[0], unit = _d[1]; - var fishP = players_1.FishPlayer.getById(uuid); - if (!fishP || !fishP.connected() || (unit.getPlayer() != fishP.player)) { - delete unitMapping[uuid]; - unit === null || unit === void 0 ? void 0 : unit.kill(); - } - } - } - catch (e_1_1) { e_1 = { error: e_1_1 }; } - finally { - try { - if (_c && !_c.done && (_a = _b.return)) _a.call(_b); - } - finally { if (e_1) throw e_1.error; } - } - }, 1, 0.5); - return { - args: [], - description: "Puts you in an emanate.", - perm: commands_1.Perm.admin, - data: { unitMapping: unitMapping }, - requirements: [], - handler: function (_a) { - var sender = _a.sender, outputSuccess = _a.outputSuccess; - var emanate = UnitTypes.emanate.spawn(sender.team(), sender.player.x, sender.player.y); - sender.player.unit(emanate); - unitMapping[sender.uuid] = emanate; - if (!config_1.Gamemode.sandbox()) - (0, utils_1.logAction)("spawned an emanate", sender); - outputSuccess("Spawned an emanate."); - } - }; - }), - updatemaps: { - args: [], - description: 'Attempt to fetch and update all map files', - perm: commands_1.Perm.trusted, - requirements: function (_a) { - var sender = _a.sender; - return [commands_1.Req.cooldownGlobal(config_1.Gamemode.testsrv() || sender.hasPerm("mod") ? 15000 : funcs_1.Duration.minutes(5))]; - }, - handler: function (_a) { - var output = _a.output, outputSuccess = _a.outputSuccess, outputFail = _a.outputFail; - output("Updating maps... (this may take a while)"); - (0, files_1.updateMaps)() - .then(function (changed) { - Log.info("Maps updated."); - if (changed) { - outputSuccess("Map update completed."); - Call.sendMessage("[orange]Maps have been updated. Run [white]/maps[] to view available maps."); - } - else { - outputSuccess("Map update completed; already up to date."); - } - }) - .catch(function (message) { - outputFail("Map update failed: ".concat(String(message))); - Log.err("Map updates failed: ".concat(String(message))); - }); - } - }, - clearfire: { - args: [], - description: "Clears all the fires.", - perm: commands_1.Perm.admin, - handler: function (_a) { - var output = _a.output, outputSuccess = _a.outputSuccess; - output("Removing fires..."); - var totalRemoved = 0; - Call.sendMessage("[scarlet][[Fire Department]:[yellow] Fires were reported. Trucks are en-route. Removing all fires shortly."); - Timer.schedule(function () { - totalRemoved += Groups.fire.size(); - Groups.fire.each(function (f) { return f.remove(); }); - Groups.fire.clear(); - }, 2, 0.1, 40); - Timer.schedule(function () { - outputSuccess("Removed ".concat(totalRemoved, " fires.")); - Call.sendMessage("[scarlet][[Fire Department]:[yellow] We've extinguished ".concat(totalRemoved, " fires.")); - }, 6.1); - } - }, - search: { - args: ["input:string"], - description: "Searches playerinfo by name, IP, or UUID.", - perm: commands_1.Perm.admin, - handler: function (_a) { - return __awaiter(this, arguments, void 0, function (_b) { - var fishP, info, matches, matches_1, displayMatches; - var input = _b.args.input, admins = _b.admins, output = _b.output, f = _b.f, sender = _b.sender; - return __generator(this, function (_c) { - switch (_c.label) { - case 0: - if (!globals_1.uuidPattern.test(input)) return [3 /*break*/, 1]; - fishP = players_1.FishPlayer.getById(input); - info = admins.getInfoOptional(input); - if (fishP == null && info == null) - (0, commands_1.fail)(f(templateObject_65 || (templateObject_65 = __makeTemplateObject(["No stored data matched uuid ", "."], ["No stored data matched uuid ", "."])), input)); - else if (fishP == null && info) - output(f(templateObject_66 || (templateObject_66 = __makeTemplateObject(["[accent]Found player info (but no fish player data) for uuid ", "\nLast name used: \"", "\" [gray](", ")[] [[", "]\nIPs used: ", ""], ["[accent]\\\nFound player info (but no fish player data) for uuid ", "\nLast name used: \"", "\" [gray](", ")[] [[", "]\nIPs used: ", ""])), input, info.plainLastName(), (0, funcs_1.escapeStringColorsClient)(info.lastName), info.names.map(funcs_1.escapeStringColorsClient).items.join(", "), info.ips.map(function (i) { return "[blue]".concat(i, "[]"); }).toString(", "))); - else if (fishP && info) - output(f(templateObject_67 || (templateObject_67 = __makeTemplateObject(["[accent]Found fish player data for uuid ", "\nLast name used: \"", "\" [gray](", ")[] [[", "]\nIPs used: ", ""], ["[accent]\\\nFound fish player data for uuid ", "\nLast name used: \"", "\" [gray](", ")[] [[", "]\nIPs used: ", ""])), input, fishP.name, (0, funcs_1.escapeStringColorsClient)(info.lastName), info.names.map(funcs_1.escapeStringColorsClient).items.join(", "), info.ips.map(function (i) { return "[blue]".concat(i, "[]"); }).toString(", "))); - else - (0, commands_1.fail)(f(templateObject_68 || (templateObject_68 = __makeTemplateObject(["Super weird edge case: found fish player data but no player info for uuid ", "."], ["Super weird edge case: found fish player data but no player info for uuid ", "."])), input)); - return [3 /*break*/, 5]; - case 1: - if (!globals_1.ipPattern.test(input)) return [3 /*break*/, 2]; - matches = admins.findByIPs(input); - if (matches.isEmpty()) - (0, commands_1.fail)(f(templateObject_69 || (templateObject_69 = __makeTemplateObject(["No stored data matched IP ", ""], ["No stored data matched IP ", ""])), input)); - output(f(templateObject_70 || (templateObject_70 = __makeTemplateObject(["[accent]Found ", " match", " for search \"", "\"."], ["[accent]Found ", " match", " for search \"", "\"."])), matches.size, matches.size == 1 ? "" : "es", input)); - matches.each(function (info) { return output(f(templateObject_71 || (templateObject_71 = __makeTemplateObject(["[accent]Player with uuid ", "\nLast name used: \"", "\" [gray](", ")[] [[", "]\nIPs used: ", ""], ["[accent]\\\nPlayer with uuid ", "\nLast name used: \"", "\" [gray](", ")[] [[", "]\nIPs used: ", ""])), info.id, info.plainLastName(), (0, funcs_1.escapeStringColorsClient)(info.lastName), info.names.map(funcs_1.escapeStringColorsClient).items.join(", "), info.ips.map(function (i) { return "[blue]".concat(i, "[]"); }).toString(", "))); }); - return [3 /*break*/, 5]; - case 2: - matches_1 = Vars.netServer.admins.searchNames(input); - if (matches_1.isEmpty()) - (0, commands_1.fail)(f(templateObject_72 || (templateObject_72 = __makeTemplateObject(["No stored data matched name ", ""], ["No stored data matched name ", ""])), input)); - output(f(templateObject_73 || (templateObject_73 = __makeTemplateObject(["[accent]Found ", " match", " for search \"", "\"."], ["[accent]Found ", " match", " for search \"", "\"."])), matches_1.size, matches_1.size == 1 ? "" : "es", input)); - displayMatches = function () { - matches_1.each(function (info) { return output(f(templateObject_74 || (templateObject_74 = __makeTemplateObject(["[accent]Player with uuid ", "\nLast name used: \"", "\" [gray](", ")[] [[", "]\nIPs used: ", ""], ["[accent]\\\nPlayer with uuid ", "\nLast name used: \"", "\" [gray](", ")[] [[", "]\nIPs used: ", ""])), info.id, info.plainLastName(), (0, funcs_1.escapeStringColorsClient)(info.lastName), info.names.map(funcs_1.escapeStringColorsClient).items.join(", "), info.ips.map(function (i) { return "[blue]".concat(i, "[]"); }).toString(", "))); }); - }; - if (!(matches_1.size > 20)) return [3 /*break*/, 4]; - return [4 /*yield*/, menus_1.Menu.confirm(sender, "Are you sure you want to view all ".concat(matches_1.size, " matches?"))]; - case 3: - _c.sent(); - _c.label = 4; - case 4: - displayMatches(); - _c.label = 5; - case 5: return [2 /*return*/]; - } - }); - }); - } - }, - peace: { - args: ["peace:boolean"], - description: "Toggles peaceful mode for sandbox.", - perm: commands_1.Perm.mod, - requirements: [commands_1.Req.mode('sandbox')], - handler: function (_a) { - var args = _a.args; - if (args.peace) { - globals_1.fishState.peacefulMode = true; - Groups.player.each(function (p) { - if (p.team() != Vars.state.rules.defaultTeam) { - p.team(Vars.state.rules.defaultTeam); - } - }); - Call.sendMessage("[[Sandbox] [green]Enabled peaceful mode."); - } - else { - globals_1.fishState.peacefulMode = false; - Call.sendMessage("[[Sandbox] [red]Disabled peaceful mode."); - } - }, - }, - effects: { - args: ["mode:string", "player:player?", "duration:time?"], - description: "Applies effects to a player's unit.", - perm: commands_1.Perm.admin.exceptModes({ - testsrv: commands_1.Perm.trusted, - }), - handler: function (_a) { - var _b, _c, _d; - var args = _a.args, sender = _a.sender, f = _a.f, outputSuccess = _a.outputSuccess; - if ((_b = args.player) === null || _b === void 0 ? void 0 : _b.hasPerm("blockTrolling")) - (0, commands_1.fail)(f(templateObject_75 || (templateObject_75 = __makeTemplateObject(["Player ", " is insufficiently trollable."], ["Player ", " is insufficiently trollable."])), args.player)); - if (args.player && !sender.canModerate(args.player, false)) - (0, commands_1.fail)("You do not have permission to perform moderation actions on this player."); - var target = (_c = args.player) !== null && _c !== void 0 ? _c : sender; - var unit = target.unit(); - if (!unit || unit.dead) - (0, commands_1.fail)(f(templateObject_76 || (templateObject_76 = __makeTemplateObject(["", "'s unit is dead."], ["", "'s unit is dead."])), target)); - var ticks = ((_d = args.duration) !== null && _d !== void 0 ? _d : 1e12) / 1000 * 60; - (0, utils_1.applyEffectMode)(args.mode, unit, ticks); - outputSuccess("".concat(args.mode === "clear" ? "Cleared" : "Applied", " effects.")); - if (!config_1.Gamemode.sandbox()) - (0, utils_1.logAction)("applied **".concat(args.mode, "** effects to"), sender, target); - } - }, - items: { - args: ["team:team", "item:item", "amount:number"], - description: "Gives items to a team.", - perm: commands_1.Perm.admin, - requirements: [commands_1.Req.integer("amount")], - handler: function (_a) { - var _b; - var _c = _a.args, team = _c.team, item = _c.item, amount = _c.amount, sender = _a.sender, outputSuccess = _a.outputSuccess, f = _a.f; - var core = (_b = team.data().cores.firstOpt()) !== null && _b !== void 0 ? _b : (0, commands_1.fail)(f(templateObject_77 || (templateObject_77 = __makeTemplateObject(["Team ", " has no cores."], ["Team ", " has no cores."])), team)); - core.items.add(item, amount); - outputSuccess(f(templateObject_78 || (templateObject_78 = __makeTemplateObject(["Gave ", " ", " to ", "."], ["Gave ", " ", " to ", "."])), amount, item, team)); - if (!config_1.Gamemode.sandbox()) - (0, utils_1.logAction)("gave ".concat(amount, " ").concat(item.localizedName.toLowerCase(), " to ").concat(team.name), sender); - } - }, - explosion: { - args: ["radius:number", "x:number", "y:number", "team:team?", "damage:number?", "damageMode:string?"], - description: "Causes an explosion at specified coordinates.", - perm: commands_1.Perm.admin, - handler: function (_a) { - var _b; - var _c = _a.args, radius = _c.radius, x = _c.x, y = _c.y, _d = _c.team, team = _d === void 0 ? Team.derelict : _d, _e = _c.damage, damage = _e === void 0 ? 1e12 : _e, _f = _c.damageMode, damageMode = _f === void 0 ? "both" : _f, outputSuccess = _a.outputSuccess; - var _g = __read((_b = (0, utils_1.match)(damageMode, { - air: [true, false], - ground: [false, true], - both: [true, true], - none: [false, false], - })) !== null && _b !== void 0 ? _b : (0, commands_1.fail)("Valid values of damageMode: air, ground, both, none"), 2), air = _g[0], ground = _g[1]; - if (radius > 100) - (0, commands_1.fail)("Maximum radius is 100"); - if (damage < 0) - Call.effect(Fx.dynamicSpikes, x * 8, y * 8, radius * 8, Pal.heal); - else - Call.effect(Fx.dynamicExplosion, x * 8, y * 8, Math.max(radius, 8) / 7, Color.white); - Damage.damage(team, x * 8, y * 8, radius * 8, damage, true, air, ground); - outputSuccess("Created an explosion at (".concat(x, ", ").concat(y, ").")); - } - }, - memorycorruption: { - args: [], - description: "Triggers a fake memory corruption prank.", - perm: commands_1.Perm.mod, - requirements: [commands_1.Req.cooldownGlobal(funcs_1.Duration.minutes(30))], - handler: function () { - (0, utils_1.definitelyRealMemoryCorruption)(); - } - }, - editor: { - args: ["editor:boolean"], - description: "Toggles the in-game editor mode.", - perm: commands_1.Perm.trusted, - requirements: [commands_1.Req.mode("testsrv"), commands_1.Req.cooldownGlobal(20000)], - handler: function (_a) { - var editor = _a.args.editor; - Vars.state.rules.editor = editor; - Call.setRules(Vars.state.rules); - } - }, - mapruns: { - args: ["map:map", "lowestHighscores:boolean?"], - description: "Displays all map runs for a selected map, and allows deleting invalid/cheated runs.", - perm: commands_1.Perm.admin, - handler: function (_a) { - return __awaiter(this, arguments, void 0, function (_b) { - var fmap, _c, initialLength, runs, _d, index, _, deleted; - var _e; - var _f = _b.args, map = _f.map, lowestHighscores = _f.lowestHighscores, sender = _b.sender, outputSuccess = _b.outputSuccess; - return __generator(this, function (_g) { - switch (_g.label) { - case 0: - fmap = (_e = maps_1.FMap.getCreate(map)) !== null && _e !== void 0 ? _e : (0, commands_1.fail)("Map data is still loading, please try again."); - if (!(lowestHighscores !== null && lowestHighscores !== void 0)) return [3 /*break*/, 1]; - _c = lowestHighscores; - return [3 /*break*/, 3]; - case 1: return [4 /*yield*/, menus_1.Menu.buttons(sender, "[accent]Map runs", "Select a view", [ - [{ data: true, text: "Lowest highscores" }], - [{ data: false, text: "All runs" }], - ], { - includeCancel: true, - onCancel: "reject" - })]; - case 2: - _c = (lowestHighscores = _g.sent()); - _g.label = 3; - case 3: - _c; - initialLength = fmap.runs.length; - runs = fmap.runs.slice(); - if (lowestHighscores) - runs = runs.filter(function (r) { return r.success; }) - .sort(function (a, b) { return a.duration() - b.duration(); }); - return [4 /*yield*/, menus_1.Menu.textPages(sender, runs.map(function (r) { return [ - (0, utils_1.formatTimestamp)(r.startTime), - function () { - return "Duration: ".concat((0, utils_1.formatTime)(r.duration()), "\nMax player count: ").concat(r.maxPlayerCount, "\nOutcome: ").concat(r.outcome()[1], "\nWave: ").concat(r.wave); - } - ]; }), ["[scarlet]\uE86FDelete"], { - onCancel: "reject" - })]; - case 4: - _d = __read.apply(void 0, [_g.sent(), 2]), index = _d[0], _ = _d[1]; - return [4 /*yield*/, menus_1.Menu.confirmDangerous(sender, "Are you sure you want to delete this map run? This action is irreversible.")]; - case 5: - _g.sent(); - if (initialLength != fmap.runs.length) - (0, commands_1.fail)("Someone else deleted a run, please try again."); - deleted = fmap.runs.splice(index, 1)[0]; - outputSuccess("Deleted run (".concat((0, utils_1.formatTimestamp)(deleted.startTime), ") with duration ").concat((0, utils_1.formatTime)(deleted.duration()), ".")); - return [2 /*return*/]; - } - }); - }); - } - }, - crash: { - args: ["target:player"], - description: "Crashes the target player's Mindustry client.", - perm: commands_1.Perm.admin, - requirements: [commands_1.Req.moderate("target", false, "admin")], - handler: function (_a) { - var target = _a.args.target, f = _a.f, output = _a.output, outputSuccess = _a.outputSuccess; - if (target.hasPerm("blockTrolling")) - (0, commands_1.fail)(f(templateObject_79 || (templateObject_79 = __makeTemplateObject(["Player ", " is insufficiently trollable."], ["Player ", " is insufficiently trollable."])), target)); - if ((0, utils_1.crashClient)(target.player)) { - outputSuccess(f(templateObject_80 || (templateObject_80 = __makeTemplateObject(["Crashed client of ", "."], ["Crashed client of ", "."])), target)); - } - else { - output(f(templateObject_81 || (templateObject_81 = __makeTemplateObject(["Attempted to crash client of ", ". The crash will only occur once the sync completes."], ["Attempted to crash client of ", ". The crash will only occur once the sync completes."])), target)); - } - } - }, - yeet: { - args: ["target:player", "width:number", "height:number", "floor:block", "overlay:block", "build:block"], - description: "Sends the target player to a parallel universe.", - perm: commands_1.Perm.admin, - requirements: [commands_1.Req.moderate("target", false, "admin")], - handler: function (_a) { - return __awaiter(this, void 0, void 0, function () { - var _b = _a.args, target = _b.target, world = __rest(_b, ["target"]), f = _a.f, outputSuccess = _a.outputSuccess; - return __generator(this, function (_c) { - switch (_c.label) { - case 0: - if (target.hasPerm("blockTrolling")) - (0, commands_1.fail)(f(templateObject_82 || (templateObject_82 = __makeTemplateObject(["Player ", " is insufficiently trollable."], ["Player ", " is insufficiently trollable."])), target)); - outputSuccess("Aligning QPUs..."); - return [4 /*yield*/, (0, utils_1.syncManual)(target.player, undefined, world)]; - case 1: - _c.sent(); - outputSuccess(f(templateObject_83 || (templateObject_83 = __makeTemplateObject(["Sent ", " to a parallel universe."], ["Sent ", " to a parallel universe."])), target)); - return [2 /*return*/]; - } - }); - }); - } - }, - menuspam: { - args: ["target:player"], - description: "Sends the target player a very large amount of menus. They will be unable to do anything unless they force close mindustry.", - perm: commands_1.Perm.admin, - requirements: [commands_1.Req.moderate("target", false, "admin")], - handler: function (_a) { - return __awaiter(this, arguments, void 0, function (_b) { - var i, j; - var target = _b.args.target, f = _b.f, output = _b.output, outputSuccess = _b.outputSuccess; - return __generator(this, function (_c) { - switch (_c.label) { - case 0: - if (target.hasPerm("blockTrolling")) - (0, commands_1.fail)(f(templateObject_84 || (templateObject_84 = __makeTemplateObject(["Player ", " is insufficiently trollable."], ["Player ", " is insufficiently trollable."])), target)); - output("Sending menus."); - i = 0; - _c.label = 1; - case 1: - if (!(i < 10)) return [3 /*break*/, 4]; - for (j = 0; j < 100; j++) { - Call.menu(target.con, menus_1.listeners.generic, "", "", []); - } - return [4 /*yield*/, (0, funcs_1.delay)(100)]; - case 2: - _c.sent(); - _c.label = 3; - case 3: - i++; - return [3 /*break*/, 1]; - case 4: - outputSuccess(f(templateObject_85 || (templateObject_85 = __makeTemplateObject(["Spammed ", " with menus."], ["Spammed ", " with menus."])), target)); - return [2 /*return*/]; - } - }); - }); - } - } -}); -var templateObject_1, templateObject_2, templateObject_3, templateObject_4, templateObject_5, templateObject_6, templateObject_7, templateObject_8, templateObject_9, templateObject_10, templateObject_11, templateObject_12, templateObject_13, templateObject_14, templateObject_15, templateObject_16, templateObject_17, templateObject_18, templateObject_19, templateObject_20, templateObject_21, templateObject_22, templateObject_23, templateObject_24, templateObject_25, templateObject_26, templateObject_27, templateObject_28, templateObject_29, templateObject_30, templateObject_31, templateObject_32, templateObject_33, templateObject_34, templateObject_35, templateObject_36, templateObject_37, templateObject_38, templateObject_39, templateObject_40, templateObject_41, templateObject_42, templateObject_43, templateObject_44, templateObject_45, templateObject_46, templateObject_47, templateObject_48, templateObject_49, templateObject_50, templateObject_51, templateObject_52, templateObject_53, templateObject_54, templateObject_55, templateObject_56, templateObject_57, templateObject_58, templateObject_59, templateObject_60, templateObject_61, templateObject_62, templateObject_63, templateObject_64, templateObject_65, templateObject_66, templateObject_67, templateObject_68, templateObject_69, templateObject_70, templateObject_71, templateObject_72, templateObject_73, templateObject_74, templateObject_75, templateObject_76, templateObject_77, templateObject_78, templateObject_79, templateObject_80, templateObject_81, templateObject_82, templateObject_83, templateObject_84, templateObject_85; +"use strict"; +/* +Copyright © BalaM314, 2026. All Rights Reserved. +This file contains the in-game chat commands that can be run by trusted staff. +*/ +var __makeTemplateObject = (this && this.__makeTemplateObject) || function (cooked, raw) { + if (Object.defineProperty) { Object.defineProperty(cooked, "raw", { value: raw }); } else { cooked.raw = raw; } + return cooked; +}; +var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + var desc = Object.getOwnPropertyDescriptor(m, k); + if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { + desc = { enumerable: true, get: function() { return m[k]; } }; + } + Object.defineProperty(o, k2, desc); +}) : (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + o[k2] = m[k]; +})); +var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { + Object.defineProperty(o, "default", { enumerable: true, value: v }); +}) : function(o, v) { + o["default"] = v; +}); +var __importStar = (this && this.__importStar) || (function () { + var ownKeys = function(o) { + ownKeys = Object.getOwnPropertyNames || function (o) { + var ar = []; + for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k; + return ar; + }; + return ownKeys(o); + }; + return function (mod) { + if (mod && mod.__esModule) return mod; + var result = {}; + if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]); + __setModuleDefault(result, mod); + return result; + }; +})(); +var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { + function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } + return new (P || (P = Promise))(function (resolve, reject) { + function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } + function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } + function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } + step((generator = generator.apply(thisArg, _arguments || [])).next()); + }); +}; +var __generator = (this && this.__generator) || function (thisArg, body) { + var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g = Object.create((typeof Iterator === "function" ? Iterator : Object).prototype); + return g.next = verb(0), g["throw"] = verb(1), g["return"] = verb(2), typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g; + function verb(n) { return function (v) { return step([n, v]); }; } + function step(op) { + if (f) throw new TypeError("Generator is already executing."); + while (g && (g = 0, op[0] && (_ = 0)), _) try { + if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t; + if (y = 0, t) op = [op[0] & 2, t.value]; + switch (op[0]) { + case 0: case 1: t = op; break; + case 4: _.label++; return { value: op[1], done: false }; + case 5: _.label++; y = op[1]; op = [0]; continue; + case 7: op = _.ops.pop(); _.trys.pop(); continue; + default: + if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; } + if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; } + if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; } + if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; } + if (t[2]) _.ops.pop(); + _.trys.pop(); continue; + } + op = body.call(thisArg, _); + } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; } + if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true }; + } +}; +var __rest = (this && this.__rest) || function (s, e) { + var t = {}; + for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0) + t[p] = s[p]; + if (s != null && typeof Object.getOwnPropertySymbols === "function") + for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) { + if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i])) + t[p[i]] = s[p[i]]; + } + return t; +}; +var __read = (this && this.__read) || function (o, n) { + var m = typeof Symbol === "function" && o[Symbol.iterator]; + if (!m) return o; + var i = m.call(o), r, ar = [], e; + try { + while ((n === void 0 || n-- > 0) && !(r = i.next()).done) ar.push(r.value); + } + catch (error) { e = { error: error }; } + finally { + try { + if (r && !r.done && (m = i["return"])) m.call(i); + } + finally { if (e) throw e.error; } + } + return ar; +}; +var __spreadArray = (this && this.__spreadArray) || function (to, from, pack) { + if (pack || arguments.length === 2) for (var i = 0, l = from.length, ar; i < l; i++) { + if (ar || !(i in from)) { + if (!ar) ar = Array.prototype.slice.call(from, 0, i); + ar[i] = from[i]; + } + } + return to.concat(ar || Array.prototype.slice.call(from)); +}; +var __values = (this && this.__values) || function(o) { + var s = typeof Symbol === "function" && Symbol.iterator, m = s && o[s], i = 0; + if (m) return m.call(o); + if (o && typeof o.length === "number") return { + next: function () { + if (o && i >= o.length) o = void 0; + return { value: o && o[i++], done: !o }; + } + }; + throw new TypeError(s ? "Object is not iterable." : "Symbol.iterator is not defined."); +}; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.commands = void 0; +var api = __importStar(require("/api")); +var config_1 = require("/config"); +var files_1 = require("/files"); +var fjsContext = __importStar(require("/fjsContext")); +var commands_1 = require("/frameworks/commands"); +var menus_1 = require("/frameworks/menus"); +var funcs_1 = require("/funcs"); +var globals_1 = require("/globals"); +var maps_1 = require("/maps"); +var players_1 = require("/players"); +var ranks_1 = require("/ranks"); +var utils_1 = require("/utils"); +exports.commands = (0, commands_1.commandList)({ + warn: { + args: ['player:player', 'message:string?'], + description: 'Sends the player a warning (menu popup).', + perm: commands_1.Perm.warn, + requirements: [commands_1.Req.cooldown(3000)], + handler: function (_a) { + var _b; + var args = _a.args, sender = _a.sender, outputSuccess = _a.outputSuccess, f = _a.f; + if (args.player.hasPerm("blockTrolling")) + (0, commands_1.fail)(f(templateObject_1 || (templateObject_1 = __makeTemplateObject(["Player ", " is insufficiently trollable."], ["Player ", " is insufficiently trollable."])), args.player)); + var message = (_b = args.message) !== null && _b !== void 0 ? _b : "You have been warned. I suggest you stop what you're doing"; + void menus_1.Menu.menu('Warning', message, ["[green]Accept"], args.player, { onCancel: 'null' }) + .then(function () { return outputSuccess('Player acknowledged the warning.'); }); + (0, utils_1.logAction)('warned', sender, args.player, message); + outputSuccess(f(templateObject_2 || (templateObject_2 = __makeTemplateObject(["Warned player ", " for \"", "\""], ["Warned player ", " for \"", "\""])), args.player, message)); + } + }, + mute: { + args: ['player:player'], + description: 'Stops a player from chatting.', + perm: commands_1.Perm.mod, + requirements: [commands_1.Req.moderate("player")], + handler: function (_a) { + return __awaiter(this, arguments, void 0, function (_b) { + var args = _b.args, sender = _b.sender, outputSuccess = _b.outputSuccess, f = _b.f; + return __generator(this, function (_c) { + switch (_c.label) { + case 0: + if (args.player.muted) + (0, commands_1.fail)(f(templateObject_3 || (templateObject_3 = __makeTemplateObject(["Player ", " is already muted."], ["Player ", " is already muted."])), args.player)); + return [4 /*yield*/, args.player.mute(sender)]; + case 1: + _c.sent(); + (0, utils_1.logAction)('muted', sender, args.player); + outputSuccess(f(templateObject_4 || (templateObject_4 = __makeTemplateObject(["Muted player ", "."], ["Muted player ", "."])), args.player)); + return [2 /*return*/]; + } + }); + }); + } + }, + unmute: { + args: ['player:player'], + description: 'Unmutes a player', + perm: commands_1.Perm.mod, + handler: function (_a) { + return __awaiter(this, arguments, void 0, function (_b) { + var args = _b.args, sender = _b.sender, outputSuccess = _b.outputSuccess, f = _b.f; + return __generator(this, function (_c) { + switch (_c.label) { + case 0: + if (!args.player.muted && args.player.autoflagged) + (0, commands_1.fail)(f(templateObject_5 || (templateObject_5 = __makeTemplateObject(["Player ", " is not muted, but they are autoflagged. You probably want to free them with /free."], ["Player ", " is not muted, but they are autoflagged. You probably want to free them with /free."])), args.player)); + if (!args.player.muted) + (0, commands_1.fail)(f(templateObject_6 || (templateObject_6 = __makeTemplateObject(["Player ", " is not muted."], ["Player ", " is not muted."])), args.player)); + return [4 /*yield*/, args.player.unmute(sender)]; + case 1: + _c.sent(); + (0, utils_1.logAction)('unmuted', sender, args.player); + outputSuccess(f(templateObject_7 || (templateObject_7 = __makeTemplateObject(["Unmuted player ", "."], ["Unmuted player ", "."])), args.player)); + return [2 /*return*/]; + } + }); + }); + } + }, + kick: { + args: ["player:player", "duration:time?", "reason:string?"], + description: 'Kick a player with optional reason.', + perm: commands_1.Perm.mod, + requirements: [commands_1.Req.moderate("player")], + handler: function (_a) { + var _b, _c, _d; + var args = _a.args, outputSuccess = _a.outputSuccess, f = _a.f, sender = _a.sender; + if (!sender.hasPerm("admin") && args.duration && args.duration > funcs_1.Duration.hours(6)) + (0, commands_1.fail)("Maximum kick duration is 6 hours."); + var reason = (_b = args.reason) !== null && _b !== void 0 ? _b : "A staff member did not like your actions."; + var duration = (_c = args.duration) !== null && _c !== void 0 ? _c : 60000; + args.player.kick(reason, duration); + (0, utils_1.logAction)("kicked", sender, args.player, (_d = args.reason) !== null && _d !== void 0 ? _d : undefined, duration); + if (duration > 60000) + args.player.setPunishedIP(config_1.stopAntiEvadeTime); + outputSuccess(f(templateObject_8 || (templateObject_8 = __makeTemplateObject(["Kicked player ", " for ", " with reason \"", "\""], ["Kicked player ", " for ", " with reason \"", "\""])), args.player, (0, utils_1.formatTime)(duration), reason)); + } + }, + pardon: { + args: ["player:offlinePlayer"], + description: 'Pardons a votekicked player.', + perm: commands_1.Perm.mod, + requirements: [commands_1.Req.moderate("player")], + handler: function (_a) { + var player = _a.args.player, admins = _a.admins, outputSuccess = _a.outputSuccess, f = _a.f; + var info = admins.getInfo(player.uuid); + if (Time.millis() > info.lastKicked && !admins.kickedIPs.containsKey(info.lastIP)) + (0, commands_1.fail)("That player is not kicked."); + info.lastKicked = 0; + admins.kickedIPs.remove(info.lastIP); + outputSuccess(f(templateObject_9 || (templateObject_9 = __makeTemplateObject(["Pardoned player ", "."], ["Pardoned player ", "."])), player)); + } + }, + stop: { + args: ['player:player', "time:time?", "message:string?"], + description: 'Stops a player.', + perm: commands_1.Perm.mod, + requirements: [commands_1.Req.moderate("player", true)], + handler: function (_a) { + return __awaiter(this, arguments, void 0, function (_b) { + var previousTime, time; + var _c, _d, _e, _f; + var args = _b.args, sender = _b.sender, outputSuccess = _b.outputSuccess, f = _b.f; + return __generator(this, function (_g) { + switch (_g.label) { + case 0: + if (!args.player.marked()) return [3 /*break*/, 2]; + //overload: overwrite stoptime + if (!args.time) + (0, commands_1.fail)(f(templateObject_10 || (templateObject_10 = __makeTemplateObject(["Player ", " is already marked."], ["Player ", " is already marked."])), args.player)); + previousTime = (0, utils_1.formatTimeRelative)(args.player.unmarkTime, true); + return [4 /*yield*/, args.player.updateStopTime(args.time)]; + case 1: + _g.sent(); + outputSuccess(f(templateObject_11 || (templateObject_11 = __makeTemplateObject(["Player ", "'s stop time has been updated to ", " (was ", ")."], ["Player ", "'s stop time has been updated to ", " (was ", ")."])), args.player, (0, utils_1.formatTime)(args.time), previousTime)); + (0, utils_1.logAction)("updated stop time of", sender, args.player, (_c = args.message) !== null && _c !== void 0 ? _c : undefined, args.time); + return [3 /*break*/, 4]; + case 2: + time = (_d = args.time) !== null && _d !== void 0 ? _d : (0, utils_1.untilForever)(); + if (time + Date.now() > globals_1.maxTime) + (0, commands_1.fail)("Error: time too high."); + return [4 /*yield*/, args.player.stop(sender, time, (_e = args.message) !== null && _e !== void 0 ? _e : undefined)]; + case 3: + _g.sent(); + (0, utils_1.logAction)('stopped', sender, args.player, (_f = args.message) !== null && _f !== void 0 ? _f : undefined, time); + //TODO outputGlobal() + Call.sendMessage("[orange]Player \"".concat(args.player.prefixedName, "[orange]\" has been marked for ").concat((0, utils_1.formatTime)(time)).concat(args.message ? " with reason: [white]".concat(args.message, "[]") : "", ".")); + _g.label = 4; + case 4: return [2 /*return*/]; + } + }); + }); + } + }, + free: { + args: ['player:player'], + description: 'Frees a player.', + perm: commands_1.Perm.mod, + handler: function (_a) { + return __awaiter(this, arguments, void 0, function (_b) { + var args = _b.args, sender = _b.sender, outputSuccess = _b.outputSuccess, outputFail = _b.outputFail, f = _b.f; + return __generator(this, function (_c) { + switch (_c.label) { + case 0: + if (!args.player.marked()) return [3 /*break*/, 2]; + return [4 /*yield*/, args.player.free(sender)]; + case 1: + _c.sent(); + (0, utils_1.logAction)('freed', sender, args.player); + outputSuccess(f(templateObject_12 || (templateObject_12 = __makeTemplateObject(["Player ", " has been unmarked."], ["Player ", " has been unmarked."])), args.player)); + return [3 /*break*/, 3]; + case 2: + if (args.player.autoflagged) { + args.player.autoflagged = false; + args.player.sendMessage("[yellow]You have been freed! Enjoy!"); + args.player.updateName(); + args.player.forceRespawn(); + outputSuccess(f(templateObject_13 || (templateObject_13 = __makeTemplateObject(["Player ", " has been unflagged."], ["Player ", " has been unflagged."])), args.player)); + } + else { + outputFail(f(templateObject_14 || (templateObject_14 = __makeTemplateObject(["Player ", " is not marked or autoflagged."], ["Player ", " is not marked or autoflagged."])), args.player)); + } + _c.label = 3; + case 3: return [2 /*return*/]; + } + }); + }); + } + }, + setrank: { + args: ["player:player", "rank:rank"], + description: "Set a player's rank.", + perm: commands_1.Perm.mod, + requirements: [commands_1.Req.moderate("player")], + handler: function (_a) { + return __awaiter(this, arguments, void 0, function (_b) { + var _c = _b.args, rank = _c.rank, player = _c.player, outputSuccess = _b.outputSuccess, f = _b.f, sender = _b.sender; + return __generator(this, function (_d) { + switch (_d.label) { + case 0: + if (rank.level >= sender.rank.level) + (0, commands_1.fail)(f(templateObject_15 || (templateObject_15 = __makeTemplateObject(["You do not have permission to promote players to rank ", ", because your current rank is ", ""], ["You do not have permission to promote players to rank ", ", because your current rank is ", ""])), rank, sender.rank)); + if (rank == ranks_1.Rank.pi && !config_1.Mode.localDebug) + (0, commands_1.fail)(f(templateObject_16 || (templateObject_16 = __makeTemplateObject(["Rank ", " is immutable."], ["Rank ", " is immutable."])), rank)); + if (player.immutable() && !config_1.Mode.localDebug) + (0, commands_1.fail)(f(templateObject_17 || (templateObject_17 = __makeTemplateObject(["Player ", " is immutable."], ["Player ", " is immutable."])), player)); + if (rank == player.rank) { + outputSuccess(f(templateObject_18 || (templateObject_18 = __makeTemplateObject(["Player ", " is already at rank ", "."], ["Player ", " is already at rank ", "."])), player, rank)); + return [2 /*return*/]; + } + if (!(player == sender && rank.level < sender.rank.level)) return [3 /*break*/, 2]; + return [4 /*yield*/, menus_1.Menu.confirmDangerous(sender, "[red] ARE YOU SURE YOU WANT TO SELF DEMOTE. THIS ACTION CANNOT BE UNDONE!")]; + case 1: + _d.sent(); + _d.label = 2; + case 2: return [4 /*yield*/, player.setRank(rank)]; + case 3: + _d.sent(); + (0, utils_1.logAction)("set rank to ".concat(rank.name, " for"), sender, player); + outputSuccess(f(templateObject_19 || (templateObject_19 = __makeTemplateObject(["Set rank of player ", " to ", ""], ["Set rank of player ", " to ", ""])), player, rank)); + if (player !== sender) + player.sendMessage("[royal]Your rank has been set to ".concat(rank.coloredName(), ".")); + return [2 /*return*/]; + } + }); + }); + } + }, + setflag: { + args: ["player:player", "flag:roleflag", "value:boolean"], + description: "Set a player's role flags.", + perm: commands_1.Perm.mod, + requirements: [commands_1.Req.moderate("player")], + handler: function (_a) { + return __awaiter(this, arguments, void 0, function (_b) { + var _c = _b.args, flag = _c.flag, player = _c.player, value = _c.value, sender = _b.sender, outputSuccess = _b.outputSuccess, f = _b.f; + return __generator(this, function (_d) { + switch (_d.label) { + case 0: + if (!sender.hasPerm("admin") && !flag.assignableByModerators) + (0, commands_1.fail)(f(templateObject_20 || (templateObject_20 = __makeTemplateObject(["You do not have permission to change the value of role flag ", ""], ["You do not have permission to change the value of role flag ", ""])), flag)); + return [4 /*yield*/, player.setFlag(flag, value)]; + case 1: + _d.sent(); + (0, utils_1.logAction)("set roleflag ".concat(flag.name, " to ").concat(value, " for"), sender, player); + outputSuccess(f(templateObject_21 || (templateObject_21 = __makeTemplateObject(["Set role flag ", " of player ", " to ", ""], ["Set role flag ", " of player ", " to ", ""])), flag, player, value)); + return [2 /*return*/]; + } + }); + }); + } + }, + murder: { + args: [], + description: 'Kills all ohno units', + perm: commands_1.Perm.mod, + customUnauthorizedMessage: "[yellow]You're a [scarlet]monster[].", + handler: function (_a) { + var output = _a.output, f = _a.f, allCommands = _a.allCommands; + var Ohnos = allCommands["ohno"].data; //this is not ideal... TODO commit omega shenanigans + var numOhnos = Ohnos.amount(); + Ohnos.killAll(); + output(f(templateObject_22 || (templateObject_22 = __makeTemplateObject(["[orange]You massacred ", " helpless ohno crawlers."], ["[orange]You massacred ", " helpless ohno crawlers."])), numOhnos)); + } + }, + stop_offline: { + args: ["time:time?", "name:string?"], + description: "Stops an offline player.", + perm: commands_1.Perm.mod, + handler: function (_a) { + return __awaiter(this, arguments, void 0, function (_b) { + function stop(option, time) { + return __awaiter(this, void 0, void 0, function () { + var fishP; + return __generator(this, function (_a) { + switch (_a.label) { + case 0: + fishP = players_1.FishPlayer.getFromInfo(option); + if (!sender.canModerate(fishP, true)) return [3 /*break*/, 2]; + (0, utils_1.logAction)(fishP.marked() ? time == 1000 ? "freed" : "updated stop time of" : "stopped", sender, option, undefined, time); + return [4 /*yield*/, fishP.stop(sender, time)]; + case 1: + _a.sent(); + outputSuccess(f(templateObject_23 || (templateObject_23 = __makeTemplateObject(["Player ", " was marked for ", "."], ["Player ", " was marked for ", "."])), option, (0, utils_1.formatTime)(time))); + return [3 /*break*/, 3]; + case 2: + outputFail("You do not have permission to stop this player."); + _a.label = 3; + case 3: return [2 /*return*/]; + } + }); + }); + } + var maxPlayers, info, possiblePlayers, exactPlayers, score_1, optionPlayer, _c, _d, _e; + var _f, _g; + var args = _b.args, sender = _b.sender, outputFail = _b.outputFail, outputSuccess = _b.outputSuccess, f = _b.f, admins = _b.admins; + return __generator(this, function (_h) { + switch (_h.label) { + case 0: + maxPlayers = 60; + if (!(args.name && globals_1.uuidPattern.test(args.name))) return [3 /*break*/, 4]; + info = admins.getInfoOptional(args.name); + if (!(info != null)) return [3 /*break*/, 2]; + return [4 /*yield*/, stop(info, (_f = args.time) !== null && _f !== void 0 ? _f : (0, utils_1.untilForever)())]; + case 1: + _h.sent(); + return [3 /*break*/, 3]; + case 2: + outputFail(f(templateObject_24 || (templateObject_24 = __makeTemplateObject(["Unknown UUID ", ""], ["Unknown UUID ", ""])), args.name)); + _h.label = 3; + case 3: return [2 /*return*/]; + case 4: + if (args.name) { + possiblePlayers = (0, funcs_1.setToArray)(admins.searchNames(args.name)); + if (possiblePlayers.length > maxPlayers) { + exactPlayers = (0, funcs_1.setToArray)(admins.findByName(args.name)); + if (exactPlayers.length > 0) { + possiblePlayers = exactPlayers; + } + else { + (0, commands_1.fail)("Too many players with that name."); + } + } + else if (possiblePlayers.length == 0) { + (0, commands_1.fail)("No players with that name were found."); + } + score_1 = function (data) { + var fishP = players_1.FishPlayer.getById(data.id); + if (fishP) + return fishP.lastJoined; + return -data.timesJoined; + }; + possiblePlayers.sort(function (a, b) { return score_1(b) - score_1(a); }); + } + else { + possiblePlayers = players_1.FishPlayer.recentLeaves.map(function (p) { return p.info(); }); + } + return [4 /*yield*/, menus_1.Menu.menu("Stop", "Choose a player to mark", possiblePlayers, sender, { + includeCancel: true, + optionStringifier: function (p) { return p.lastName; } + })]; + case 5: + optionPlayer = _h.sent(); + if (!((_g = args.time) !== null && _g !== void 0)) return [3 /*break*/, 6]; + _c = _g; + return [3 /*break*/, 8]; + case 6: + _d = args; + _e = utils_1.match; + return [4 /*yield*/, menus_1.Menu.menu("Stop", "Select stop time", ["2 days", "7 days", "30 days", "forever"], sender)]; + case 7: + _c = (_d.time = _e.apply(void 0, [_h.sent(), { + "2 days": funcs_1.Duration.days(2), + "7 days": funcs_1.Duration.days(7), + "30 days": funcs_1.Duration.days(30), + "forever": globals_1.maxTime - Date.now() - 10000, + }])); + _h.label = 8; + case 8: + _c; + return [4 /*yield*/, stop(optionPlayer, args.time)]; + case 9: + _h.sent(); + return [2 /*return*/]; + } + }); + }); + } + }, + mute_offline: { + args: ["name:string?"], + description: "Mutes an offline player.", + perm: commands_1.Perm.mod, + handler: function (_a) { + return __awaiter(this, arguments, void 0, function (_b) { + function mute(option) { + return __awaiter(this, void 0, void 0, function () { + var fishP; + return __generator(this, function (_a) { + switch (_a.label) { + case 0: + fishP = players_1.FishPlayer.getFromInfo(option); + if (!sender.canModerate(fishP, true)) + (0, commands_1.fail)("You do not have permission to mute this player."); + return [4 /*yield*/, menus_1.Menu.confirm(sender, "Are you sure you want to ".concat(fishP.muted ? "unmute" : "mute", " player ").concat(option.lastName, "?"), { + title: "Mute Offine Confirmation", + confirmText: "[green]Yes, ".concat(fishP.muted ? "unmute" : "mute", " them"), + })]; + case 1: + _a.sent(); + (0, utils_1.logAction)(fishP.muted ? "unmuted" : "muted", sender, fishP); + if (!fishP.muted) return [3 /*break*/, 3]; + return [4 /*yield*/, fishP.unmute(sender)]; + case 2: + _a.sent(); + return [3 /*break*/, 5]; + case 3: return [4 /*yield*/, fishP.mute(sender)]; + case 4: + _a.sent(); + _a.label = 5; + case 5: + outputSuccess("".concat(fishP.muted ? "Muted" : "Unmuted", " ").concat(option.lastName, ".")); + return [2 /*return*/]; + } + }); + }); + } + var maxPlayers, info, possiblePlayers, exactPlayers, score_2, option; + var _c; + var args = _b.args, sender = _b.sender, outputSuccess = _b.outputSuccess, f = _b.f, admins = _b.admins; + return __generator(this, function (_d) { + switch (_d.label) { + case 0: + maxPlayers = 300; + if (!(args.name && globals_1.uuidPattern.test(args.name))) return [3 /*break*/, 2]; + info = (_c = admins.getInfoOptional(args.name)) !== null && _c !== void 0 ? _c : (0, commands_1.fail)(f(templateObject_25 || (templateObject_25 = __makeTemplateObject(["Unknown UUID ", ""], ["Unknown UUID ", ""])), args.name)); + return [4 /*yield*/, mute(info)]; + case 1: + _d.sent(); + return [2 /*return*/]; + case 2: + if (args.name) { + possiblePlayers = (0, funcs_1.setToArray)(admins.searchNames(args.name)); + if (possiblePlayers.length > maxPlayers) { + exactPlayers = (0, funcs_1.setToArray)(admins.findByName(args.name)); + if (exactPlayers.length > 0) { + possiblePlayers = exactPlayers; + } + else { + (0, commands_1.fail)("Too many players with that name."); + } + } + else if (possiblePlayers.length == 0) { + (0, commands_1.fail)("No players with that name were found."); + } + score_2 = function (data) { + var fishP = players_1.FishPlayer.getById(data.id); + if (fishP) + return fishP.lastJoined; + return -data.timesJoined; + }; + possiblePlayers.sort(function (a, b) { return score_2(b) - score_2(a); }); + } + else { + possiblePlayers = players_1.FishPlayer.recentLeaves.map(function (p) { return p.info(); }); + } + return [4 /*yield*/, menus_1.Menu.pagedList(sender, "Mute", "Choose a player to mute", possiblePlayers, { + optionStringifier: function (p) { return p.lastName; } + })]; + case 3: + option = _d.sent(); + return [4 /*yield*/, mute(option)]; + case 4: + _d.sent(); + return [2 /*return*/]; + } + }); + }); + } + }, + restart: { + args: ["time:number?"], + perm: commands_1.Perm.admin, + description: "Restarts the server.", + handler: function (_a) { + var _b; + var time = _a.args.time; + (_b = globals_1.fishState.restartLoopTask) === null || _b === void 0 ? void 0 : _b.cancel(); + if (Groups.player.isEmpty()) { + if (time == undefined) { + Log.info("Restarting immediately as no players are online."); + time !== null && time !== void 0 ? time : (time = 0); + } + } + else if (config_1.Gamemode.pvp()) { + time !== null && time !== void 0 ? time : (time = -1); + Log.info("PVP: restart will occur at the end of the current match. Specify a time to override, but &rthat would interrupt the current pvp match, and players would lose their teams.&fr"); + } + else { + time !== null && time !== void 0 ? time : (time = 60); + } + if (time == -1) { + Call.sendMessage("[accent]---[[[coral]+++[]]---\n[accent]Server restart queued. The server will restart after the current match is over.[]\n[accent]---[[[coral]+++[]]---"); + globals_1.fishState.restartQueued = true; + } + else { + if (time < 0 || time > 100) + (0, commands_1.fail)("Invalid time: out of valid range."); + (0, utils_1.serverRestartLoop)(time); + if (config_1.Gamemode.pvp()) { + Call.sendMessage("[accent]---[[[coral]+++[]]---\n[accent]Server restart imminent. [green]We'll be back after 15 seconds.[]\n[accent]---[[[coral]+++[]]---"); + } + else { + Call.sendMessage("[accent]---[[[coral]+++[]]---\n[accent]Server restart imminent. [green]We'll be back with 20 seconds of downtime, and all progress will be saved.[]\n[accent]---[[[coral]+++[]]---"); + } + } + } + }, + history: { + args: ["player:player"], + description: "Shows moderation history for a player.", + perm: commands_1.Perm.mod, + handler: function (_a) { + var args = _a.args, output = _a.output, f = _a.f; + if (args.player.history && args.player.history.length > 0) { + output("[yellow]_______________Player history_______________\n\n" + + (args.player).history.sort(function (a, b) { return a.time - b.time; }).map(function (e) { + return "".concat(e.by, " [yellow]").concat(e.action, " ").concat(args.player.prefixedName, " [white]").concat((0, utils_1.formatTimeRelative)(e.time)); + }).join("\n")); + } + else { + output(f(templateObject_26 || (templateObject_26 = __makeTemplateObject(["[yellow]No history was found for player ", "."], ["[yellow]No history was found for player ", "."])), args.player)); + } + } + }, + save: { + args: [], + description: "Saves the game state.", + perm: commands_1.Perm.mod, + handler: function (_a) { + var outputSuccess = _a.outputSuccess; + players_1.FishPlayer.saveAll(); + players_1.FishPlayer.uploadAll(); + globals_1.FishEvents.fire("saveData", []); + var file = Vars.saveDirectory.child("1.".concat(Vars.saveExtension)); + SaveIO.save(file); + outputSuccess("Game saved."); + } + }, + wave: { + args: ["wave:number"], + description: "Sets the wave number.", + perm: commands_1.Perm.admin, + requirements: [commands_1.Req.positiveInteger("wave")], + handler: function (_a) { + var args = _a.args, outputSuccess = _a.outputSuccess, f = _a.f; + Vars.state.wave = args.wave; + outputSuccess(f(templateObject_27 || (templateObject_27 = __makeTemplateObject(["Set wave to ", ""], ["Set wave to ", ""])), Vars.state.wave)); + } + }, + label: { + args: ["time:time", "message:string"], + description: "Places a label at your position for a specified amount of time.", + perm: commands_1.Perm.mod, + handler: function (_a) { + var _b; + var args = _a.args, sender = _a.sender, outputSuccess = _a.outputSuccess, f = _a.f; + if (args.time > funcs_1.Duration.hours(10)) + (0, commands_1.fail)("Time must be less than 10 hours."); + var unit = (_b = sender.unit()) !== null && _b !== void 0 ? _b : (0, commands_1.fail)("You must be in a unit to use this command."); + var timeRemaining = args.time / 1000; + var labelx = unit.x; + var labely = unit.y; + var task = Timer.schedule(function () { + if (timeRemaining > 0) { + var timeseconds = timeRemaining % 60; + var timeminutes = (timeRemaining - timeseconds) / 60; + Call.label("".concat(sender.name, "\n\n[white]").concat(args.message, "\n\n[acid]").concat(timeminutes.toString().padStart(2, "0"), ":").concat(timeseconds.toString().padStart(2, "0")), 1, labelx, labely); + timeRemaining--; + } + }, 0, 1, args.time); + globals_1.fishState.labels.push({ x: labelx, y: labely, task: task }); + outputSuccess(f(templateObject_28 || (templateObject_28 = __makeTemplateObject(["Placed label \"", "\" for ", " seconds."], ["Placed label \"", "\" for ", " seconds."])), args.message, timeRemaining)); + } + }, + labelsticky: { + args: ["time:time", "message:string"], + description: "Places a label at the bottom left corner of everyone's screen.", + perm: commands_1.Perm.admin, + handler: function (_a) { + var args = _a.args, outputSuccess = _a.outputSuccess, f = _a.f; + if (args.time > funcs_1.Duration.hours(10)) + (0, commands_1.fail)("Time must be less than 10 hours."); + var timeRemaining = args.time / 1000; + var task = Timer.schedule(function () { + if (timeRemaining > 0) { + Call.label(args.message, 5, NaN, NaN); + timeRemaining -= 5; + } + }, 0, 5, Math.ceil(args.time / 5)); + globals_1.fishState.labels.push({ task: task, x: null, y: null }); + outputSuccess(f(templateObject_29 || (templateObject_29 = __makeTemplateObject(["Placed label \"", "\" for ", " seconds."], ["Placed label \"", "\" for ", " seconds."])), args.message, timeRemaining)); + } + }, + clearlabels: { + args: [], + description: "Removes all labels.", + perm: commands_1.Perm.mod, + handler: function (_a) { + var outputSuccess = _a.outputSuccess; + if (globals_1.fishState.labels.length == 0) + (0, commands_1.fail)("No labels found."); + globals_1.fishState.labels.forEach(function (l) { return l.task.cancel(); }); + outputSuccess("Removed all labels."); + } + }, + clearlabel: { + args: ["sticky:boolean?"], + description: "Removes the closest label, or sticky label if specified", + perm: commands_1.Perm.mod, + handler: function (_a) { + var _b; + var _c = _a.args.sticky, sticky = _c === void 0 ? false : _c, sender = _a.sender, outputSuccess = _a.outputSuccess; + if (globals_1.fishState.labels.length == 0) + (0, commands_1.fail)("No labels found."); + var label; + if (sticky) { + var index = globals_1.fishState.labels.findIndex(function (l) { return l.x == null; }); + if (index == -1) + (0, commands_1.fail)("No sticky label found."); + label = globals_1.fishState.labels.splice(index, 1)[0]; + } + else { + var unit_1 = (_b = sender.unit()) !== null && _b !== void 0 ? _b : (0, commands_1.fail)("Cannot remove the closest label because you are dead."); + var dist_1 = function (label) { + if (label.x == null || label.y == null) + return Infinity; + return Mathf.dst(label.x, label.y, unit_1.x, unit_1.y); + }; + var index = __spreadArray([], __read(globals_1.fishState.labels.entries()), false).reduce(function (a, b) { return dist_1(a[1]) < dist_1(b[1]) ? a : b; })[0]; + label = globals_1.fishState.labels.splice(index, 1)[0]; + } + label.task.cancel(); + outputSuccess("Removed one label."); + } + }, + member: { + args: ["value:boolean", "player:player"], + description: "Sets a player's member status.", + perm: commands_1.Perm.admin, + handler: function (_a) { + return __awaiter(this, arguments, void 0, function (_b) { + var args = _b.args, outputSuccess = _b.outputSuccess, f = _b.f; + return __generator(this, function (_c) { + switch (_c.label) { + case 0: return [4 /*yield*/, args.player.setFlag("member", args.value)]; + case 1: + _c.sent(); + outputSuccess(f(templateObject_30 || (templateObject_30 = __makeTemplateObject(["Set membership status of player ", " to ", "."], ["Set membership status of player ", " to ", "."])), args.player, args.value)); + return [2 /*return*/]; + } + }); + }); + } + }, + remind: { + args: ["rule:number", "target:player?"], + description: "Remind players in chat of a specific rule.", + perm: commands_1.Perm.mod, + handler: function (_a) { + var _b; + var args = _a.args, outputSuccess = _a.outputSuccess, f = _a.f; + var rule = (_b = config_1.rules[args.rule - 1]) !== null && _b !== void 0 ? _b : (0, commands_1.fail)("The rule you requested does not exist."); + if (args.target) { + args.target.sendMessage("A staff member wants to remind you of the following rule:\n" + rule); + outputSuccess(f(templateObject_31 || (templateObject_31 = __makeTemplateObject(["Reminded ", " of rule ", ""], ["Reminded ", " of rule ", ""])), args.target, args.rule)); + } + else { + Call.sendMessage("A staff member wants to remind everyone of the following rule:\n" + rule); + } + }, + }, + ban: { + args: ["uuid_or_ip:string?"], + description: "Bans a player by UUID and IP.", + perm: commands_1.Perm.admin, + handler: function (_a) { + return __awaiter(this, arguments, void 0, function (_b) { + var uuid, data, name, ip, ip, info, alreadyBanned, option; + var args = _b.args, sender = _b.sender, outputSuccess = _b.outputSuccess, f = _b.f, admins = _b.admins; + return __generator(this, function (_c) { + switch (_c.label) { + case 0: + if (!(args.uuid_or_ip && globals_1.uuidPattern.test(args.uuid_or_ip))) return [3 /*break*/, 2]; + uuid = args.uuid_or_ip; + data = void 0; + if ((data = admins.getInfoOptional(uuid)) != null && data.admin) + (0, commands_1.fail)("Cannot ban an admin."); + name = data ? "".concat((0, funcs_1.escapeStringColorsClient)(data.lastName), " (").concat(uuid, "/").concat(data.lastIP, ")") : uuid; + return [4 /*yield*/, menus_1.Menu.confirmDangerous(sender, "Are you sure you want to ban ".concat(name, "?"))]; + case 1: + _c.sent(); + admins.banPlayerID(uuid); + if (data) { + ip = data.lastIP; + admins.banPlayerIP(ip); + api.ban({ ip: ip, uuid: uuid }); + Log.info("".concat(uuid, "/").concat(ip, " was banned.")); + (0, utils_1.logAction)("banned", sender, data); + outputSuccess(f(templateObject_32 || (templateObject_32 = __makeTemplateObject(["Banned player ", " (", "/", ")"], ["Banned player ", " (", "/", ")"])), (0, funcs_1.escapeStringColorsClient)(data.lastName), uuid, ip)); + //TODO add way to specify whether to activate or escape color tags + } + else { + api.ban({ uuid: uuid }); + Log.info("".concat(uuid, " was banned.")); + (0, utils_1.logAction)("banned", sender, uuid); + outputSuccess(f(templateObject_33 || (templateObject_33 = __makeTemplateObject(["Banned player ", ". [yellow]Unable to determine IP.[]"], ["Banned player ", ". [yellow]Unable to determine IP.[]"])), uuid)); + } + (0, utils_1.updateBans)(function (player) { return "[scarlet]Player [yellow]".concat(player.name, "[scarlet] has been whacked by ").concat(sender.prefixedName, "."); }); + return [2 /*return*/]; + case 2: + if (!(args.uuid_or_ip && globals_1.ipPattern.test(args.uuid_or_ip))) return [3 /*break*/, 4]; + ip = args.uuid_or_ip; + return [4 /*yield*/, menus_1.Menu.confirmDangerous(sender, "Are you sure you want to ban IP ".concat(ip, "?"))]; + case 3: + _c.sent(); + api.ban({ ip: ip }); + info = admins.findByIP(ip); + if (info) + (0, utils_1.logAction)("banned", sender, info); + else + (0, utils_1.logAction)("banned ".concat(ip), sender); + alreadyBanned = admins.banPlayerIP(ip); + if (alreadyBanned) { + outputSuccess(f(templateObject_34 || (templateObject_34 = __makeTemplateObject(["IP ", " is already banned. Ban was synced to other servers."], ["IP ", " is already banned. Ban was synced to other servers."])), ip)); + } + else { + outputSuccess(f(templateObject_35 || (templateObject_35 = __makeTemplateObject(["IP ", " has been banned. Ban was synced to other servers."], ["IP ", " has been banned. Ban was synced to other servers."])), ip)); + } + (0, utils_1.updateBans)(function (player) { return "[scarlet]Player [yellow]".concat(player.name, "[scarlet] has been whacked by ").concat(sender.prefixedName, "."); }); + return [2 /*return*/]; + case 4: return [4 /*yield*/, menus_1.Menu.menu("[scarlet]BAN[]", "Choose a player to ban.", (0, funcs_1.setToArray)(Groups.player), sender, { + includeCancel: true, + optionStringifier: function (opt) { return opt.name; } + })]; + case 5: + option = _c.sent(); + if (option.admin) + (0, commands_1.fail)("Cannot ban an admin."); + return [4 /*yield*/, menus_1.Menu.confirmDangerous(sender, "Are you sure you want to ban ".concat(option.name, "?"))]; + case 6: + _c.sent(); + admins.banPlayerIP(option.ip()); //this also bans the UUID + api.ban({ ip: option.ip(), uuid: option.uuid() }); + Log.info("".concat(option.ip(), "/").concat(option.uuid(), " was banned.")); + (0, utils_1.logAction)("banned", sender, option.getInfo()); + outputSuccess(f(templateObject_36 || (templateObject_36 = __makeTemplateObject(["Banned player ", "."], ["Banned player ", "."])), option)); + (0, utils_1.updateBans)(function (player) { return "[scarlet]Player [yellow]".concat(player.name, "[scarlet] has been whacked by ").concat(sender.prefixedName, "."); }); + return [2 /*return*/]; + } + }); + }); + } + }, + kill: { + args: ["player:player"], + description: "Kills a player's unit.", + perm: commands_1.Perm.admin, + requirements: [commands_1.Req.moderate("player", true)], + handler: function (_a) { + var args = _a.args, outputFail = _a.outputFail, outputSuccess = _a.outputSuccess, f = _a.f; + var unit = args.player.unit(); + if (unit) { + unit.kill(); + outputSuccess(f(templateObject_37 || (templateObject_37 = __makeTemplateObject(["Killed the unit of player ", "."], ["Killed the unit of player ", "."])), args.player)); + } + else { + outputFail(f(templateObject_38 || (templateObject_38 = __makeTemplateObject(["Player ", " does not have a unit."], ["Player ", " does not have a unit."])), args.player)); + } + } + }, + killunits: { + args: ["team:team?", "unit:unittype?"], + description: "Kills all units, optionally specifying a team and unit type.", + perm: commands_1.Perm.massKill, + handler: function (_a) { + return __awaiter(this, arguments, void 0, function (_b) { + var i_1, before, i_2, before; + var _c = _b.args, team = _c.team, unit = _c.unit, sender = _b.sender, outputSuccess = _b.outputSuccess, f = _b.f; + return __generator(this, function (_d) { + switch (_d.label) { + case 0: + if (!team) return [3 /*break*/, 2]; + return [4 /*yield*/, menus_1.Menu.confirmDangerous(sender, "This will kill [scarlet]every ".concat(unit ? unit.localizedName : "unit", "[] on the team ").concat(team.coloredName(), "."), { confirmText: "[orange]Kill units[]" })]; + case 1: + _d.sent(); + if (unit) { + i_1 = 0; + team.data().units.each(function (u) { return u.type == unit; }, function (u) { + u.kill(); + i_1++; + }); + outputSuccess(f(templateObject_39 || (templateObject_39 = __makeTemplateObject(["Killed ", " units on ", "."], ["Killed ", " units on ", "."])), i_1, team)); + } + else { + before = team.data().units.size; + team.data().units.each(function (u) { return u.kill(); }); + outputSuccess(f(templateObject_40 || (templateObject_40 = __makeTemplateObject(["Killed ", " units on ", "."], ["Killed ", " units on ", "."])), before, team)); + } + return [3 /*break*/, 4]; + case 2: return [4 /*yield*/, menus_1.Menu.confirmDangerous(sender, "This will kill [scarlet]every single ".concat(unit ? unit.localizedName : "unit", "[]."), { confirmText: "[orange]Kill all units[]" })]; + case 3: + _d.sent(); + if (unit) { + i_2 = 0; + Groups.unit.each(function (u) { return u.type == unit; }, function (u) { + u.kill(); + i_2++; + }); + outputSuccess(f(templateObject_41 || (templateObject_41 = __makeTemplateObject(["Killed ", " units."], ["Killed ", " units."])), i_2)); + } + else { + before = Groups.unit.size(); + Groups.unit.each(function (u) { return u.kill(); }); + outputSuccess(f(templateObject_42 || (templateObject_42 = __makeTemplateObject(["Killed ", " units."], ["Killed ", " units."])), before)); + } + _d.label = 4; + case 4: return [2 /*return*/]; + } + }); + }); + } + }, + killbuildings: { + args: ["team:team?"], + description: "Kills all buildings (except cores), optionally specifying a team.", + perm: commands_1.Perm.massKill, + handler: function (_a) { + return __awaiter(this, arguments, void 0, function (_b) { + var count, count; + var team = _b.args.team, sender = _b.sender, outputSuccess = _b.outputSuccess, f = _b.f; + return __generator(this, function (_c) { + switch (_c.label) { + case 0: + if (!team) return [3 /*break*/, 2]; + return [4 /*yield*/, menus_1.Menu.confirmDangerous(sender, "This will kill [scarlet]every building[] on the team ".concat(team.coloredName(), ", except cores."), { confirmText: "[orange]Kill buildings[]" })]; + case 1: + _c.sent(); + count = team.data().buildings.size; + team.data().buildings.each(function (b) { return !(b.block instanceof CoreBlock); }, function (b) { return b.tile.remove(); }); + outputSuccess(f(templateObject_43 || (templateObject_43 = __makeTemplateObject(["Killed ", " buildings on ", "."], ["Killed ", " buildings on ", "."])), count, team)); + return [3 /*break*/, 4]; + case 2: return [4 /*yield*/, menus_1.Menu.confirmDangerous(sender, "This will kill [scarlet]every building[] except cores.", { confirmText: "[orange]Kill buildings[]" })]; + case 3: + _c.sent(); + count = Groups.build.size(); + Groups.build.each(function (b) { return !(b.block instanceof CoreBlock); }, function (b) { return b.tile.remove(); }); + outputSuccess(f(templateObject_44 || (templateObject_44 = __makeTemplateObject(["Killed ", " buildings."], ["Killed ", " buildings."])), count)); + _c.label = 4; + case 4: return [2 /*return*/]; + } + }); + }); + } + }, + respawn: { + args: ["player:player"], + description: "Forces a player to respawn.", + perm: commands_1.Perm.mod, + requirements: [commands_1.Req.moderate("player", true, "mod", true)], + handler: function (_a) { + var args = _a.args, outputSuccess = _a.outputSuccess, f = _a.f; + args.player.forceRespawn(); + outputSuccess(f(templateObject_45 || (templateObject_45 = __makeTemplateObject(["Respawned player ", "."], ["Respawned player ", "."])), args.player)); + } + }, + clearunit: { + args: ["target:player", "duration:time?"], + description: "Forces a player out of the unit they are controlling, and blocks them from possessing units for a specified duration.", + perm: commands_1.Perm.mod, + requirements: [commands_1.Req.moderate("target", false, "mod", false)], + handler: function (_a) { + var _b = _a.args, target = _b.target, duration = _b.duration, sender = _a.sender, outputSuccess = _a.outputSuccess, f = _a.f; + if (Date.now() > 1000 + target.blockedFromPossessingUnitsUntil) + duration !== null && duration !== void 0 ? duration : (duration = funcs_1.Duration.minutes(1)); + else + duration !== null && duration !== void 0 ? duration : (duration = 0); + if (duration == 0) { + target.blockedFromPossessingUnitsUntil = 0; + target.sendMessage("You are allowed to control units again."); + outputSuccess(f(templateObject_46 || (templateObject_46 = __makeTemplateObject(["Restored ", "'s ability to control units."], ["Restored ", "'s ability to control units."])), target)); + (0, utils_1.logAction)("restored unit possession for", sender, target); + } + else { + target.forceRespawn(); + target.blockedFromPossessingUnitsUntil = Date.now() + duration; + target.sendMessage("You have been blocked from controlling units for ".concat((0, utils_1.formatTime)(duration), ".")); + outputSuccess(f(templateObject_47 || (templateObject_47 = __makeTemplateObject(["Blocked ", " from controlling units for ", "."], ["Blocked ", " from controlling units for ", "."])), target, (0, utils_1.formatTime)(duration))); + (0, utils_1.logAction)("revoked unit possession for", sender, target, undefined, duration); + } + } + }, + clearcommand: { + args: ["target:player", "duration:time?"], + description: "Blocks a player from commanding units for a specified duration.", + perm: commands_1.Perm.mod, + requirements: [commands_1.Req.moderate("target", false, "mod", false)], + handler: function (_a) { + var _b = _a.args, target = _b.target, duration = _b.duration, sender = _a.sender, outputSuccess = _a.outputSuccess, f = _a.f; + if (Date.now() > 1000 + target.blockedFromCommandingUnitsUntil) + duration !== null && duration !== void 0 ? duration : (duration = funcs_1.Duration.minutes(1)); + else + duration !== null && duration !== void 0 ? duration : (duration = 0); + if (duration == 0) { + target.blockedFromCommandingUnitsUntil = 0; + target.sendMessage("You are allowed to command units again."); + outputSuccess(f(templateObject_48 || (templateObject_48 = __makeTemplateObject(["Restored ", "'s ability to command units."], ["Restored ", "'s ability to command units."])), target)); + (0, utils_1.logAction)("restored command mode for", sender, target, undefined, duration); + } + else { + target.blockedFromCommandingUnitsUntil = Date.now() + duration; + target.sendMessage("You have been blocked from commanding units for ".concat((0, utils_1.formatTime)(duration), ".")); + outputSuccess(f(templateObject_49 || (templateObject_49 = __makeTemplateObject(["Blocked ", " from commanding units for ", "."], ["Blocked ", " from commanding units for ", "."])), target, (0, utils_1.formatTime)(duration))); + (0, utils_1.logAction)("revoked command mode for", sender, target, undefined, duration); + } + } + }, + stealunit: { + args: ["target:player", "newcontroller:player?"], + description: "Steals the unit of a player, putting you in their unit and forcing them to respawn.", + perm: commands_1.Perm.mod, + requirements: [commands_1.Req.moderate("target", true, "mod", true), commands_1.Req.moderate("newcontroller", true, "mod", true)], + handler: function (_a) { + var _b; + var sender = _a.sender, _c = _a.args, target = _c.target, _d = _c.newcontroller, newcontroller = _d === void 0 ? sender : _d, outputSuccess = _a.outputSuccess, f = _a.f; + var unit = (_b = target.unit()) !== null && _b !== void 0 ? _b : (0, commands_1.fail)(f(templateObject_50 || (templateObject_50 = __makeTemplateObject(["Targeted player ", " is not in a unit."], ["Targeted player ", " is not in a unit."])), target)); + if (target.team() !== newcontroller.team()) { + if (!sender.hasPerm("changeTeamExternal")) { + if (!sender.hasPerm("changeTeam")) + (0, commands_1.fail)("You do not have permission to change teams."); + newcontroller.setTeam(unit.team); + } + } + target.forceRespawn(); + newcontroller.unit(unit); + if (newcontroller == sender) { + outputSuccess(f(templateObject_51 || (templateObject_51 = __makeTemplateObject(["Commandeered the unit of player ", "."], ["Commandeered the unit of player ", "."])), target)); + } + else { + outputSuccess(f(templateObject_52 || (templateObject_52 = __makeTemplateObject(["Transferred player ", " into the unit of ", "."], ["Transferred player ", " into the unit of ", "."])), newcontroller, target)); + newcontroller.sendMessage(f(templateObject_53 || (templateObject_53 = __makeTemplateObject(["[green]You were transferred to the unit of player ", " by ", "."], ["[green]You were transferred to the unit of player ", " by ", "."])), target, sender)('[green]')); + } + } + }, + m: { + args: ["message:string"], + description: "Sends a message to muted players only.", + perm: commands_1.Perm.mod, + handler: function (_a) { + var sender = _a.sender, args = _a.args; + players_1.FishPlayer.messageMuted(sender.prefixedName, args.message); + } + }, + info: { + args: ["target:player", "showColors:boolean?"], + description: "Displays information about an online player.", + perm: commands_1.Perm.none, + handler: function (_a) { + var sender = _a.sender, args = _a.args, output = _a.output, f = _a.f; + var info = args.target.info(); + var names = args.showColors + ? info.names.map(funcs_1.escapeStringColorsClient).toString(", ") + : __spreadArray([], __read(new Set(info.names.map(function (n) { return Strings.stripColors(n); }).toArray())), false).join(", "); + output(f(templateObject_54 || (templateObject_54 = __makeTemplateObject(["[accent]Info for player ", " [gray](", ") (#", ")\n\t[accent]Rank: ", "\n\t[accent]Role flags: ", "\n\t[accent]Stopped: ", "\n\t[accent]marked: ", "\n\t[accent]muted: ", "\n\t[accent]autoflagged: ", "\n\t[accent]VPN detected: ", "\n\t[accent]times joined / kicked: ", "/", "\n\t[accent]First joined: ", "\n\t[accent]Names used: [[", "]"], ["\\\n[accent]Info for player ", " [gray](", ") (#", ")\n\t[accent]Rank: ", "\n\t[accent]Role flags: ", "\n\t[accent]Stopped: ", "\n\t[accent]marked: ", "\n\t[accent]muted: ", "\n\t[accent]autoflagged: ", "\n\t[accent]VPN detected: ", "\n\t[accent]times joined / kicked: ", "/", "\n\t[accent]First joined: ", "\n\t[accent]Names used: [[", "]"])), args.target, (0, funcs_1.escapeStringColorsClient)(args.target.name), args.target.player.id.toString(), args.target.rank, Array.from(args.target.flags).map(function (f) { return f.coloredName(); }).join(" "), f.boolBad(!args.target.hasPerm("play")), args.target.marked() ? "until ".concat((0, utils_1.formatTimeRelative)(args.target.unmarkTime)) : "[green]false", f.boolBad(args.target.muted), f.boolBad(args.target.autoflagged), f.boolBad(args.target.ipDetectedVpn), info.timesJoined, info.timesKicked, args.target.firstJoined < 1 ? "unknown" : (0, utils_1.formatTimeRelative)(args.target.firstJoined), names)); + if (sender.hasPerm("viewUUIDs")) + output(f(templateObject_55 || (templateObject_55 = __makeTemplateObject(["\t[#FFAAAA]UUID: ", ""], ["\\t[#FFAAAA]UUID: ", ""])), args.target.uuid)); + if (sender.hasPerm("viewIPs")) + output(f(templateObject_56 || (templateObject_56 = __makeTemplateObject(["\t[#FFAAAA]IP: ", ""], ["\\t[#FFAAAA]IP: ", ""])), args.target.ip())); + } + }, + spawn: { + args: ["type:unittype", "x:number?", "y:number?", "count:number?", "team:team?", "effects:string?", "stack:boolean?"], + description: "Spawns a unit of specified type at your position. [scarlet]Usage will be logged.[]", + perm: commands_1.Perm.admin.exceptModes({ + testsrv: commands_1.Perm.trusted, + }), + data: [], + requirements: [commands_1.Req.positiveInteger("count")], + handler: function (_a) { + var _b, _c; + var sender = _a.sender, args = _a.args, data = _a.data, outputSuccess = _a.outputSuccess, f = _a.f; + var x = args.x ? (args.x * 8) : sender.player.x; + var y = args.y ? (args.y * 8) : sender.player.y; + var team = (_b = args.team) !== null && _b !== void 0 ? _b : sender.team(); + var count = Math.min((_c = args.count) !== null && _c !== void 0 ? _c : 1, 1000); + for (var i = 0; i < count; i++) { + var unit = args.type.create(team); + var xOffset = args.stack ? 0 : 0.01 * i; + var yOffset = args.stack ? 0 : 0.5 * (i % 10); + unit.set(x + xOffset, y + yOffset); + if (args.effects) + (0, utils_1.applyEffectMode)(args.effects, unit, 1e12); + unit.add(); + data.push(unit); + } + if (!(config_1.Gamemode.sandbox() || config_1.Gamemode.testsrv()) && args.effects !== 'paper') + (0, utils_1.logAction)("spawned unit ".concat(args.type.name).concat(count == 1 ? '' : " x".concat(count), " at ").concat(Math.round(x / 8), ", ").concat(Math.round(y / 8)) + (args.effects ? "with ".concat(args.effects, " effects") : ''), sender); + outputSuccess(f(templateObject_57 || (templateObject_57 = __makeTemplateObject(["Spawned unit ", " at (", ", ", ")"], ["Spawned unit ", " at (", ", ", ")"])), args.type, Math.round(x / 8), Math.round(y / 8))); + } + }, + setblock: { + args: ["x:number", "y:number", "block:block", "team:team?", "rotation:number?"], + description: "Sets the block at a location.", + perm: commands_1.Perm.admin.exceptModes({ + testsrv: commands_1.Perm.trusted, + }), + requirements: [commands_1.Req.integerRange("rotation", 0, 3)], + handler: function (_a) { + var _b, _c; + var args = _a.args, sender = _a.sender, outputSuccess = _a.outputSuccess, f = _a.f; + var team = (_b = args.team) !== null && _b !== void 0 ? _b : sender.team(); + var tile = Vars.world.tile(args.x, args.y); + if (tile == null) + (0, commands_1.fail)(f(templateObject_58 || (templateObject_58 = __makeTemplateObject(["Position (", ", ", ") is out of bounds."], ["Position (", ", ", ") is out of bounds."])), args.x, args.y)); + tile.setNet(args.block, team, (_c = args.rotation) !== null && _c !== void 0 ? _c : 0); + (0, utils_1.addToTileHistory)({ + pos: "".concat(args.x, ",").concat(args.y), + uuid: sender.uuid, + action: "setblocked", + type: args.block.localizedName + }); + if (!(config_1.Gamemode.sandbox() || config_1.Gamemode.testsrv())) + (0, utils_1.logAction)("set block to ".concat(args.block.localizedName, " at ").concat(args.x, ",").concat(args.y), sender); + outputSuccess(f(templateObject_59 || (templateObject_59 = __makeTemplateObject(["Set block at ", ", ", " to ", ""], ["Set block at ", ", ", " to ", ""])), args.x, args.y, args.block)); + } + }, + setblockr: { + args: ["block:block?", "team:team?", "rotation:number?"], + description: "Sets the block at tapped locations, repeatedly.", + perm: commands_1.Perm.admin, + requirements: [commands_1.Req.integerRange("rotation", 0, 3)], + tapped: function (_a) { + var _b, _c; + var args = _a.args, sender = _a.sender, f = _a.f, x = _a.x, y = _a.y, outputSuccess = _a.outputSuccess; + if (!args.block) + (0, funcs_1.crash)("uh oh"); + var team = (_b = args.team) !== null && _b !== void 0 ? _b : sender.team(); + var tile = Vars.world.tile(x, y); + if (tile == null) + (0, commands_1.fail)(f(templateObject_60 || (templateObject_60 = __makeTemplateObject(["Position (", ", ", ") is out of bounds."], ["Position (", ", ", ") is out of bounds."])), x, y)); + tile.setNet(args.block, team, (_c = args.rotation) !== null && _c !== void 0 ? _c : 0); + (0, utils_1.addToTileHistory)({ + pos: "".concat(x, ",").concat(y), + uuid: sender.uuid, + action: "setblocked", + type: args.block.localizedName + }); + if (!(config_1.Gamemode.sandbox() || config_1.Gamemode.testsrv())) + (0, utils_1.logAction)("set block to ".concat(args.block.localizedName, " at ").concat(x, ",").concat(y), sender); + outputSuccess(f(templateObject_61 || (templateObject_61 = __makeTemplateObject(["Set block at ", ", ", " to ", ""], ["Set block at ", ", ", " to ", ""])), x, y, args.block)); + }, + handler: function (_a) { + var args = _a.args, outputSuccess = _a.outputSuccess, handleTaps = _a.handleTaps, currentTapMode = _a.currentTapMode, f = _a.f; + if (args.block) { + handleTaps("on"); + if (currentTapMode == "off") { + outputSuccess("setblockr enabled.\n[scarlet]Be careful, you have the midas touch now![] Turn it off by running /setblockr again."); + } + else { + outputSuccess(f(templateObject_62 || (templateObject_62 = __makeTemplateObject(["Changed setblockr's block to ", ""], ["Changed setblockr's block to ", ""])), args.block)); + } + } + else { + if (currentTapMode == "off") { + (0, commands_1.fail)("Please specify the block to place."); + } + else { + handleTaps("off"); + outputSuccess("setblockr disabled."); + } + } + } + }, + exterminate: { + args: [], + description: "Removes all spawned units.", + perm: commands_1.Perm.admin.exceptModes({ + testsrv: commands_1.Perm.trusted, + }), + handler: function (_a) { + var sender = _a.sender, outputSuccess = _a.outputSuccess, f = _a.f, allCommands = _a.allCommands; + var numKilled = 0; + allCommands.spawn.data.forEach(function (u) { + if (u.isAdded() && !u.dead) { + u.kill(); + numKilled++; + } + }); + if (!config_1.Gamemode.sandbox()) + (0, utils_1.logAction)("exterminated ".concat(numKilled, " units"), sender); + outputSuccess(f(templateObject_63 || (templateObject_63 = __makeTemplateObject(["Exterminated ", " units."], ["Exterminated ", " units."])), numKilled)); + } + }, + js: { + args: ["javascript:string"], + description: "Run arbitrary javascript.", + perm: commands_1.Perm.runJS, + customUnauthorizedMessage: "[scarlet]You are not in the jsers file. This incident will be reported.[]", + handler: function (_a) { + var javascript = _a.args.javascript, output = _a.output, outputFail = _a.outputFail, sender = _a.sender; + //Additional validation couldn't hurt... + var playerInfo_AdminUsid = sender.info().adminUsid; + if (!playerInfo_AdminUsid || playerInfo_AdminUsid != sender.player.usid() || sender.usid != sender.player.usid()) { + api.sendModerationMessage("# !!!!! /js authentication failed !!!!!\nServer: ".concat(config_1.Gamemode.name(), " Player: ").concat((0, funcs_1.escapeTextDiscord)(sender.cleanedName), "/`").concat(sender.uuid, "`\n<@!709904412033810533>")); + (0, commands_1.fail)("Authentication failure"); + } + if (javascript == "Timer.instance().clear()") + (0, commands_1.fail)("Are you really sure you want to do that? If so, prepend \"void\" to your command."); + try { + var scripts = Vars.mods.getScripts(); + var out = scripts.context.evaluateString(scripts.scope, javascript, "fish-js-console.js", 1); + if (out instanceof Array) { + output("[cyan]Array: [[[]" + out.join(", ") + "[cyan]]"); + } + else if (out === undefined) { + output("[blue]undefined[]"); + } + else if (out === null) { + output("[blue]null[]"); + } + else if (out instanceof Error) { + outputFail((0, funcs_1.parseError)(out)); + } + else if (typeof out == "number") { + output("[blue]".concat(out, "[]")); + } + else { + output(out); + } + } + catch (err) { + outputFail((0, funcs_1.parseError)(err)); + } + } + }, + fjs: { + args: ["javascript:string"], + description: "Run arbitrary javascript in the fish-commands context.", + perm: commands_1.Perm.runJS, + customUnauthorizedMessage: "[scarlet]You are not in the jsers file. This incident will be reported.[]", + handler: function (_a) { + var javascript = _a.args.javascript, output = _a.output, outputFail = _a.outputFail, sender = _a.sender; + //Additional validation couldn't hurt... + var playerInfo_AdminUsid = sender.info().adminUsid; + if (!playerInfo_AdminUsid || playerInfo_AdminUsid != sender.player.usid() || sender.usid != sender.player.usid()) { + api.sendModerationMessage("# !!!!! /js authentication failed !!!!!\nServer: ".concat(config_1.Gamemode.name(), " Player: ").concat((0, funcs_1.escapeTextDiscord)(sender.cleanedName), "/`").concat(sender.uuid, "`\n<@!709904412033810533>")); + (0, commands_1.fail)("Authentication failure"); + } + fjsContext.runJS(javascript, output, outputFail, sender); + } + }, + antibot: { + args: ["timeout:time?"], + description: "Checks anti bot stats, or force enables anti bot mode.", + perm: commands_1.Perm.mod, + handler: function (_a) { + var args = _a.args, sender = _a.sender, outputSuccess = _a.outputSuccess, output = _a.output, f = _a.f; + if (args.timeout != undefined) { + args.timeout = Math.min(args.timeout, sender.hasPerm("admin") ? funcs_1.Duration.hours(1) : funcs_1.Duration.minutes(10)); + players_1.FishPlayer.triggerAntibot(args.timeout, "Manually triggered by player ".concat(sender.name), "manual"); + outputSuccess("Set antibot mode override for ".concat((0, utils_1.formatTime)(args.timeout), ".")); + } + else { + output("[acid]Antibot status:\n[acid]Enabled: ".concat(f.boolBad(players_1.FishPlayer.antiBotMode()), "\n").concat((0, utils_1.getAntiBotInfo)("client"))); + } + } + }, + chatstrictness: { + args: ["player:player", "value:string"], + description: "Sets chat strictness for a player.", + perm: commands_1.Perm.mod, + handler: function (_a) { + var _b = _a.args, player = _b.player, value = _b.value, sender = _a.sender, outputSuccess = _a.outputSuccess, f = _a.f; + if (!sender.canModerate(player, true)) + (0, commands_1.fail)("You do not have permission to set the chat strictness level of this player."); + if (!(value == "chat" || value == "strict")) + (0, commands_1.fail)("Invalid chat strictness level: valid levels are \"chat\", \"strict\""); + player.chatStrictness = value; + (0, utils_1.logAction)("set chat strictness to ".concat(value, " for"), sender, player); + outputSuccess(f(templateObject_64 || (templateObject_64 = __makeTemplateObject(["Set chat strictness for player ", " to \"", "\"."], ["Set chat strictness for player ", " to \"", "\"."])), player, value)); + } + }, + emanate: (0, commands_1.command)(function () { + var unitMapping = {}; + Timer.schedule(function () { + var e_1, _a; + try { + for (var _b = __values(Object.entries(unitMapping)), _c = _b.next(); !_c.done; _c = _b.next()) { + var _d = __read(_c.value, 2), uuid = _d[0], unit = _d[1]; + var fishP = players_1.FishPlayer.getById(uuid); + if (!fishP || !fishP.connected() || (unit.getPlayer() != fishP.player)) { + delete unitMapping[uuid]; + unit === null || unit === void 0 ? void 0 : unit.kill(); + } + } + } + catch (e_1_1) { e_1 = { error: e_1_1 }; } + finally { + try { + if (_c && !_c.done && (_a = _b.return)) _a.call(_b); + } + finally { if (e_1) throw e_1.error; } + } + }, 1, 0.5); + return { + args: [], + description: "Puts you in an emanate.", + perm: commands_1.Perm.admin, + data: { unitMapping: unitMapping }, + requirements: [], + handler: function (_a) { + var sender = _a.sender, outputSuccess = _a.outputSuccess; + var emanate = UnitTypes.emanate.spawn(sender.team(), sender.player.x, sender.player.y); + sender.player.unit(emanate); + unitMapping[sender.uuid] = emanate; + if (!config_1.Gamemode.sandbox()) + (0, utils_1.logAction)("spawned an emanate", sender); + outputSuccess("Spawned an emanate."); + } + }; + }), + updatemaps: { + args: [], + description: 'Attempt to fetch and update all map files', + perm: commands_1.Perm.trusted, + requirements: function (_a) { + var sender = _a.sender; + return [commands_1.Req.cooldownGlobal(config_1.Gamemode.testsrv() || sender.hasPerm("mod") ? 15000 : funcs_1.Duration.minutes(5))]; + }, + handler: function (_a) { + var output = _a.output, outputSuccess = _a.outputSuccess, outputFail = _a.outputFail; + output("Updating maps... (this may take a while)"); + (0, files_1.updateMaps)() + .then(function (changed) { + Log.info("Maps updated."); + if (changed) { + outputSuccess("Map update completed."); + Call.sendMessage("[orange]Maps have been updated. Run [white]/maps[] to view available maps."); + } + else { + outputSuccess("Map update completed; already up to date."); + } + }) + .catch(function (message) { + outputFail("Map update failed: ".concat(String(message))); + Log.err("Map updates failed: ".concat(String(message))); + }); + } + }, + clearfire: { + args: [], + description: "Clears all the fires.", + perm: commands_1.Perm.admin, + handler: function (_a) { + var output = _a.output, outputSuccess = _a.outputSuccess; + output("Removing fires..."); + var totalRemoved = 0; + Call.sendMessage("[scarlet][[Fire Department]:[yellow] Fires were reported. Trucks are en-route. Removing all fires shortly."); + Timer.schedule(function () { + totalRemoved += Groups.fire.size(); + Groups.fire.each(function (f) { return f.remove(); }); + Groups.fire.clear(); + }, 2, 0.1, 40); + Timer.schedule(function () { + outputSuccess("Removed ".concat(totalRemoved, " fires.")); + Call.sendMessage("[scarlet][[Fire Department]:[yellow] We've extinguished ".concat(totalRemoved, " fires.")); + }, 6.1); + } + }, + search: { + args: ["input:string"], + description: "Searches playerinfo by name, IP, or UUID.", + perm: commands_1.Perm.admin, + handler: function (_a) { + return __awaiter(this, arguments, void 0, function (_b) { + var fishP, info, matches, matches_1, displayMatches; + var input = _b.args.input, admins = _b.admins, output = _b.output, f = _b.f, sender = _b.sender; + return __generator(this, function (_c) { + switch (_c.label) { + case 0: + if (!globals_1.uuidPattern.test(input)) return [3 /*break*/, 1]; + fishP = players_1.FishPlayer.getById(input); + info = admins.getInfoOptional(input); + if (fishP == null && info == null) + (0, commands_1.fail)(f(templateObject_65 || (templateObject_65 = __makeTemplateObject(["No stored data matched uuid ", "."], ["No stored data matched uuid ", "."])), input)); + else if (fishP == null && info) + output(f(templateObject_66 || (templateObject_66 = __makeTemplateObject(["[accent]Found player info (but no fish player data) for uuid ", "\nLast name used: \"", "\" [gray](", ")[] [[", "]\nIPs used: ", ""], ["[accent]\\\nFound player info (but no fish player data) for uuid ", "\nLast name used: \"", "\" [gray](", ")[] [[", "]\nIPs used: ", ""])), input, info.plainLastName(), (0, funcs_1.escapeStringColorsClient)(info.lastName), info.names.map(funcs_1.escapeStringColorsClient).items.join(", "), info.ips.map(function (i) { return "[blue]".concat(i, "[]"); }).toString(", "))); + else if (fishP && info) + output(f(templateObject_67 || (templateObject_67 = __makeTemplateObject(["[accent]Found fish player data for uuid ", "\nLast name used: \"", "\" [gray](", ")[] [[", "]\nIPs used: ", ""], ["[accent]\\\nFound fish player data for uuid ", "\nLast name used: \"", "\" [gray](", ")[] [[", "]\nIPs used: ", ""])), input, fishP.name, (0, funcs_1.escapeStringColorsClient)(info.lastName), info.names.map(funcs_1.escapeStringColorsClient).items.join(", "), info.ips.map(function (i) { return "[blue]".concat(i, "[]"); }).toString(", "))); + else + (0, commands_1.fail)(f(templateObject_68 || (templateObject_68 = __makeTemplateObject(["Super weird edge case: found fish player data but no player info for uuid ", "."], ["Super weird edge case: found fish player data but no player info for uuid ", "."])), input)); + return [3 /*break*/, 5]; + case 1: + if (!globals_1.ipPattern.test(input)) return [3 /*break*/, 2]; + matches = admins.findByIPs(input); + if (matches.isEmpty()) + (0, commands_1.fail)(f(templateObject_69 || (templateObject_69 = __makeTemplateObject(["No stored data matched IP ", ""], ["No stored data matched IP ", ""])), input)); + output(f(templateObject_70 || (templateObject_70 = __makeTemplateObject(["[accent]Found ", " match", " for search \"", "\"."], ["[accent]Found ", " match", " for search \"", "\"."])), matches.size, matches.size == 1 ? "" : "es", input)); + matches.each(function (info) { return output(f(templateObject_71 || (templateObject_71 = __makeTemplateObject(["[accent]Player with uuid ", "\nLast name used: \"", "\" [gray](", ")[] [[", "]\nIPs used: ", ""], ["[accent]\\\nPlayer with uuid ", "\nLast name used: \"", "\" [gray](", ")[] [[", "]\nIPs used: ", ""])), info.id, info.plainLastName(), (0, funcs_1.escapeStringColorsClient)(info.lastName), info.names.map(funcs_1.escapeStringColorsClient).items.join(", "), info.ips.map(function (i) { return "[blue]".concat(i, "[]"); }).toString(", "))); }); + return [3 /*break*/, 5]; + case 2: + matches_1 = Vars.netServer.admins.searchNames(input); + if (matches_1.isEmpty()) + (0, commands_1.fail)(f(templateObject_72 || (templateObject_72 = __makeTemplateObject(["No stored data matched name ", ""], ["No stored data matched name ", ""])), input)); + output(f(templateObject_73 || (templateObject_73 = __makeTemplateObject(["[accent]Found ", " match", " for search \"", "\"."], ["[accent]Found ", " match", " for search \"", "\"."])), matches_1.size, matches_1.size == 1 ? "" : "es", input)); + displayMatches = function () { + matches_1.each(function (info) { return output(f(templateObject_74 || (templateObject_74 = __makeTemplateObject(["[accent]Player with uuid ", "\nLast name used: \"", "\" [gray](", ")[] [[", "]\nIPs used: ", ""], ["[accent]\\\nPlayer with uuid ", "\nLast name used: \"", "\" [gray](", ")[] [[", "]\nIPs used: ", ""])), info.id, info.plainLastName(), (0, funcs_1.escapeStringColorsClient)(info.lastName), info.names.map(funcs_1.escapeStringColorsClient).items.join(", "), info.ips.map(function (i) { return "[blue]".concat(i, "[]"); }).toString(", "))); }); + }; + if (!(matches_1.size > 20)) return [3 /*break*/, 4]; + return [4 /*yield*/, menus_1.Menu.confirm(sender, "Are you sure you want to view all ".concat(matches_1.size, " matches?"))]; + case 3: + _c.sent(); + _c.label = 4; + case 4: + displayMatches(); + _c.label = 5; + case 5: return [2 /*return*/]; + } + }); + }); + } + }, + peace: { + args: ["peace:boolean"], + description: "Toggles peaceful mode for sandbox.", + perm: commands_1.Perm.mod, + requirements: [commands_1.Req.mode('sandbox')], + handler: function (_a) { + var args = _a.args; + if (args.peace) { + globals_1.fishState.peacefulMode = true; + Groups.player.each(function (p) { + if (p.team() != Vars.state.rules.defaultTeam) { + p.team(Vars.state.rules.defaultTeam); + } + }); + Call.sendMessage("[[Sandbox] [green]Enabled peaceful mode."); + } + else { + globals_1.fishState.peacefulMode = false; + Call.sendMessage("[[Sandbox] [red]Disabled peaceful mode."); + } + }, + }, + effects: { + args: ["mode:string", "player:player?", "duration:time?"], + description: "Applies effects to a player's unit.", + perm: commands_1.Perm.admin.exceptModes({ + testsrv: commands_1.Perm.trusted, + }), + handler: function (_a) { + var _b, _c, _d; + var args = _a.args, sender = _a.sender, f = _a.f, outputSuccess = _a.outputSuccess; + if ((_b = args.player) === null || _b === void 0 ? void 0 : _b.hasPerm("blockTrolling")) + (0, commands_1.fail)(f(templateObject_75 || (templateObject_75 = __makeTemplateObject(["Player ", " is insufficiently trollable."], ["Player ", " is insufficiently trollable."])), args.player)); + if (args.player && !sender.canModerate(args.player, false)) + (0, commands_1.fail)("You do not have permission to perform moderation actions on this player."); + var target = (_c = args.player) !== null && _c !== void 0 ? _c : sender; + var unit = target.unit(); + if (!unit || unit.dead) + (0, commands_1.fail)(f(templateObject_76 || (templateObject_76 = __makeTemplateObject(["", "'s unit is dead."], ["", "'s unit is dead."])), target)); + var ticks = ((_d = args.duration) !== null && _d !== void 0 ? _d : 1e12) / 1000 * 60; + (0, utils_1.applyEffectMode)(args.mode, unit, ticks); + outputSuccess("".concat(args.mode === "clear" ? "Cleared" : "Applied", " effects.")); + if (!config_1.Gamemode.sandbox()) + (0, utils_1.logAction)("applied **".concat(args.mode, "** effects to"), sender, target); + } + }, + items: { + args: ["team:team", "item:item", "amount:number"], + description: "Gives items to a team.", + perm: commands_1.Perm.admin, + requirements: [commands_1.Req.integer("amount")], + handler: function (_a) { + var _b; + var _c = _a.args, team = _c.team, item = _c.item, amount = _c.amount, sender = _a.sender, outputSuccess = _a.outputSuccess, f = _a.f; + var core = (_b = team.data().cores.firstOpt()) !== null && _b !== void 0 ? _b : (0, commands_1.fail)(f(templateObject_77 || (templateObject_77 = __makeTemplateObject(["Team ", " has no cores."], ["Team ", " has no cores."])), team)); + core.items.add(item, amount); + outputSuccess(f(templateObject_78 || (templateObject_78 = __makeTemplateObject(["Gave ", " ", " to ", "."], ["Gave ", " ", " to ", "."])), amount, item, team)); + if (!config_1.Gamemode.sandbox()) + (0, utils_1.logAction)("gave ".concat(amount, " ").concat(item.localizedName.toLowerCase(), " to ").concat(team.name), sender); + } + }, + explosion: { + args: ["radius:number", "x:number", "y:number", "team:team?", "damage:number?", "damageMode:string?"], + description: "Causes an explosion at specified coordinates.", + perm: commands_1.Perm.admin, + handler: function (_a) { + var _b; + var _c = _a.args, radius = _c.radius, x = _c.x, y = _c.y, _d = _c.team, team = _d === void 0 ? Team.derelict : _d, _e = _c.damage, damage = _e === void 0 ? 1e12 : _e, _f = _c.damageMode, damageMode = _f === void 0 ? "both" : _f, outputSuccess = _a.outputSuccess; + var _g = __read((_b = (0, utils_1.match)(damageMode, { + air: [true, false], + ground: [false, true], + both: [true, true], + none: [false, false], + })) !== null && _b !== void 0 ? _b : (0, commands_1.fail)("Valid values of damageMode: air, ground, both, none"), 2), air = _g[0], ground = _g[1]; + if (radius > 100) + (0, commands_1.fail)("Maximum radius is 100"); + if (damage < 0) + Call.effect(Fx.dynamicSpikes, x * 8, y * 8, radius * 8, Pal.heal); + else + Call.effect(Fx.dynamicExplosion, x * 8, y * 8, Math.max(radius, 8) / 7, Color.white); + Damage.damage(team, x * 8, y * 8, radius * 8, damage, true, air, ground); + outputSuccess("Created an explosion at (".concat(x, ", ").concat(y, ").")); + } + }, + memorycorruption: { + args: [], + description: "Triggers a fake memory corruption prank.", + perm: commands_1.Perm.mod, + requirements: [commands_1.Req.cooldownGlobal(funcs_1.Duration.minutes(30))], + handler: function () { + (0, utils_1.definitelyRealMemoryCorruption)(); + } + }, + editor: { + args: ["editor:boolean"], + description: "Toggles the in-game editor mode.", + perm: commands_1.Perm.trusted, + requirements: [commands_1.Req.mode("testsrv"), commands_1.Req.cooldownGlobal(20000)], + handler: function (_a) { + var editor = _a.args.editor; + Vars.state.rules.editor = editor; + Call.setRules(Vars.state.rules); + } + }, + mapruns: { + args: ["map:map", "lowestHighscores:boolean?"], + description: "Displays all map runs for a selected map, and allows deleting invalid/cheated runs.", + perm: commands_1.Perm.admin, + handler: function (_a) { + return __awaiter(this, arguments, void 0, function (_b) { + var fmap, _c, initialLength, runs, _d, index, _, deleted; + var _e; + var _f = _b.args, map = _f.map, lowestHighscores = _f.lowestHighscores, sender = _b.sender, outputSuccess = _b.outputSuccess; + return __generator(this, function (_g) { + switch (_g.label) { + case 0: + fmap = (_e = maps_1.FMap.getCreate(map)) !== null && _e !== void 0 ? _e : (0, commands_1.fail)("Map data is still loading, please try again."); + if (!(lowestHighscores !== null && lowestHighscores !== void 0)) return [3 /*break*/, 1]; + _c = lowestHighscores; + return [3 /*break*/, 3]; + case 1: return [4 /*yield*/, menus_1.Menu.buttons(sender, "[accent]Map runs", "Select a view", [ + [{ data: true, text: "Lowest highscores" }], + [{ data: false, text: "All runs" }], + ], { + includeCancel: true, + onCancel: "reject" + })]; + case 2: + _c = (lowestHighscores = _g.sent()); + _g.label = 3; + case 3: + _c; + initialLength = fmap.runs.length; + runs = fmap.runs.slice(); + if (lowestHighscores) + runs = runs.filter(function (r) { return r.success; }) + .sort(function (a, b) { return a.duration() - b.duration(); }); + return [4 /*yield*/, menus_1.Menu.textPages(sender, runs.map(function (r) { return [ + (0, utils_1.formatTimestamp)(r.startTime), + function () { + return "Duration: ".concat((0, utils_1.formatTime)(r.duration()), "\nMax player count: ").concat(r.maxPlayerCount, "\nOutcome: ").concat(r.outcome()[1], "\nWave: ").concat(r.wave); + } + ]; }), ["[scarlet]\uE86FDelete"], { + onCancel: "reject" + })]; + case 4: + _d = __read.apply(void 0, [_g.sent(), 2]), index = _d[0], _ = _d[1]; + return [4 /*yield*/, menus_1.Menu.confirmDangerous(sender, "Are you sure you want to delete this map run? This action is irreversible.")]; + case 5: + _g.sent(); + if (initialLength != fmap.runs.length) + (0, commands_1.fail)("Someone else deleted a run, please try again."); + deleted = fmap.runs.splice(index, 1)[0]; + outputSuccess("Deleted run (".concat((0, utils_1.formatTimestamp)(deleted.startTime), ") with duration ").concat((0, utils_1.formatTime)(deleted.duration()), ".")); + return [2 /*return*/]; + } + }); + }); + } + }, + crash: { + args: ["target:player"], + description: "Crashes the target player's Mindustry client.", + perm: commands_1.Perm.admin, + requirements: [commands_1.Req.moderate("target", false, "admin")], + handler: function (_a) { + var target = _a.args.target, f = _a.f, output = _a.output, outputSuccess = _a.outputSuccess; + if (target.hasPerm("blockTrolling")) + (0, commands_1.fail)(f(templateObject_79 || (templateObject_79 = __makeTemplateObject(["Player ", " is insufficiently trollable."], ["Player ", " is insufficiently trollable."])), target)); + if ((0, utils_1.crashClient)(target.player)) { + outputSuccess(f(templateObject_80 || (templateObject_80 = __makeTemplateObject(["Crashed client of ", "."], ["Crashed client of ", "."])), target)); + } + else { + output(f(templateObject_81 || (templateObject_81 = __makeTemplateObject(["Attempted to crash client of ", ". The crash will only occur once the sync completes."], ["Attempted to crash client of ", ". The crash will only occur once the sync completes."])), target)); + } + } + }, + yeet: { + args: ["target:player", "width:number", "height:number", "floor:block", "overlay:block", "build:block"], + description: "Sends the target player to a parallel universe.", + perm: commands_1.Perm.admin, + requirements: [commands_1.Req.moderate("target", false, "admin")], + handler: function (_a) { + return __awaiter(this, void 0, void 0, function () { + var _b = _a.args, target = _b.target, world = __rest(_b, ["target"]), f = _a.f, outputSuccess = _a.outputSuccess; + return __generator(this, function (_c) { + switch (_c.label) { + case 0: + if (target.hasPerm("blockTrolling")) + (0, commands_1.fail)(f(templateObject_82 || (templateObject_82 = __makeTemplateObject(["Player ", " is insufficiently trollable."], ["Player ", " is insufficiently trollable."])), target)); + outputSuccess("Aligning QPUs..."); + return [4 /*yield*/, (0, utils_1.syncManual)(target.player, undefined, world)]; + case 1: + _c.sent(); + outputSuccess(f(templateObject_83 || (templateObject_83 = __makeTemplateObject(["Sent ", " to a parallel universe."], ["Sent ", " to a parallel universe."])), target)); + return [2 /*return*/]; + } + }); + }); + } + }, + menuspam: { + args: ["target:player"], + description: "Sends the target player a very large amount of menus. They will be unable to do anything unless they force close mindustry.", + perm: commands_1.Perm.admin, + requirements: [commands_1.Req.moderate("target", false, "admin")], + handler: function (_a) { + return __awaiter(this, arguments, void 0, function (_b) { + var i, j; + var target = _b.args.target, f = _b.f, output = _b.output, outputSuccess = _b.outputSuccess; + return __generator(this, function (_c) { + switch (_c.label) { + case 0: + if (target.hasPerm("blockTrolling")) + (0, commands_1.fail)(f(templateObject_84 || (templateObject_84 = __makeTemplateObject(["Player ", " is insufficiently trollable."], ["Player ", " is insufficiently trollable."])), target)); + output("Sending menus."); + i = 0; + _c.label = 1; + case 1: + if (!(i < 10)) return [3 /*break*/, 4]; + for (j = 0; j < 100; j++) { + Call.menu(target.con, menus_1.listeners.generic, "", "", []); + } + return [4 /*yield*/, (0, funcs_1.delay)(100)]; + case 2: + _c.sent(); + _c.label = 3; + case 3: + i++; + return [3 /*break*/, 1]; + case 4: + outputSuccess(f(templateObject_85 || (templateObject_85 = __makeTemplateObject(["Spammed ", " with menus."], ["Spammed ", " with menus."])), target)); + return [2 /*return*/]; + } + }); + }); + } + } +}); +var templateObject_1, templateObject_2, templateObject_3, templateObject_4, templateObject_5, templateObject_6, templateObject_7, templateObject_8, templateObject_9, templateObject_10, templateObject_11, templateObject_12, templateObject_13, templateObject_14, templateObject_15, templateObject_16, templateObject_17, templateObject_18, templateObject_19, templateObject_20, templateObject_21, templateObject_22, templateObject_23, templateObject_24, templateObject_25, templateObject_26, templateObject_27, templateObject_28, templateObject_29, templateObject_30, templateObject_31, templateObject_32, templateObject_33, templateObject_34, templateObject_35, templateObject_36, templateObject_37, templateObject_38, templateObject_39, templateObject_40, templateObject_41, templateObject_42, templateObject_43, templateObject_44, templateObject_45, templateObject_46, templateObject_47, templateObject_48, templateObject_49, templateObject_50, templateObject_51, templateObject_52, templateObject_53, templateObject_54, templateObject_55, templateObject_56, templateObject_57, templateObject_58, templateObject_59, templateObject_60, templateObject_61, templateObject_62, templateObject_63, templateObject_64, templateObject_65, templateObject_66, templateObject_67, templateObject_68, templateObject_69, templateObject_70, templateObject_71, templateObject_72, templateObject_73, templateObject_74, templateObject_75, templateObject_76, templateObject_77, templateObject_78, templateObject_79, templateObject_80, templateObject_81, templateObject_82, templateObject_83, templateObject_84, templateObject_85; diff --git a/build/scripts/config.js b/build/scripts/config.js index 3af914ac..5c6bdeda 100644 --- a/build/scripts/config.js +++ b/build/scripts/config.js @@ -1,381 +1,381 @@ -"use strict"; -/* -Copyright © BalaM314, 2026. All Rights Reserved. -This file contains configurable constants. -*/ -var __makeTemplateObject = (this && this.__makeTemplateObject) || function (cooked, raw) { - if (Object.defineProperty) { Object.defineProperty(cooked, "raw", { value: raw }); } else { cooked.raw = raw; } - return cooked; -}; -var __read = (this && this.__read) || function (o, n) { - var m = typeof Symbol === "function" && o[Symbol.iterator]; - if (!m) return o; - var i = m.call(o), r, ar = [], e; - try { - while ((n === void 0 || n-- > 0) && !(r = i.next()).done) ar.push(r.value); - } - catch (error) { e = { error: error }; } - finally { - try { - if (r && !r.done && (m = i["return"])) m.call(i); - } - finally { if (e) throw e.error; } - } - return ar; -}; -var __spreadArray = (this && this.__spreadArray) || function (to, from, pack) { - if (pack || arguments.length === 2) for (var i = 0, l = from.length, ar; i < l; i++) { - if (ar || !(i in from)) { - if (!ar) ar = Array.prototype.slice.call(from, 0, i); - ar[i] = from[i]; - } - } - return to.concat(ar || Array.prototype.slice.call(from)); -}; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.noMultiVnwTag = exports.rules = exports.tips = exports.FColor = exports.text = exports.prefixes = exports.GamemodeNames = exports.Gamemode = exports.FishServer = exports.mapRepoURLs = exports.Mode = exports.campaigns = exports.Campaign = exports.throwbackMapFileNames = exports.backendIP = exports.stopAntiEvadeTime = exports.heuristics = exports.adminNames = exports.multiCharSubstitutions = exports.substitutions = exports.bannedWords = void 0; -var globals_1 = require("/globals"); -var ranks_1 = require("/ranks"); -var funcs_1 = require("/funcs"); -function processBannedWordList(words) { - return words.map(function (word) { - return (typeof word == "string" || word instanceof RegExp) ? - [word, []] - : [word[0], word.slice(1)]; - }); -} -exports.bannedWords = { - // README: Information on how to update this list - // All words must be in *lowercase*. - // Words need to be separated by commas, even if they are on a new line. - // If a word can be contained in another word that should be allowed (the scunthorpe problem), - // surround the entire thing in square brackets, then list out the words after - // like this: ["badw", "goodbadw"] - /** Normal: banned always. */ - normal: processBannedWordList([ - "fanum tax", "gyatt", ["rizz", "grizzly", "frizz", "horizzon"], "skibidi", //With love, DarthScion - //>:( -dart - // "uwu", //lol - "nig" + "ger", "nig" + "ga", "niger", "ni8" + "8er", "nig" + "gre", "негр", "ниг" + "гер", "нигер", "нігер", "ніг" + "гер", /\bnegr\b/, //our apologies to citizens of the Republic of Niger - ["ni" + "ga", "anniga", "inniga", "unniga", "aniga", "iniga", "eniga", "oniga"], - "re" + "tard", - 'kill yourself', 'kill urself', /\bkys\b/, - "kill blacks", "heil hitler", "heil nazis", "heil the nazis", "sieg heil", "hail hitler", "hail nazis", "hail the nazis", "sieg hail", /\b1488\b/, //nazi-related words - ["co" + "ck", "cockroach", "poppycock", "cocktail"], "suck dick", "sucking dick", - "iamasussyimposter", - ["cu" + "nt", "scunthorpe"], - ["penis", "peniston"], - "hawk tuah", - ["rape", "grape", "therap", "drape", "scrape", "trapez", "earrape", "atrape", "traped"], - ["raping", "draping", "graping", "scraping", "craping"], - /\bf(a)g\b/, "fa" + "gg" + "ot", - /\bc(u)m\b/, ["semen", "sement", "horsemen", "housemen", "defensemen", "those", "menders"], - ["porn", "maporn"], - "futa" + "nari", "futa", - "ur gay", "your gay", "youre gay", "you're gay", - "gooning", "gooner", "dildo", "loli", /\banal\b/, "cunny" - ]), - /** Strict: banned in names and for players with a chat strictness level of 'strict'. */ - strict: processBannedWordList([ - "fu" + "ck", "bi" + "tch", ["sh" + "it", "harshit"], /\ba(s)s\b/, "as" + "shole", ["dick", "medick", "dickens"], - ]), - /** Names: banned only in names. */ - names: processBannedWordList([ - "sex", /\bgoldberg\b/, "hitler", "stalin", "putin", "lenin", /^something$/, "[something]", "[[something]", "卐", "diddy", "epstein", - globals_1.uuidPattern, globals_1.ipPattern, globals_1.ipPortPattern - ]), - /** autoWhack: new players saying one of these words will be automatically stopped and muted. Comes with \b so no need to add it. */ - autoWhack: [ - "nig" + "ger", "nig" + "ga", "ni8" + "8er", "nig" + "g3r", "hit" + "ler", "fa" + "gg" + "ot", "nazis", "негр", "ниг" + "гер", "нигер", "нігер", "ніг" + "гер", "negr" - ], -}; -//for some reason the external mindustry server does not read the files correctly, so we can only use ASCII -exports.substitutions = Object.fromEntries(Object.entries({ - "a": "\u0430\u1E9A\u1EA1\u1E01\u00E4\u03B1@\u0101\u0103\u0105\u03AC", - "b": "\u1E03\u1E07\u1E03\u0253\u0185", - "c": "\u0441\u217D\u00E7\u03C2\u010B", - "d": "\u217E\u1E0B\u1E11\u010F\u1E13\u1E0D\u1E0F\u0257\u20AB\u0256\u056A", - "e": "\u0435\u1E1B\u0113\u1E17\u0229\u0451\u011B\u0205\u03F5\u03B5\u025B3", - "f": "\u1E1F\u0493\u0192", - "g": "\u0581\u0123\u01F5\u0260\u011F\u011D\u01E5\u1E21", - "h": "\u1E23\u021F\u1E25\u1E2B\u0570\u056B\u1E29\u0266\u1E27\u1E23\u0266\u1E96\u0127", - "i": "\u0456\u012F\u03B9\u1EC9\u1F31\u1F77\u012B1\u00A1\u0457\u0390\u03CA", - "j": "\u0458\u029D\u0575\u025F\u0135\u0237\u01F0", - "k": "\u049F\u1E31\u0137\u0138\u043A\u0199\u049D", - "l": "\u217C\u1E3D\u1E3B\u013E\u0140\u013C\u1E39\u0142\u038A\u00CC\u00CD\u00CE\u00CF\u0128\u012A\u012C\u012E\u0130\u0196\u0208\u020A\u0399\u03AA\u0406\u0407\u04C0\u04CF\u1E2C\u1EC8\u1F38\u1F39\u1FD8\u1FD9\u1FDA\u01D0\u03B9", - "m": "\u217F\u1E43\u0271\u1E41\u1E3F", - "n": "\u00F1\u0144\u0146\u0148\u0149\u01F9\u03AE\u03B7\u0578\u057C\u0580\u1E45\u1E47\u03A0", - "o": "\u00F2\u1ED9\u1EDB\u1EDD\u1EDF\u1EE1\u1EE3\u1F40\u1F41\u1F42\u1F43\u1F44\u1F45\u1F78\u1F79\u03C3\u0E50\u00F6\u014D\u014F\u0151\u01A1\u01D2\u03BF\u03CC0", - "p": "\u03C1\u0440\u048F\u1E55\u1E57\u1FE4\u1FE5\u2374", - "q": "\u051B\u0563\u0566\u0563\u0566", - "r": "\u0155\u0157\u0159\u0211\u0213\u027C\u027D\u0433\u0453\u0491\u04F7\u1E59\u1E5B\u1E5D", - "s": "\u015B\u015D\u015F\u0161\u0219\u0282\u0455\u1E61\u1E63\u1E65\u1E67\u1E69\u03C2", - "t": "\u0163\u0165\u01AB\u021B\u0288\u1E6B\u1E6D\u1E6F\u1E71\u1E97\u0236\u2020\u04AD", - "u": "\u00B5\u03BC\u00F9\u00FA\u00FB\u00FC\u0169\u016B\u016D\u016F\u0171\u0173\u01B0\u01D4\u0215\u0217\u0265\u1EE9\u1EEB\u1EED\u1EEF\u1EF1\u03BC\u03C5\u03CB\u03CD", - "v": "\u03BD\u0475\u0477\u1E7D\u1E7F\u2174\u2228\u03C5\u03CB\u03CD", - "w": "\u0175\u051D\u1E81\u1E83\u1E85\u1E87\u1E89\u1E98\u03C9\u03CE", - "x": "\u0445\u04B3\u1E8B\u1E8D\u03C7", - "y": "\u00FD\u00FF\u0177\u01B4\u0233\u03B3\u0443\u045E\u04EF\u04F1\u04F3\u1E8F\u1E99\u1EF3\u1EF5\u1EF7\u1EF9\u04AF\u04B1", - "z": "\u017A\u017C\u017E\u01B6\u0225\u0290\u1E91\u1E93\u1E95", - "A": "\u1E00\u1EA0\u1EA2\u1EA4\u1EA6\u1EA8\u1EAC\u1F08\u1F09\u1F88\u1F89\u1FB8\u1FB9\u1FBA\u1FBC\u212B\u0100\u0102\u0104\u0386\u0391\u0410", - "B": "\u0181\u0392\u0412\u1E02\u1E04\u1E06", - "C": "\u00C7\u0106\u0108\u010A\u010C\u0187\u0421\u04AA\u1E08\u216D\u03F9", - "D": "\u00D0\u010E\u0110\u0189\u018A\u1E0A\u1E0C\u1E0E\u216E", - "E": "\u00C8\u00C9\u00CA\u00CB\u0112\u0114\u0116\u0118\u011A\u0204\u0206\u0228\u0395\u0400\u0415\u04D6\u1E18\u0510\u2107\u0190\u1F19\u1FC8\u0404\u0388\u03AD\u03B5\u03B7\u0415", - "F": "\u03DC\u1E1E\u0492\u0191\u0492\u0493", - "G": "\u011C\u011E\u0120\u0122\u0193\u01E6\u01F4\u1E20", - "H": "\u0124\u021E\u0397\u041D\u04A2\u04A4\u04C7\u04C9\u1E22\u1E24\u1E26\u1E28\u1E2A\u1FCC\uA726\u0389", - "I": "\u038A\u00CC\u00CD\u00CE\u00CF\u0128\u012A\u012C\u012E\u0130\u0196\u0208\u020A\u0399\u03AA\u0406\u0407\u04C0\u04CF\u1E2C\u1EC8\u1F38\u1F39\u1FD8\u1FD9\u1FDA\u01D0\u217C\u1E3D\u1E3B\u026B\u013E\u0140\u013C\u1E39\u038A", - "J": "\u0134\u0408\u037F", - "K": "\u0136\u0198\u01E8\u039A\u040C\u041A\u051E\u1E30\u1E32\u1E34\u20AD\u212A\u03BA", - "L": "\u0139\u013B\u013D\u013F\u0141\u053C\u1E36\u1E38\u1E3A\u1E3C\u216C", - "M": "\u039C\u041C\u04CD\u1E3E\u1E40\u1E42\u216F", - "N": "\u00D1\u0143\u0145\u0147\u01F8\u039D\u1E44\u1E46\u1E48\u1E4A\u019D", - "O": "\u03B8\u236C\u00D2\u00D3\u00D4\u00D5\u00D6\u014C\u014E\u0150\u019F\u01A0\u01D1\u020E\u022E\u0230\u0398\u039F\u041E\u04E6\u0555\u1ECC\u1ECE\u1ED4\u1FF9\u038C", - "P": "\u01A4\u03A1\u0420\u048E\u1E54\u1E56\u1FEC", - "Q": "\u051A", - "R": "\u0154\u0156\u0158\u0210\u0212\u1E58\u1E5A\u1E5C\u1E5E\u211E\u024C\u2C64", - "S": "\u015A\u015C\u015E\u0160\u0218\u0405\u054F\u1E60\u1E62\u1E68\u1E64\u1E66", - "T": "\u0162\u0164\u0166\u01AE\u021A\u03A4\u0422\u04AC\u1E6A\u1E6C\u1E6E\u1E70\u038A\u1FDB\uA68C\u0372\u0373\u03C4", - "U": "\u016A\u016C\u016E\u0170\u0172\u01AF\u01D3\u1EE8\u1EEA\u1EEC\u1EEE\u0544", - "V": "\u0474\u0476\u1E7C\u1E7E\u22C1\u2164", - "W": "\u051C\u1E80\u1E82\u1E84\u1E86\u1E88\u019C", - "X": "\u03A7\u0425\u04B2\u1E8A\u1E8C\u2169", - "Y": "\u01B3\u0232\u03A5\u03AB\u03D3\u0423\u04AE\u04B0\u1E8E\u1EF2\u1EF4\u038E", - "Z": "\u0179\u017B\u017D\u0224\u0396\u1E90\u1E92\u1E94", - "": "\u200B\u200C\u200D", -}).map(function (_a) { - var _b = __read(_a, 2), char = _b[0], alts = _b[1]; - return alts.split("").map(function (alt) { return [alt, char]; }); -}).flat(1)); -exports.multiCharSubstitutions = [ - [/\|-\|/g, "H"] -]; -//#endregion -//#region misc -/** Used for anti-impersonation. Make sure to replace numbers with letters, for example, balam314 -> balamei4. */ -exports.adminNames = ["fish", "balamei4", "clashgone", "darthscion", "firefridge", "aricia", "rawsewage", "skeledragon", "edh8e", "everydayhuman8e", "benjamonsrl"]; -exports.heuristics = { - /** Will trip if more than this many blocks are broken within 25 seconds of joining. */ - blocksBrokenAfterJoin: 40, -}; -exports.stopAntiEvadeTime = funcs_1.Duration.minutes(30); -exports.backendIP = '45.79.202.111:5082'; -/** File names of throwback (retired) maps. These will not appear in the normal maps list, but will be available in the throwback maps tab. */ -exports.throwbackMapFileNames = []; -/** A campaign is a curated sequence of maps that overrides normal map rotation. */ -var Campaign = /** @class */ (function () { - function Campaign( - /** Display name of the campaign */ - name, - /** Short description shown in the menu */ - description, - /** Ordered list of map file names in the campaign */ - mapFileNames) { - this.name = name; - this.description = description; - this.mapFileNames = mapFileNames; - } - return Campaign; -}()); -exports.Campaign = Campaign; -/** Available campaigns. Maps are listed in order; when a campaign is active, rotation follows this sequence. */ -exports.campaigns = []; -exports.Mode = { - localDebug: new Fi("config/.debug").exists(), - noBackend: new Fi("config/.debug").exists() && !exports.backendIP.startsWith("127.0.0.1:"), - isChristmas: new Date().getMonth() == 11, - isAprilFools: new Date().getMonth() == 3 && new Date().getDate() == 1, -}; -//#endregion -//#region servers -/** Stores the repository url for the maps for each gamemode. */ -exports.mapRepoURLs = { - attack: "https://api.github.com/repos/Fish-Community/fish-maps/contents/attack", - survival: "https://api.github.com/repos/Fish-Community/fish-maps/contents/survival", - pvp: "https://api.github.com/repos/Fish-Community/fish-maps/contents/pvp", - hexed: "https://api.github.com/repos/Fish-Community/fish-maps/contents/hexed", - sandbox: "https://api.github.com/repos/Fish-Community/fish-maps/contents/sandbox", - hardcore: "https://api.github.com/repos/Fish-Community/fish-maps/contents/hardcore", - testsrv: "https://api.github.com/repos/Fish-Community/fish-maps/contents/testsrv", - minigame: "https://api.github.com/repos/Fish-Community/fish-maps/contents/minigame", -}; -/** Stores the names and addresses of each active server. */ -var FishServer = /** @class */ (function () { - function FishServer(name, ip, port, aliases, - /** If set, this permission is required to switch to or get information about this server. */ - requiredPerm) { - this.name = name; - this.ip = ip; - this.port = port; - this.aliases = aliases; - this.requiredPerm = requiredPerm; - FishServer.all.push(this); - } - FishServer.byName = function (input) { - var _a; - input = input.toLowerCase(); - return (_a = FishServer.all.find(function (s) { return s.aliases.concat(s.name).includes(input); })) !== null && _a !== void 0 ? _a : null; - }; - FishServer.all = []; - FishServer.attack = new FishServer("attack", "162.248.100.98", "6567", ["attac", "atack", "atak", "atck", "atk", "a"]); - FishServer.survival = new FishServer("survival", "162.248.101.95", "6567", ["surviv", "surv", "sur", "su", "s", "sl"]); - FishServer.pvp = new FishServer("pvp", "162.248.102.101", "6567", ["pv", "p", "v", "playerversusplayer"]); - FishServer.sandbox = new FishServer("sandbox", "162.248.101.53", "6567", ["sand", "box", "sa", "sb"]); - FishServer.hexed = new FishServer("hexed", "162.248.100.133", "6567", ["h", "hx", "hxd", "hpvp", "hxpvp", "hexpvp"]); - FishServer.minigame = new FishServer("minigame", "162.248.101.116", "6567", ["m", "mg", "mini", "minig", "mgame", "mng", "minigame", "mpvp"]); - FishServer.testing = new FishServer("testing", "162.248.101.52", "6567", ["test", "testsrv", "t", "testingserver", "testserver"]); - return FishServer; -}()); -exports.FishServer = FishServer; -; -/** Stores functions that return whether the specified gamemode is the current gamemode. */ -exports.Gamemode = { - attack: function () { return exports.Gamemode.name() == "attack"; }, - survival: function () { return exports.Gamemode.name() == "survival"; }, - pvp: function () { return exports.Gamemode.name() == "pvp" || exports.Gamemode.name() == "hexed" || exports.Gamemode.name() == "minigame"; }, - sandbox: function () { return exports.Gamemode.name() == "sandbox"; }, - hexed: function () { return exports.Gamemode.name() == "hexed"; }, - hardcore: function () { return exports.Gamemode.name() == "hardcore"; }, - testsrv: function () { return exports.Gamemode.name() == "testsrv"; }, - minigame: function () { return exports.Gamemode.name() == "minigame"; }, - name: function () { return Core.settings.get("mode", Vars.state.rules.mode().name()); }, -}; -exports.GamemodeNames = Object.keys(exports.Gamemode).filter(function (x) { return x !== "name"; }); -//#endregion -//#region text content -exports.prefixes = { - marked: '[yellow]\u26A0[scarlet]Marked Griefer[]\u26A0[]', - flagged: '[yellow]\u26A0[orange]Flagged[]\u26A0[]', - muted: '[white](muted)', -}; -exports.text = { - discordURL: "https://discord.gg/VpzcYSQ33Y", - membershipURL: "https://patreon.com/FishServers", - reportsPing: "<@&1040193678817378305>", - welcomeMessage: function () { return (0, funcs_1.random)([ - "[gold]Welcome![]" - ]); }, - chatFilterReplacement: { - message: function () { return "I really hope everyone is having a fun time :) <3"; }, - messageShort: function () { return "I hope we're all having a fun time :) <3"; }, - highlight: function () { return "[#f456f]"; }, - // `[#22AA22]Merry [#EC4444]Christmas!`, - // `[gold]Happy Holidays! [white]•*•☃*•`, - // `[gold]Happy Hanukkah!`, - // `[#EC4444]May your days be merry and bright!`, - // `[gold]Merry Fishmas! >|||> [white]☃`, - // `[gold]Deck the halls with lots of fun!`, - // `[gold]>|||> Fish wishes you [#22AA22]a merry Christmas!`, - // ]), - // chatFilterReplacement: { - // message: () => random([ - // `Have a holly jolly Christmas :) <3`, - // `I really hope everyone is jolly for the season! :D`, - // `All I want for Christmaaaaaaas is everyone having a fun time! :)`, - // `Remember to be nice in chat: Santa is watching! <3`, - // `All I want for Christmas is Fish! >|||>`, - // ]), - // highlight: () => random([ - // `[#22AA22]`, `[#EC4444]`, `[#FFFFFF]` - // ]), - }, - dataFetchFailed: "[scarlet]\u26A0 Data fetch failed!\n[white]Please disconnect and rejoin the server if you encounter further issues, such as missing rank or statistics.", -}; -//TODO use this -exports.FColor = (function (data) { - return Object.fromEntries(Object.entries(data).map(function (_a) { - var _b = __read(_a, 2), k = _b[0], c = _b[1]; - return [k, function (str) { - var varChunks = []; - for (var _i = 1; _i < arguments.length; _i++) { - varChunks[_i - 1] = arguments[_i]; - } - return str != null ? - "".concat(c).concat(Array.isArray(str) ? String.raw.apply(String, __spreadArray([{ raw: str }], __read(varChunks.map(function (v) { return String(v) + c; })), false)) : str, "[]") - : c; - } - ]; - })); -})({ - discord: "[#7289DA]", - /** Used for tips and welcome messages. */ - tip: "[gold]", - member: "[pink]", - achievement: "[lime]", -}); -/** Tips that are shown to players randomly. */ -exports.tips = { - ads: [ - "".concat(exports.FColor.member(templateObject_1 || (templateObject_1 = __makeTemplateObject(["Fish Membership"], ["Fish Membership"]))), " subscribers can access the ").concat(exports.FColor.member(templateObject_2 || (templateObject_2 = __makeTemplateObject(["/pet"], ["/pet"]))), " command, which spawns a merui that follows you around. Get a Fish Membership at[sky] ").concat(exports.text.membershipURL, " []"), - "".concat(exports.FColor.member(templateObject_3 || (templateObject_3 = __makeTemplateObject(["Fish Membership"], ["Fish Membership"]))), " subscribers can use the ").concat(exports.FColor.member(templateObject_4 || (templateObject_4 = __makeTemplateObject(["/highlight"], ["/highlight"]))), " command, which turns your chat messages to a color of your choice. Get a Fish Membership at[sky] ").concat(exports.text.membershipURL, " []"), - "".concat(exports.FColor.member(templateObject_5 || (templateObject_5 = __makeTemplateObject(["Fish Membership"], ["Fish Membership"]))), " subscribers can use the ").concat(exports.FColor.member(templateObject_6 || (templateObject_6 = __makeTemplateObject(["/rainbow"], ["/rainbow"]))), " command, which makes your name flash different colors. Get a Fish Membership at[sky] ").concat(exports.text.membershipURL, " []"), - "Want to support the server and get some perks? Get a ".concat(exports.FColor.member(templateObject_7 || (templateObject_7 = __makeTemplateObject(["Fish Membership"], ["Fish Membership"]))), " at[sky] ").concat(exports.text.membershipURL, " []"), - "Join our ".concat(exports.FColor.discord(templateObject_8 || (templateObject_8 = __makeTemplateObject(["Discord server"], ["Discord server"]))), "[]! ").concat(exports.FColor.discord(exports.text.discordURL), " or type ").concat(exports.FColor.discord(templateObject_9 || (templateObject_9 = __makeTemplateObject(["/discord"], ["/discord"])))), - ], - normal: [ - //commands - "You can spawn an [scarlet]Ohno[] with the [scarlet]/ohno[] command. Ohnos are harmless creatures that were created by fusing an alpha and an atrax.", - "Ohnos cannot be spawned near enemy buildings, because they are peaceful and do not want to be used for attacks.", - "You can use [white]/tp[] to teleport directly to any other player! (But only when you're in a core unit)", - "You can unload bulk conveyors (\uF819 or \uF732) with unloaders (\uF864 or \uF731).", - "Hate boulders? You can remove them with [white]/clean[].", - "You can check our rules at any time by running [white]/rules[].", - // `You can kill your unit by running [white]/die[].`, - "We have a tilelog system to help catch griefers. Run [white]/tilelog[], then click a tile to see what's happened there.", - "Run [white]/tilelog 1[] to check the tile history of multiple tiles.", - "Tilelog stores when a building is placed, broken, rotated, configured, and picked up/dropped by a payload unit. Access it with [white]/tilelog[]", - "Tilelog doesn't just log tile actions, it also logs unit deaths! Access it with [white]/tilelog[]", - "Did someone kill a T5 with commands? Run [white]/aoelog 0 15 killed[] to check tilelogs for unit deaths in a large area.", - "Aoelog can show the history of tiles in an area. Select the opposite corners of a rectangle to view the history of its tiles.", - "Aoelog is the plural version of tilelog, access it via [white]/aoelog[]", - "You can mark yourself as AFK(away from keyboard) with [white]/afk[].", - "Run /survival, /attack, /pvp, /sandbox, /hexed or /minigame to quickly change to another server.", - "Need to get rid of an active griefer? Use [#6FFC7C]/s[] to send a message to all staff members across all servers.", - "Use [white]/help to get more information about a specific command.", - "If you want to send a message to just one player, you can use the [white]/msg[] command.", - "Use [white]/r[] to reply to a message sent by another player.", - "[white]/trail[] can be used to give your unit a trail of particle effects.", - "Run [white]/ranks[] to see all the ranks on our server.", - "Is someone impersonating a staff member? Run [white]/rank[] to see their real rank.", - "Don't like the map? Vote to change it with [white]/rtv[].", - "If you want to end the current map, DO NOT BREAK DEFENCES! Vote to change the map with [white]/rtv[].", - //misc - "Anyone attempting to impersonate a ranked player, or the server, will have [scarlet]SUSSY IMPOSTOR[] prepended to their name. Beware!", - "Griefers will often be found with the text ".concat(exports.prefixes.marked, " prepended to their name: they are harmless and cannot grief again."), - "Don't votekick ".concat(exports.prefixes.marked.slice(0, -3), "[][scarlet]s [gold]if they aren't breaking the rules: they are incapable of griefing more."), - "Players marked as ".concat(exports.prefixes.flagged, " have been flagged as suspicious by our detection systems, but they may not be griefers."), - "Need to appeal a moderation action? Join the discord at ".concat(exports.FColor.discord(exports.text.discordURL), " or type /discord"), - "Want to send the phrase [white]\"/command\"[] in chat? Type [white]\"./command\"[] and the [white].[] will be removed.", - "All commands with a player as an argument support using a menu to specify the player. Just run the command leaving the argument blank (using two spaces if necessary), and a menu will show up.", - "Players with a ".concat(ranks_1.Rank.trusted.prefix, " in front of their name aren't staff members, but they do have extra powers."), - "Staff members will have the following prefixes in front of their name: ".concat(ranks_1.Rank.manager.prefix, ", ").concat(ranks_1.Rank.admin.prefix, ", ").concat(ranks_1.Rank.mod.prefix), - "Wave cooldown too long? Skip the wait with [white]/vnw[]", - "You can tell new players not to break power voids with [white]/void[]", - "You can add [pink]color[] to things with color tags! Try typing \"[[".concat(["pink", "green", "cyan", "acid", "royal", "coral"][Math.floor(Math.random() * 6)], "]Hello\" in chat, and see what happens!") - ], - christmas: [ - "Remember to be nice in-game, Santa is watching!", - "Santa's checking his list, so be nice!", - "Have a merry christmas and a happy new year!", - "Fish becomes a bit more jolly around Christmastime!", - "Many server maps have been changed for the season.", - ], - staff: [], -}; -exports.rules = [ - "# 1: [red]No griefing. This refers to intentionally hurting your own team in any way.", - "# 2: [orange]False votekicking isn't allowed. Avoid votekicking if there's an active staff member in the server.", - "# 3: [yellow]Gore, pornography, suggestive content and jokes, and flashing images aren't allowed here. Being horny and a creep in chat will result in a ban.", - "# 4: [green]Do not harass other people. We have zero tolerance for any bigotry. Please respect everyone.", - "# 5: [#00D8D8]Spamming is prohibited. Be reasonable with messaging staff in-game. Misuse may result in a mute.", - "# 6: [blue]Impersonating people or ranks is prohibited.", - "# 7: [purple]Talking about controversial or sensitive topics is not allowed in-game. Hate symbols, such as swastikas, are not permitted.", - "# 8: [pink]No uncomfortable trolling or intentionally causing chaos. This includes any actions or messages that create an unpleasant atmosphere.", - "Failure to follow these rules will result in consequences: likely a ".concat(exports.prefixes.marked, " tag for any game disruption, mute for broken chat rules, and bans for repeated offenses or bypasses.") -].map(function (r) { return "[white]".concat(r); }); -/** Map tag key for maps that opt out of multi-wave skipping (Issue #151). - * Maps with this tag set to "true" will only allow 1 wave to be skipped per /vnw call. */ -exports.noMultiVnwTag = "no-multi-vnw"; -var templateObject_1, templateObject_2, templateObject_3, templateObject_4, templateObject_5, templateObject_6, templateObject_7, templateObject_8, templateObject_9; -//#endregion +"use strict"; +/* +Copyright © BalaM314, 2026. All Rights Reserved. +This file contains configurable constants. +*/ +var __makeTemplateObject = (this && this.__makeTemplateObject) || function (cooked, raw) { + if (Object.defineProperty) { Object.defineProperty(cooked, "raw", { value: raw }); } else { cooked.raw = raw; } + return cooked; +}; +var __read = (this && this.__read) || function (o, n) { + var m = typeof Symbol === "function" && o[Symbol.iterator]; + if (!m) return o; + var i = m.call(o), r, ar = [], e; + try { + while ((n === void 0 || n-- > 0) && !(r = i.next()).done) ar.push(r.value); + } + catch (error) { e = { error: error }; } + finally { + try { + if (r && !r.done && (m = i["return"])) m.call(i); + } + finally { if (e) throw e.error; } + } + return ar; +}; +var __spreadArray = (this && this.__spreadArray) || function (to, from, pack) { + if (pack || arguments.length === 2) for (var i = 0, l = from.length, ar; i < l; i++) { + if (ar || !(i in from)) { + if (!ar) ar = Array.prototype.slice.call(from, 0, i); + ar[i] = from[i]; + } + } + return to.concat(ar || Array.prototype.slice.call(from)); +}; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.noMultiVnwTag = exports.rules = exports.tips = exports.FColor = exports.text = exports.prefixes = exports.GamemodeNames = exports.Gamemode = exports.FishServer = exports.mapRepoURLs = exports.Mode = exports.campaigns = exports.Campaign = exports.throwbackMapFileNames = exports.backendIP = exports.stopAntiEvadeTime = exports.heuristics = exports.adminNames = exports.multiCharSubstitutions = exports.substitutions = exports.bannedWords = void 0; +var globals_1 = require("/globals"); +var ranks_1 = require("/ranks"); +var funcs_1 = require("/funcs"); +function processBannedWordList(words) { + return words.map(function (word) { + return (typeof word == "string" || word instanceof RegExp) ? + [word, []] + : [word[0], word.slice(1)]; + }); +} +exports.bannedWords = { + // README: Information on how to update this list + // All words must be in *lowercase*. + // Words need to be separated by commas, even if they are on a new line. + // If a word can be contained in another word that should be allowed (the scunthorpe problem), + // surround the entire thing in square brackets, then list out the words after + // like this: ["badw", "goodbadw"] + /** Normal: banned always. */ + normal: processBannedWordList([ + "fanum tax", "gyatt", ["rizz", "grizzly", "frizz", "horizzon"], "skibidi", //With love, DarthScion + //>:( -dart + // "uwu", //lol + "nig" + "ger", "nig" + "ga", "niger", "ni8" + "8er", "nig" + "gre", "негр", "ниг" + "гер", "нигер", "нігер", "ніг" + "гер", /\bnegr\b/, //our apologies to citizens of the Republic of Niger + ["ni" + "ga", "anniga", "inniga", "unniga", "aniga", "iniga", "eniga", "oniga"], + "re" + "tard", + 'kill yourself', 'kill urself', /\bkys\b/, + "kill blacks", "heil hitler", "heil nazis", "heil the nazis", "sieg heil", "hail hitler", "hail nazis", "hail the nazis", "sieg hail", /\b1488\b/, //nazi-related words + ["co" + "ck", "cockroach", "poppycock", "cocktail"], "suck dick", "sucking dick", + "iamasussyimposter", + ["cu" + "nt", "scunthorpe"], + ["penis", "peniston"], + "hawk tuah", + ["rape", "grape", "therap", "drape", "scrape", "trapez", "earrape", "atrape", "traped"], + ["raping", "draping", "graping", "scraping", "craping"], + /\bf(a)g\b/, "fa" + "gg" + "ot", + /\bc(u)m\b/, ["semen", "sement", "horsemen", "housemen", "defensemen", "those", "menders"], + ["porn", "maporn"], + "futa" + "nari", "futa", + "ur gay", "your gay", "youre gay", "you're gay", + "gooning", "gooner", "dildo", "loli", /\banal\b/, "cunny" + ]), + /** Strict: banned in names and for players with a chat strictness level of 'strict'. */ + strict: processBannedWordList([ + "fu" + "ck", "bi" + "tch", ["sh" + "it", "harshit"], /\ba(s)s\b/, "as" + "shole", ["dick", "medick", "dickens"], + ]), + /** Names: banned only in names. */ + names: processBannedWordList([ + "sex", /\bgoldberg\b/, "hitler", "stalin", "putin", "lenin", /^something$/, "[something]", "[[something]", "卐", "diddy", "epstein", + globals_1.uuidPattern, globals_1.ipPattern, globals_1.ipPortPattern + ]), + /** autoWhack: new players saying one of these words will be automatically stopped and muted. Comes with \b so no need to add it. */ + autoWhack: [ + "nig" + "ger", "nig" + "ga", "ni8" + "8er", "nig" + "g3r", "hit" + "ler", "fa" + "gg" + "ot", "nazis", "негр", "ниг" + "гер", "нигер", "нігер", "ніг" + "гер", "negr" + ], +}; +//for some reason the external mindustry server does not read the files correctly, so we can only use ASCII +exports.substitutions = Object.fromEntries(Object.entries({ + "a": "\u0430\u1E9A\u1EA1\u1E01\u00E4\u03B1@\u0101\u0103\u0105\u03AC", + "b": "\u1E03\u1E07\u1E03\u0253\u0185", + "c": "\u0441\u217D\u00E7\u03C2\u010B", + "d": "\u217E\u1E0B\u1E11\u010F\u1E13\u1E0D\u1E0F\u0257\u20AB\u0256\u056A", + "e": "\u0435\u1E1B\u0113\u1E17\u0229\u0451\u011B\u0205\u03F5\u03B5\u025B3", + "f": "\u1E1F\u0493\u0192", + "g": "\u0581\u0123\u01F5\u0260\u011F\u011D\u01E5\u1E21", + "h": "\u1E23\u021F\u1E25\u1E2B\u0570\u056B\u1E29\u0266\u1E27\u1E23\u0266\u1E96\u0127", + "i": "\u0456\u012F\u03B9\u1EC9\u1F31\u1F77\u012B1\u00A1\u0457\u0390\u03CA", + "j": "\u0458\u029D\u0575\u025F\u0135\u0237\u01F0", + "k": "\u049F\u1E31\u0137\u0138\u043A\u0199\u049D", + "l": "\u217C\u1E3D\u1E3B\u013E\u0140\u013C\u1E39\u0142\u038A\u00CC\u00CD\u00CE\u00CF\u0128\u012A\u012C\u012E\u0130\u0196\u0208\u020A\u0399\u03AA\u0406\u0407\u04C0\u04CF\u1E2C\u1EC8\u1F38\u1F39\u1FD8\u1FD9\u1FDA\u01D0\u03B9", + "m": "\u217F\u1E43\u0271\u1E41\u1E3F", + "n": "\u00F1\u0144\u0146\u0148\u0149\u01F9\u03AE\u03B7\u0578\u057C\u0580\u1E45\u1E47\u03A0", + "o": "\u00F2\u1ED9\u1EDB\u1EDD\u1EDF\u1EE1\u1EE3\u1F40\u1F41\u1F42\u1F43\u1F44\u1F45\u1F78\u1F79\u03C3\u0E50\u00F6\u014D\u014F\u0151\u01A1\u01D2\u03BF\u03CC0", + "p": "\u03C1\u0440\u048F\u1E55\u1E57\u1FE4\u1FE5\u2374", + "q": "\u051B\u0563\u0566\u0563\u0566", + "r": "\u0155\u0157\u0159\u0211\u0213\u027C\u027D\u0433\u0453\u0491\u04F7\u1E59\u1E5B\u1E5D", + "s": "\u015B\u015D\u015F\u0161\u0219\u0282\u0455\u1E61\u1E63\u1E65\u1E67\u1E69\u03C2", + "t": "\u0163\u0165\u01AB\u021B\u0288\u1E6B\u1E6D\u1E6F\u1E71\u1E97\u0236\u2020\u04AD", + "u": "\u00B5\u03BC\u00F9\u00FA\u00FB\u00FC\u0169\u016B\u016D\u016F\u0171\u0173\u01B0\u01D4\u0215\u0217\u0265\u1EE9\u1EEB\u1EED\u1EEF\u1EF1\u03BC\u03C5\u03CB\u03CD", + "v": "\u03BD\u0475\u0477\u1E7D\u1E7F\u2174\u2228\u03C5\u03CB\u03CD", + "w": "\u0175\u051D\u1E81\u1E83\u1E85\u1E87\u1E89\u1E98\u03C9\u03CE", + "x": "\u0445\u04B3\u1E8B\u1E8D\u03C7", + "y": "\u00FD\u00FF\u0177\u01B4\u0233\u03B3\u0443\u045E\u04EF\u04F1\u04F3\u1E8F\u1E99\u1EF3\u1EF5\u1EF7\u1EF9\u04AF\u04B1", + "z": "\u017A\u017C\u017E\u01B6\u0225\u0290\u1E91\u1E93\u1E95", + "A": "\u1E00\u1EA0\u1EA2\u1EA4\u1EA6\u1EA8\u1EAC\u1F08\u1F09\u1F88\u1F89\u1FB8\u1FB9\u1FBA\u1FBC\u212B\u0100\u0102\u0104\u0386\u0391\u0410", + "B": "\u0181\u0392\u0412\u1E02\u1E04\u1E06", + "C": "\u00C7\u0106\u0108\u010A\u010C\u0187\u0421\u04AA\u1E08\u216D\u03F9", + "D": "\u00D0\u010E\u0110\u0189\u018A\u1E0A\u1E0C\u1E0E\u216E", + "E": "\u00C8\u00C9\u00CA\u00CB\u0112\u0114\u0116\u0118\u011A\u0204\u0206\u0228\u0395\u0400\u0415\u04D6\u1E18\u0510\u2107\u0190\u1F19\u1FC8\u0404\u0388\u03AD\u03B5\u03B7\u0415", + "F": "\u03DC\u1E1E\u0492\u0191\u0492\u0493", + "G": "\u011C\u011E\u0120\u0122\u0193\u01E6\u01F4\u1E20", + "H": "\u0124\u021E\u0397\u041D\u04A2\u04A4\u04C7\u04C9\u1E22\u1E24\u1E26\u1E28\u1E2A\u1FCC\uA726\u0389", + "I": "\u038A\u00CC\u00CD\u00CE\u00CF\u0128\u012A\u012C\u012E\u0130\u0196\u0208\u020A\u0399\u03AA\u0406\u0407\u04C0\u04CF\u1E2C\u1EC8\u1F38\u1F39\u1FD8\u1FD9\u1FDA\u01D0\u217C\u1E3D\u1E3B\u026B\u013E\u0140\u013C\u1E39\u038A", + "J": "\u0134\u0408\u037F", + "K": "\u0136\u0198\u01E8\u039A\u040C\u041A\u051E\u1E30\u1E32\u1E34\u20AD\u212A\u03BA", + "L": "\u0139\u013B\u013D\u013F\u0141\u053C\u1E36\u1E38\u1E3A\u1E3C\u216C", + "M": "\u039C\u041C\u04CD\u1E3E\u1E40\u1E42\u216F", + "N": "\u00D1\u0143\u0145\u0147\u01F8\u039D\u1E44\u1E46\u1E48\u1E4A\u019D", + "O": "\u03B8\u236C\u00D2\u00D3\u00D4\u00D5\u00D6\u014C\u014E\u0150\u019F\u01A0\u01D1\u020E\u022E\u0230\u0398\u039F\u041E\u04E6\u0555\u1ECC\u1ECE\u1ED4\u1FF9\u038C", + "P": "\u01A4\u03A1\u0420\u048E\u1E54\u1E56\u1FEC", + "Q": "\u051A", + "R": "\u0154\u0156\u0158\u0210\u0212\u1E58\u1E5A\u1E5C\u1E5E\u211E\u024C\u2C64", + "S": "\u015A\u015C\u015E\u0160\u0218\u0405\u054F\u1E60\u1E62\u1E68\u1E64\u1E66", + "T": "\u0162\u0164\u0166\u01AE\u021A\u03A4\u0422\u04AC\u1E6A\u1E6C\u1E6E\u1E70\u038A\u1FDB\uA68C\u0372\u0373\u03C4", + "U": "\u016A\u016C\u016E\u0170\u0172\u01AF\u01D3\u1EE8\u1EEA\u1EEC\u1EEE\u0544", + "V": "\u0474\u0476\u1E7C\u1E7E\u22C1\u2164", + "W": "\u051C\u1E80\u1E82\u1E84\u1E86\u1E88\u019C", + "X": "\u03A7\u0425\u04B2\u1E8A\u1E8C\u2169", + "Y": "\u01B3\u0232\u03A5\u03AB\u03D3\u0423\u04AE\u04B0\u1E8E\u1EF2\u1EF4\u038E", + "Z": "\u0179\u017B\u017D\u0224\u0396\u1E90\u1E92\u1E94", + "": "\u200B\u200C\u200D", +}).map(function (_a) { + var _b = __read(_a, 2), char = _b[0], alts = _b[1]; + return alts.split("").map(function (alt) { return [alt, char]; }); +}).flat(1)); +exports.multiCharSubstitutions = [ + [/\|-\|/g, "H"] +]; +//#endregion +//#region misc +/** Used for anti-impersonation. Make sure to replace numbers with letters, for example, balam314 -> balamei4. */ +exports.adminNames = ["fish", "balamei4", "clashgone", "darthscion", "firefridge", "aricia", "rawsewage", "skeledragon", "edh8e", "everydayhuman8e", "benjamonsrl"]; +exports.heuristics = { + /** Will trip if more than this many blocks are broken within 25 seconds of joining. */ + blocksBrokenAfterJoin: 40, +}; +exports.stopAntiEvadeTime = funcs_1.Duration.minutes(30); +exports.backendIP = '45.79.202.111:5082'; +/** File names of throwback (retired) maps. These will not appear in the normal maps list, but will be available in the throwback maps tab. */ +exports.throwbackMapFileNames = []; +/** A campaign is a curated sequence of maps that overrides normal map rotation. */ +var Campaign = /** @class */ (function () { + function Campaign( + /** Display name of the campaign */ + name, + /** Short description shown in the menu */ + description, + /** Ordered list of map file names in the campaign */ + mapFileNames) { + this.name = name; + this.description = description; + this.mapFileNames = mapFileNames; + } + return Campaign; +}()); +exports.Campaign = Campaign; +/** Available campaigns. Maps are listed in order; when a campaign is active, rotation follows this sequence. */ +exports.campaigns = []; +exports.Mode = { + localDebug: new Fi("config/.debug").exists(), + noBackend: new Fi("config/.debug").exists() && !exports.backendIP.startsWith("127.0.0.1:"), + isChristmas: new Date().getMonth() == 11, + isAprilFools: new Date().getMonth() == 3 && new Date().getDate() == 1, +}; +//#endregion +//#region servers +/** Stores the repository url for the maps for each gamemode. */ +exports.mapRepoURLs = { + attack: "https://api.github.com/repos/Fish-Community/fish-maps/contents/attack", + survival: "https://api.github.com/repos/Fish-Community/fish-maps/contents/survival", + pvp: "https://api.github.com/repos/Fish-Community/fish-maps/contents/pvp", + hexed: "https://api.github.com/repos/Fish-Community/fish-maps/contents/hexed", + sandbox: "https://api.github.com/repos/Fish-Community/fish-maps/contents/sandbox", + hardcore: "https://api.github.com/repos/Fish-Community/fish-maps/contents/hardcore", + testsrv: "https://api.github.com/repos/Fish-Community/fish-maps/contents/testsrv", + minigame: "https://api.github.com/repos/Fish-Community/fish-maps/contents/minigame", +}; +/** Stores the names and addresses of each active server. */ +var FishServer = /** @class */ (function () { + function FishServer(name, ip, port, aliases, + /** If set, this permission is required to switch to or get information about this server. */ + requiredPerm) { + this.name = name; + this.ip = ip; + this.port = port; + this.aliases = aliases; + this.requiredPerm = requiredPerm; + FishServer.all.push(this); + } + FishServer.byName = function (input) { + var _a; + input = input.toLowerCase(); + return (_a = FishServer.all.find(function (s) { return s.aliases.concat(s.name).includes(input); })) !== null && _a !== void 0 ? _a : null; + }; + FishServer.all = []; + FishServer.attack = new FishServer("attack", "162.248.100.98", "6567", ["attac", "atack", "atak", "atck", "atk", "a"]); + FishServer.survival = new FishServer("survival", "162.248.101.95", "6567", ["surviv", "surv", "sur", "su", "s", "sl"]); + FishServer.pvp = new FishServer("pvp", "162.248.102.101", "6567", ["pv", "p", "v", "playerversusplayer"]); + FishServer.sandbox = new FishServer("sandbox", "162.248.101.53", "6567", ["sand", "box", "sa", "sb"]); + FishServer.hexed = new FishServer("hexed", "162.248.100.133", "6567", ["h", "hx", "hxd", "hpvp", "hxpvp", "hexpvp"]); + FishServer.minigame = new FishServer("minigame", "162.248.101.116", "6567", ["m", "mg", "mini", "minig", "mgame", "mng", "minigame", "mpvp"]); + FishServer.testing = new FishServer("testing", "162.248.101.52", "6567", ["test", "testsrv", "t", "testingserver", "testserver"]); + return FishServer; +}()); +exports.FishServer = FishServer; +; +/** Stores functions that return whether the specified gamemode is the current gamemode. */ +exports.Gamemode = { + attack: function () { return exports.Gamemode.name() == "attack"; }, + survival: function () { return exports.Gamemode.name() == "survival"; }, + pvp: function () { return exports.Gamemode.name() == "pvp" || exports.Gamemode.name() == "hexed" || exports.Gamemode.name() == "minigame"; }, + sandbox: function () { return exports.Gamemode.name() == "sandbox"; }, + hexed: function () { return exports.Gamemode.name() == "hexed"; }, + hardcore: function () { return exports.Gamemode.name() == "hardcore"; }, + testsrv: function () { return exports.Gamemode.name() == "testsrv"; }, + minigame: function () { return exports.Gamemode.name() == "minigame"; }, + name: function () { return Core.settings.get("mode", Vars.state.rules.mode().name()); }, +}; +exports.GamemodeNames = Object.keys(exports.Gamemode).filter(function (x) { return x !== "name"; }); +//#endregion +//#region text content +exports.prefixes = { + marked: '[yellow]\u26A0[scarlet]Marked Griefer[]\u26A0[]', + flagged: '[yellow]\u26A0[orange]Flagged[]\u26A0[]', + muted: '[white](muted)', +}; +exports.text = { + discordURL: "https://discord.gg/VpzcYSQ33Y", + membershipURL: "https://patreon.com/FishServers", + reportsPing: "<@&1040193678817378305>", + welcomeMessage: function () { return (0, funcs_1.random)([ + "[gold]Welcome![]" + ]); }, + chatFilterReplacement: { + message: function () { return "I really hope everyone is having a fun time :) <3"; }, + messageShort: function () { return "I hope we're all having a fun time :) <3"; }, + highlight: function () { return "[#f456f]"; }, + // `[#22AA22]Merry [#EC4444]Christmas!`, + // `[gold]Happy Holidays! [white]•*•☃*•`, + // `[gold]Happy Hanukkah!`, + // `[#EC4444]May your days be merry and bright!`, + // `[gold]Merry Fishmas! >|||> [white]☃`, + // `[gold]Deck the halls with lots of fun!`, + // `[gold]>|||> Fish wishes you [#22AA22]a merry Christmas!`, + // ]), + // chatFilterReplacement: { + // message: () => random([ + // `Have a holly jolly Christmas :) <3`, + // `I really hope everyone is jolly for the season! :D`, + // `All I want for Christmaaaaaaas is everyone having a fun time! :)`, + // `Remember to be nice in chat: Santa is watching! <3`, + // `All I want for Christmas is Fish! >|||>`, + // ]), + // highlight: () => random([ + // `[#22AA22]`, `[#EC4444]`, `[#FFFFFF]` + // ]), + }, + dataFetchFailed: "[scarlet]\u26A0 Data fetch failed!\n[white]Please disconnect and rejoin the server if you encounter further issues, such as missing rank or statistics.", +}; +//TODO use this +exports.FColor = (function (data) { + return Object.fromEntries(Object.entries(data).map(function (_a) { + var _b = __read(_a, 2), k = _b[0], c = _b[1]; + return [k, function (str) { + var varChunks = []; + for (var _i = 1; _i < arguments.length; _i++) { + varChunks[_i - 1] = arguments[_i]; + } + return str != null ? + "".concat(c).concat(Array.isArray(str) ? String.raw.apply(String, __spreadArray([{ raw: str }], __read(varChunks.map(function (v) { return String(v) + c; })), false)) : str, "[]") + : c; + } + ]; + })); +})({ + discord: "[#7289DA]", + /** Used for tips and welcome messages. */ + tip: "[gold]", + member: "[pink]", + achievement: "[lime]", +}); +/** Tips that are shown to players randomly. */ +exports.tips = { + ads: [ + "".concat(exports.FColor.member(templateObject_1 || (templateObject_1 = __makeTemplateObject(["Fish Membership"], ["Fish Membership"]))), " subscribers can access the ").concat(exports.FColor.member(templateObject_2 || (templateObject_2 = __makeTemplateObject(["/pet"], ["/pet"]))), " command, which spawns a merui that follows you around. Get a Fish Membership at[sky] ").concat(exports.text.membershipURL, " []"), + "".concat(exports.FColor.member(templateObject_3 || (templateObject_3 = __makeTemplateObject(["Fish Membership"], ["Fish Membership"]))), " subscribers can use the ").concat(exports.FColor.member(templateObject_4 || (templateObject_4 = __makeTemplateObject(["/highlight"], ["/highlight"]))), " command, which turns your chat messages to a color of your choice. Get a Fish Membership at[sky] ").concat(exports.text.membershipURL, " []"), + "".concat(exports.FColor.member(templateObject_5 || (templateObject_5 = __makeTemplateObject(["Fish Membership"], ["Fish Membership"]))), " subscribers can use the ").concat(exports.FColor.member(templateObject_6 || (templateObject_6 = __makeTemplateObject(["/rainbow"], ["/rainbow"]))), " command, which makes your name flash different colors. Get a Fish Membership at[sky] ").concat(exports.text.membershipURL, " []"), + "Want to support the server and get some perks? Get a ".concat(exports.FColor.member(templateObject_7 || (templateObject_7 = __makeTemplateObject(["Fish Membership"], ["Fish Membership"]))), " at[sky] ").concat(exports.text.membershipURL, " []"), + "Join our ".concat(exports.FColor.discord(templateObject_8 || (templateObject_8 = __makeTemplateObject(["Discord server"], ["Discord server"]))), "[]! ").concat(exports.FColor.discord(exports.text.discordURL), " or type ").concat(exports.FColor.discord(templateObject_9 || (templateObject_9 = __makeTemplateObject(["/discord"], ["/discord"])))), + ], + normal: [ + //commands + "You can spawn an [scarlet]Ohno[] with the [scarlet]/ohno[] command. Ohnos are harmless creatures that were created by fusing an alpha and an atrax.", + "Ohnos cannot be spawned near enemy buildings, because they are peaceful and do not want to be used for attacks.", + "You can use [white]/tp[] to teleport directly to any other player! (But only when you're in a core unit)", + "You can unload bulk conveyors (\uF819 or \uF732) with unloaders (\uF864 or \uF731).", + "Hate boulders? You can remove them with [white]/clean[].", + "You can check our rules at any time by running [white]/rules[].", + // `You can kill your unit by running [white]/die[].`, + "We have a tilelog system to help catch griefers. Run [white]/tilelog[], then click a tile to see what's happened there.", + "Run [white]/tilelog 1[] to check the tile history of multiple tiles.", + "Tilelog stores when a building is placed, broken, rotated, configured, and picked up/dropped by a payload unit. Access it with [white]/tilelog[]", + "Tilelog doesn't just log tile actions, it also logs unit deaths! Access it with [white]/tilelog[]", + "Did someone kill a T5 with commands? Run [white]/aoelog 0 15 killed[] to check tilelogs for unit deaths in a large area.", + "Aoelog can show the history of tiles in an area. Select the opposite corners of a rectangle to view the history of its tiles.", + "Aoelog is the plural version of tilelog, access it via [white]/aoelog[]", + "You can mark yourself as AFK(away from keyboard) with [white]/afk[].", + "Run /survival, /attack, /pvp, /sandbox, /hexed or /minigame to quickly change to another server.", + "Need to get rid of an active griefer? Use [#6FFC7C]/s[] to send a message to all staff members across all servers.", + "Use [white]/help to get more information about a specific command.", + "If you want to send a message to just one player, you can use the [white]/msg[] command.", + "Use [white]/r[] to reply to a message sent by another player.", + "[white]/trail[] can be used to give your unit a trail of particle effects.", + "Run [white]/ranks[] to see all the ranks on our server.", + "Is someone impersonating a staff member? Run [white]/rank[] to see their real rank.", + "Don't like the map? Vote to change it with [white]/rtv[].", + "If you want to end the current map, DO NOT BREAK DEFENCES! Vote to change the map with [white]/rtv[].", + //misc + "Anyone attempting to impersonate a ranked player, or the server, will have [scarlet]SUSSY IMPOSTOR[] prepended to their name. Beware!", + "Griefers will often be found with the text ".concat(exports.prefixes.marked, " prepended to their name: they are harmless and cannot grief again."), + "Don't votekick ".concat(exports.prefixes.marked.slice(0, -3), "[][scarlet]s [gold]if they aren't breaking the rules: they are incapable of griefing more."), + "Players marked as ".concat(exports.prefixes.flagged, " have been flagged as suspicious by our detection systems, but they may not be griefers."), + "Need to appeal a moderation action? Join the discord at ".concat(exports.FColor.discord(exports.text.discordURL), " or type /discord"), + "Want to send the phrase [white]\"/command\"[] in chat? Type [white]\"./command\"[] and the [white].[] will be removed.", + "All commands with a player as an argument support using a menu to specify the player. Just run the command leaving the argument blank (using two spaces if necessary), and a menu will show up.", + "Players with a ".concat(ranks_1.Rank.trusted.prefix, " in front of their name aren't staff members, but they do have extra powers."), + "Staff members will have the following prefixes in front of their name: ".concat(ranks_1.Rank.manager.prefix, ", ").concat(ranks_1.Rank.admin.prefix, ", ").concat(ranks_1.Rank.mod.prefix), + "Wave cooldown too long? Skip the wait with [white]/vnw[]", + "You can tell new players not to break power voids with [white]/void[]", + "You can add [pink]color[] to things with color tags! Try typing \"[[".concat(["pink", "green", "cyan", "acid", "royal", "coral"][Math.floor(Math.random() * 6)], "]Hello\" in chat, and see what happens!") + ], + christmas: [ + "Remember to be nice in-game, Santa is watching!", + "Santa's checking his list, so be nice!", + "Have a merry christmas and a happy new year!", + "Fish becomes a bit more jolly around Christmastime!", + "Many server maps have been changed for the season.", + ], + staff: [], +}; +exports.rules = [ + "# 1: [red]No griefing. This refers to intentionally hurting your own team in any way.", + "# 2: [orange]False votekicking isn't allowed. Avoid votekicking if there's an active staff member in the server.", + "# 3: [yellow]Gore, pornography, suggestive content and jokes, and flashing images aren't allowed here. Being horny and a creep in chat will result in a ban.", + "# 4: [green]Do not harass other people. We have zero tolerance for any bigotry. Please respect everyone.", + "# 5: [#00D8D8]Spamming is prohibited. Be reasonable with messaging staff in-game. Misuse may result in a mute.", + "# 6: [blue]Impersonating people or ranks is prohibited.", + "# 7: [purple]Talking about controversial or sensitive topics is not allowed in-game. Hate symbols, such as swastikas, are not permitted.", + "# 8: [pink]No uncomfortable trolling or intentionally causing chaos. This includes any actions or messages that create an unpleasant atmosphere.", + "Failure to follow these rules will result in consequences: likely a ".concat(exports.prefixes.marked, " tag for any game disruption, mute for broken chat rules, and bans for repeated offenses or bypasses.") +].map(function (r) { return "[white]".concat(r); }); +/** Map tag key for maps that opt out of multi-wave skipping (Issue #151). + * Maps with this tag set to "true" will only allow 1 wave to be skipped per /vnw call. */ +exports.noMultiVnwTag = "no-multi-vnw"; +var templateObject_1, templateObject_2, templateObject_3, templateObject_4, templateObject_5, templateObject_6, templateObject_7, templateObject_8, templateObject_9; +//#endregion diff --git a/build/scripts/files.js b/build/scripts/files.js index 350e2975..f08cfe8f 100644 --- a/build/scripts/files.js +++ b/build/scripts/files.js @@ -1,96 +1,96 @@ -"use strict"; -/* -Copyright © BalaM314, 2026. All Rights Reserved. -This file contains the code for automated map syncing. -Original contributor: @author Jurorno9 -Maintenance: @author BalaM314 -*/ -Object.defineProperty(exports, "__esModule", { value: true }); -exports.updateMaps = updateMaps; -var config_1 = require("/config"); -var funcs_1 = require("/funcs"); -var promise_1 = require("/promise"); -var utils_1 = require("/utils"); -//if we switch to a self-hosted setup, just make it respond with the githubfile object for a drop-in replacement -function fetchGithubContents() { - return new promise_1.Promise(function (resolve, reject) { - var url = config_1.mapRepoURLs[config_1.Gamemode.name()]; - if (!url) - return reject("No recognized gamemode detected. please enter \"host \" and try again"); - Http.get(url, function (res) { - try { - //Trust github to return valid JSON data - resolve(JSON.parse(res.getResultAsString())); - } - catch (e) { - reject("Failed to parse GitHub repository contents: ".concat(String(e))); - } - }, function () { return reject("Network error while fetching github repository contents"); }); - }); -} -function downloadFile(address, filename) { - if (!/^https?:\/\//i.test(address)) { - (0, funcs_1.crash)("Invalid address, please start with 'http://' or 'https://'"); - } - return new promise_1.Promise(function (resolve, reject) { - var instream = null; - var outstream = null; - Log.info("Downloading ".concat(filename, "...")); - Http.get(address, function (res) { - try { - instream = res.getResultAsStream(); - outstream = new Fi(filename).write(); - instream.transferTo(outstream); - resolve(); - } - finally { - instream === null || instream === void 0 ? void 0 : instream.close(); - outstream === null || outstream === void 0 ? void 0 : outstream.close(); - } - }, function () { - Log.err("Download failed."); - reject("Network error while downloading a map file: ".concat(address)); - }); - }); -} -function downloadMaps(githubListing) { - return promise_1.Promise.all(githubListing.map(function (fileEntry) { - if (!(typeof fileEntry.download_url == "string")) { - Log.warn("Map ".concat(fileEntry.name, " has no valid download link, skipped.")); - return promise_1.Promise.resolve(null); - } - return downloadFile(fileEntry.download_url, Vars.customMapDirectory.child(fileEntry.name).absolutePath()); - })).then(function (v) { }); -} -/** - * @returns whether any maps were changed - */ -function updateMaps() { - //get github map listing - return fetchGithubContents().then(function (listing) { - //filter only valid mindustry maps - var mapList = listing - .filter(function (entry) { return entry.type == 'file'; }) - .filter(function (entry) { return entry.name.endsWith(".msav"); }); - var mapFiles = Vars.customMapDirectory.list(); - var mapsToDelete = mapFiles.filter(function (localFile) { - return !mapList.some(function (remoteFile) { - return remoteFile.name === localFile.name(); - }) - && !localFile.name().startsWith("$$"); - }); - mapsToDelete.forEach(function (map) { return map.delete(); }); - var mapsToDownload = mapList - .filter(function (entry) { - var file = Vars.customMapDirectory.child(entry.name); - return !file.exists() || entry.sha !== (0, utils_1.getHash)(file); //sha'd - }); - if (mapsToDownload.length == 0) { - return mapsToDelete.length > 0 ? true : false; - } - return downloadMaps(mapsToDownload).then(function () { - Vars.maps.reload(); - return true; - }); - }); -} +"use strict"; +/* +Copyright © BalaM314, 2026. All Rights Reserved. +This file contains the code for automated map syncing. +Original contributor: @author Jurorno9 +Maintenance: @author BalaM314 +*/ +Object.defineProperty(exports, "__esModule", { value: true }); +exports.updateMaps = updateMaps; +var config_1 = require("/config"); +var funcs_1 = require("/funcs"); +var promise_1 = require("/promise"); +var utils_1 = require("/utils"); +//if we switch to a self-hosted setup, just make it respond with the githubfile object for a drop-in replacement +function fetchGithubContents() { + return new promise_1.Promise(function (resolve, reject) { + var url = config_1.mapRepoURLs[config_1.Gamemode.name()]; + if (!url) + return reject("No recognized gamemode detected. please enter \"host \" and try again"); + Http.get(url, function (res) { + try { + //Trust github to return valid JSON data + resolve(JSON.parse(res.getResultAsString())); + } + catch (e) { + reject("Failed to parse GitHub repository contents: ".concat(String(e))); + } + }, function () { return reject("Network error while fetching github repository contents"); }); + }); +} +function downloadFile(address, filename) { + if (!/^https?:\/\//i.test(address)) { + (0, funcs_1.crash)("Invalid address, please start with 'http://' or 'https://'"); + } + return new promise_1.Promise(function (resolve, reject) { + var instream = null; + var outstream = null; + Log.info("Downloading ".concat(filename, "...")); + Http.get(address, function (res) { + try { + instream = res.getResultAsStream(); + outstream = new Fi(filename).write(); + instream.transferTo(outstream); + resolve(); + } + finally { + instream === null || instream === void 0 ? void 0 : instream.close(); + outstream === null || outstream === void 0 ? void 0 : outstream.close(); + } + }, function () { + Log.err("Download failed."); + reject("Network error while downloading a map file: ".concat(address)); + }); + }); +} +function downloadMaps(githubListing) { + return promise_1.Promise.all(githubListing.map(function (fileEntry) { + if (!(typeof fileEntry.download_url == "string")) { + Log.warn("Map ".concat(fileEntry.name, " has no valid download link, skipped.")); + return promise_1.Promise.resolve(null); + } + return downloadFile(fileEntry.download_url, Vars.customMapDirectory.child(fileEntry.name).absolutePath()); + })).then(function (v) { }); +} +/** + * @returns whether any maps were changed + */ +function updateMaps() { + //get github map listing + return fetchGithubContents().then(function (listing) { + //filter only valid mindustry maps + var mapList = listing + .filter(function (entry) { return entry.type == 'file'; }) + .filter(function (entry) { return entry.name.endsWith(".msav"); }); + var mapFiles = Vars.customMapDirectory.list(); + var mapsToDelete = mapFiles.filter(function (localFile) { + return !mapList.some(function (remoteFile) { + return remoteFile.name === localFile.name(); + }) + && !localFile.name().startsWith("$$"); + }); + mapsToDelete.forEach(function (map) { return map.delete(); }); + var mapsToDownload = mapList + .filter(function (entry) { + var file = Vars.customMapDirectory.child(entry.name); + return !file.exists() || entry.sha !== (0, utils_1.getHash)(file); //sha'd + }); + if (mapsToDownload.length == 0) { + return mapsToDelete.length > 0 ? true : false; + } + return downloadMaps(mapsToDownload).then(function () { + Vars.maps.reload(); + return true; + }); + }); +} diff --git a/build/scripts/fjsContext.js b/build/scripts/fjsContext.js index ba28a49d..50f5cfdd 100644 --- a/build/scripts/fjsContext.js +++ b/build/scripts/fjsContext.js @@ -1,101 +1,101 @@ -"use strict"; -/* -Copyright © BalaM314, 2026. All Rights Reserved. -This file contains the context for the "fjs" command, -which executes code with access to the plugin's internals. -*/ -Object.defineProperty(exports, "__esModule", { value: true }); -exports.runJS = runJS; -var achievements = require("/achievements"); -var api = require("/api"); -var commands = require("/frameworks/commands"); -var config = require("/config"); -var consoleCommands = require("/commands/console").commands; -var files = require("/files"); -var funcs = require("/funcs"); -var globals = require("/globals"); -var io = require("/frameworks/io"); -var maps = require("/maps"); -var memberCommands = require("/commands/member").commands; -var menus = require("/frameworks/menus"); -var packetHandlers = require("/packetHandlers"); -var playerCommands = require("/commands/general").commands; -var players = require("/players"); -var ranks = require("/ranks"); -var staffCommands = require("/commands/staff").commands; -var timers = require("/timers"); -var utils = require("/utils"); -var votes = require("/votes"); -var Promise = require("/promise").Promise; -var Achievement = achievements.Achievement, Achievements = achievements.Achievements; -var Perm = commands.Perm, allCommands = commands.allCommands; -var bannedWords = config.bannedWords, FishServer = config.FishServer, Mode = config.Mode, Gamemode = config.Gamemode, FColor = config.FColor, mapRepoURLs = config.mapRepoURLs; -var FishPlayer = players.FishPlayer; -var Serializer = io.Serializer; -var FishEvents = globals.FishEvents, fishPlugin = globals.fishPlugin, fishState = globals.fishState, tileHistory = globals.tileHistory; -var FMap = maps.FMap; -var Rank = ranks.Rank, RoleFlag = ranks.RoleFlag; -var Menu = menus.Menu; -Object.assign(this, utils, funcs); //global scope goes brrrrr, I'm sure this will not cause any bugs whatsoever -var Ranks = null; -var $ = Object.assign(function $(input) { - if (typeof input == "string") { - if (Pattern.matches("[a-zA-Z0-9+/]{22}==", input)) { - return FishPlayer.getById(input); - } - } - return null; -}, { - sussy: true, - info: function (input) { - if (typeof input == "string") { - if (Pattern.matches("[a-zA-Z0-9+/]{22}==", input)) { - return Vars.netServer.admins.getInfo(input); - } - } - return null; - }, - create: function (input) { - if (typeof input == "string") { - if (Pattern.matches("[a-zA-Z0-9+/]{22}==", input)) { - return FishPlayer.getFromInfo(Vars.netServer.admins.getInfo(input)); - } - } - return null; - }, - me: null, - meM: null, -}); -/** Used to persist variables. */ -var vars = {}; -function runJS(input, outputFunction, errorFunction, player) { - if (outputFunction === void 0) { outputFunction = Log.info; } - if (errorFunction === void 0) { errorFunction = Log.err; } - if (player) { - $.me = player; - $.meM = player.player; - } - else if (Groups.player.size() == 1) { - $.meM = Groups.player.first(); - $.me = players.FishPlayer.get($.meM); - } - try { - var admins = Vars.netServer.admins; - var output = eval(input); - if (output instanceof Array) { - outputFunction("&cArray: [&fr" + output.join(", ") + "&c]&fr"); - } - else if (output === undefined) { - outputFunction("undefined"); - } - else if (output === null) { - outputFunction("null"); - } - else { - outputFunction(output); - } - } - catch (err) { - errorFunction(err); - } -} +"use strict"; +/* +Copyright © BalaM314, 2026. All Rights Reserved. +This file contains the context for the "fjs" command, +which executes code with access to the plugin's internals. +*/ +Object.defineProperty(exports, "__esModule", { value: true }); +exports.runJS = runJS; +var achievements = require("/achievements"); +var api = require("/api"); +var commands = require("/frameworks/commands"); +var config = require("/config"); +var consoleCommands = require("/commands/console").commands; +var files = require("/files"); +var funcs = require("/funcs"); +var globals = require("/globals"); +var io = require("/frameworks/io"); +var maps = require("/maps"); +var memberCommands = require("/commands/member").commands; +var menus = require("/frameworks/menus"); +var packetHandlers = require("/packetHandlers"); +var playerCommands = require("/commands/general").commands; +var players = require("/players"); +var ranks = require("/ranks"); +var staffCommands = require("/commands/staff").commands; +var timers = require("/timers"); +var utils = require("/utils"); +var votes = require("/votes"); +var Promise = require("/promise").Promise; +var Achievement = achievements.Achievement, Achievements = achievements.Achievements; +var Perm = commands.Perm, allCommands = commands.allCommands; +var bannedWords = config.bannedWords, FishServer = config.FishServer, Mode = config.Mode, Gamemode = config.Gamemode, FColor = config.FColor, mapRepoURLs = config.mapRepoURLs; +var FishPlayer = players.FishPlayer; +var Serializer = io.Serializer; +var FishEvents = globals.FishEvents, fishPlugin = globals.fishPlugin, fishState = globals.fishState, tileHistory = globals.tileHistory; +var FMap = maps.FMap; +var Rank = ranks.Rank, RoleFlag = ranks.RoleFlag; +var Menu = menus.Menu; +Object.assign(this, utils, funcs); //global scope goes brrrrr, I'm sure this will not cause any bugs whatsoever +var Ranks = null; +var $ = Object.assign(function $(input) { + if (typeof input == "string") { + if (Pattern.matches("[a-zA-Z0-9+/]{22}==", input)) { + return FishPlayer.getById(input); + } + } + return null; +}, { + sussy: true, + info: function (input) { + if (typeof input == "string") { + if (Pattern.matches("[a-zA-Z0-9+/]{22}==", input)) { + return Vars.netServer.admins.getInfo(input); + } + } + return null; + }, + create: function (input) { + if (typeof input == "string") { + if (Pattern.matches("[a-zA-Z0-9+/]{22}==", input)) { + return FishPlayer.getFromInfo(Vars.netServer.admins.getInfo(input)); + } + } + return null; + }, + me: null, + meM: null, +}); +/** Used to persist variables. */ +var vars = {}; +function runJS(input, outputFunction, errorFunction, player) { + if (outputFunction === void 0) { outputFunction = Log.info; } + if (errorFunction === void 0) { errorFunction = Log.err; } + if (player) { + $.me = player; + $.meM = player.player; + } + else if (Groups.player.size() == 1) { + $.meM = Groups.player.first(); + $.me = players.FishPlayer.get($.meM); + } + try { + var admins = Vars.netServer.admins; + var output = eval(input); + if (output instanceof Array) { + outputFunction("&cArray: [&fr" + output.join(", ") + "&c]&fr"); + } + else if (output === undefined) { + outputFunction("undefined"); + } + else if (output === null) { + outputFunction("null"); + } + else { + outputFunction(output); + } + } + catch (err) { + errorFunction(err); + } +} diff --git a/build/scripts/frameworks/commands.js b/build/scripts/frameworks/commands.js index 63ab5097..50e79d93 100644 --- a/build/scripts/frameworks/commands.js +++ b/build/scripts/frameworks/commands.js @@ -1,22 +1,22 @@ -"use strict"; -var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { - if (k2 === undefined) k2 = k; - var desc = Object.getOwnPropertyDescriptor(m, k); - if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { - desc = { enumerable: true, get: function() { return m[k]; } }; - } - Object.defineProperty(o, k2, desc); -}) : (function(o, m, k, k2) { - if (k2 === undefined) k2 = k; - o[k2] = m[k]; -})); -var __exportStar = (this && this.__exportStar) || function(m, exports) { - for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p); -}; -Object.defineProperty(exports, "__esModule", { value: true }); -__exportStar(require("/frameworks/commands/commands"), exports); -__exportStar(require("/frameworks/commands/errors"), exports); -__exportStar(require("/frameworks/commands/formatting"), exports); -__exportStar(require("/frameworks/commands/perm"), exports); -__exportStar(require("/frameworks/commands/requirements"), exports); -__exportStar(require("/frameworks/commands/types"), exports); +"use strict"; +var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + var desc = Object.getOwnPropertyDescriptor(m, k); + if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { + desc = { enumerable: true, get: function() { return m[k]; } }; + } + Object.defineProperty(o, k2, desc); +}) : (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + o[k2] = m[k]; +})); +var __exportStar = (this && this.__exportStar) || function(m, exports) { + for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p); +}; +Object.defineProperty(exports, "__esModule", { value: true }); +__exportStar(require("/frameworks/commands/commands"), exports); +__exportStar(require("/frameworks/commands/errors"), exports); +__exportStar(require("/frameworks/commands/formatting"), exports); +__exportStar(require("/frameworks/commands/perm"), exports); +__exportStar(require("/frameworks/commands/requirements"), exports); +__exportStar(require("/frameworks/commands/types"), exports); diff --git a/build/scripts/frameworks/commands/commands.js b/build/scripts/frameworks/commands/commands.js index 5ef64a58..9da2ae6e 100644 --- a/build/scripts/frameworks/commands/commands.js +++ b/build/scripts/frameworks/commands/commands.js @@ -1,775 +1,775 @@ -"use strict"; -/* -Copyright © BalaM314, 2026. All Rights Reserved. -This file contains the commands framework. -For usage information, see docs/framework-usage-guide.md -For maintenance information, see docs/frameworks.md -*/ -//Behold, the power of typescript! -var __assign = (this && this.__assign) || function () { - __assign = Object.assign || function(t) { - for (var s, i = 1, n = arguments.length; i < n; i++) { - s = arguments[i]; - for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p)) - t[p] = s[p]; - } - return t; - }; - return __assign.apply(this, arguments); -}; -var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { - function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } - return new (P || (P = Promise))(function (resolve, reject) { - function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } - function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } - function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } - step((generator = generator.apply(thisArg, _arguments || [])).next()); - }); -}; -var __generator = (this && this.__generator) || function (thisArg, body) { - var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g = Object.create((typeof Iterator === "function" ? Iterator : Object).prototype); - return g.next = verb(0), g["throw"] = verb(1), g["return"] = verb(2), typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g; - function verb(n) { return function (v) { return step([n, v]); }; } - function step(op) { - if (f) throw new TypeError("Generator is already executing."); - while (g && (g = 0, op[0] && (_ = 0)), _) try { - if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t; - if (y = 0, t) op = [op[0] & 2, t.value]; - switch (op[0]) { - case 0: case 1: t = op; break; - case 4: _.label++; return { value: op[1], done: false }; - case 5: _.label++; y = op[1]; op = [0]; continue; - case 7: op = _.ops.pop(); _.trys.pop(); continue; - default: - if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; } - if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; } - if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; } - if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; } - if (t[2]) _.ops.pop(); - _.trys.pop(); continue; - } - op = body.call(thisArg, _); - } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; } - if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true }; - } -}; -var __read = (this && this.__read) || function (o, n) { - var m = typeof Symbol === "function" && o[Symbol.iterator]; - if (!m) return o; - var i = m.call(o), r, ar = [], e; - try { - while ((n === void 0 || n-- > 0) && !(r = i.next()).done) ar.push(r.value); - } - catch (error) { e = { error: error }; } - finally { - try { - if (r && !r.done && (m = i["return"])) m.call(i); - } - finally { if (e) throw e.error; } - } - return ar; -}; -var __values = (this && this.__values) || function(o) { - var s = typeof Symbol === "function" && Symbol.iterator, m = s && o[s], i = 0; - if (m) return m.call(o); - if (o && typeof o.length === "number") return { - next: function () { - if (o && i >= o.length) o = void 0; - return { value: o && o[i++], done: !o }; - } - }; - throw new TypeError(s ? "Object is not iterable." : "Symbol.iterator is not defined."); -}; -var __spreadArray = (this && this.__spreadArray) || function (to, from, pack) { - if (pack || arguments.length === 2) for (var i = 0, l = from.length, ar; i < l; i++) { - if (ar || !(i in from)) { - if (!ar) ar = Array.prototype.slice.call(from, 0, i); - ar[i] = from[i]; - } - } - return to.concat(ar || Array.prototype.slice.call(from)); -}; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.consoleCommandList = exports.commandList = exports.allConsoleCommands = exports.allCommands = void 0; -exports.command = command; -exports.processArgString = processArgString; -exports.formatArg = formatArg; -exports.joinArgs = joinArgs; -exports.disambiguateArgument = disambiguateArgument; -exports.processArgs = processArgs; -exports.convertArgs = convertArgs; -exports.handleTapEvent = handleTapEvent; -exports.register = register; -exports.registerConsole = registerConsole; -exports.initialize = initialize; -exports.reset = reset; -var config_1 = require("/config"); -var errors_1 = require("/frameworks/commands/errors"); -var formatting_1 = require("/frameworks/commands/formatting"); -var types_1 = require("/frameworks/commands/types"); -var menus_1 = require("/frameworks/menus"); -var funcs_1 = require("/funcs"); -var globals_1 = require("/globals"); -var players_1 = require("/players"); -var ranks_1 = require("/ranks"); -var utils_1 = require("/utils"); -var hiddenUnauthorizedMessage = "[scarlet]Unknown command. Check [lightgray]/help[scarlet]."; -/** Flag to prevent double initialization */ -var initialized = false; -/** Stores all chat comamnds by their name. */ -exports.allCommands = {}; -/** Stores all console commands by their name. */ -exports.allConsoleCommands = {}; -/** Stores the last usage data for chat commands by their name. */ -var globalUsageData = {}; -/** Helper function to get the correct type for command lists. */ -var commandList = function (list) { return list; }; -exports.commandList = commandList; -/** Helper function to get the correct type for command lists. */ -var consoleCommandList = function (list) { return list; }; -exports.consoleCommandList = consoleCommandList; -/** - * Helper function to get the correct type definitions for commands that use "data" or init(). - * Necessary because, while typescript is capable of inferring A1, A2... - * ``` - * { - * prop1: Type; - * prop2: Type; - * } - * ``` - * it cannot handle inferring A1 and B1. - * ``` - * { - * prop1: Type; - * prop2: Type; - * } - * ``` - */ -function command(input) { - return input; -} -/** Takes an arg string, like `reason:string?` and converts it to a CommandArg. */ -function processArgString(str) { - //this was copypasted from mlogx haha - var matchResult = str.match(/(\w+):(\w+)(\?)?/); - if (!matchResult) { - (0, funcs_1.crash)("Bad arg string ".concat(str, ": does not match pattern word:word(?)")); - } - var _a = __read(matchResult, 4), name = _a[1], type = _a[2], isOptional = _a[3]; - if (types_1.commandArgTypes.includes(type)) { - return { name: name, type: type, isOptional: !!isOptional }; - } - else { - (0, funcs_1.crash)("Bad arg string ".concat(str, ": invalid type ").concat(type)); - } -} -function formatArg(a) { - var isOptional = a.at(-1) == "?"; - var brackets = isOptional ? ["[", "]"] : ["<", ">"]; - return brackets[0] + a.split(":")[0] + brackets[1]; -} -/** Joins multi-word arguments that have been groups with quotes. Ex: turns [`"a`, `b"`] into [`a b`]*/ -function joinArgs(rawArgs) { - var e_1, _a; - var outputArgs = []; - var groupedArg = null; - try { - for (var rawArgs_1 = __values(rawArgs), rawArgs_1_1 = rawArgs_1.next(); !rawArgs_1_1.done; rawArgs_1_1 = rawArgs_1.next()) { - var arg = rawArgs_1_1.value; - if (arg.startsWith("\"") && groupedArg == null) { - groupedArg = []; - } - if (groupedArg) { - groupedArg.push(arg); - if (arg.endsWith("\"")) { - outputArgs.push(groupedArg.join(" ").slice(1, -1)); - groupedArg = null; - } - } - else { - outputArgs.push(arg); - } - } - } - catch (e_1_1) { e_1 = { error: e_1_1 }; } - finally { - try { - if (rawArgs_1_1 && !rawArgs_1_1.done && (_a = rawArgs_1.return)) _a.call(rawArgs_1); - } - finally { if (e_1) throw e_1.error; } - } - if (groupedArg != null) { - //return `Unterminated string literal.`; - outputArgs.push(groupedArg.join(" ")); - } - return outputArgs; -} -function disambiguateArgument(options_1, arg_1, _a, sender_1, outputArgs_1, optionStringifier_1) { - return __awaiter(this, arguments, void 0, function (options, arg, _b, sender, outputArgs, optionStringifier, columns) { - var word, a_an_word, _c, _d; - var name = _b.name, type = _b.type; - if (columns === void 0) { columns = 3; } - return __generator(this, function (_e) { - switch (_e.label) { - case 0: - if (!(options == null)) return [3 /*break*/, 1]; - (0, errors_1.fail)("".concat((0, funcs_1.capitalizeText)(types_1.commandArgNames[type]), " \"").concat(arg, "\" not found.")); - return [3 /*break*/, 4]; - case 1: - if (!(options instanceof Array)) return [3 /*break*/, 3]; - word = types_1.commandArgNames[type]; - if (!sender) - (0, errors_1.fail)("Name \"".concat(arg, "\" could refer to more than one ").concat(word, ".")); - a_an_word = (0, funcs_1.indefiniteArticle)(word); - _c = outputArgs; - _d = name; - return [4 /*yield*/, menus_1.Menu.menu("Select ".concat(a_an_word), "Select ".concat(a_an_word, " for the argument \"").concat(name, "\""), options, sender, { - includeCancel: true, - optionStringifier: optionStringifier, - columns: columns, - })]; - case 2: - _c[_d] = _e.sent(); - return [3 /*break*/, 4]; - case 3: - outputArgs[name] = options; - _e.label = 4; - case 4: return [2 /*return*/]; - } - }); - }); -} -var argsSupportingBlank = ["player", "offlinePlayer", "unittype", "map", "mapOrRandom", "rank", "roleflag", "item", "team"]; -/** Takes a list of joined args passed to the command, and processes it, turning it into a kwargs style object. */ -function processArgs(args, processedCmdArgs, sender) { - return __awaiter(this, void 0, void 0, function () { - var outputArgs, _a, _b, _c, i, cmdArg, commonArgs, _d, player, num, number, milliseconds, block, e_2_1; - var e_2, _e; - var _f; - return __generator(this, function (_g) { - switch (_g.label) { - case 0: - outputArgs = {}; - _g.label = 1; - case 1: - _g.trys.push([1, 35, 36, 37]); - _a = __values(processedCmdArgs.entries()), _b = _a.next(); - _g.label = 2; - case 2: - if (!!_b.done) return [3 /*break*/, 34]; - _c = __read(_b.value, 2), i = _c[0], cmdArg = _c[1]; - if (!(i in args) || args[i] === "") { - //if the arg was not provided or it was empty - if (cmdArg.isOptional) { - outputArgs[cmdArg.name] = undefined; - return [3 /*break*/, 33]; - } - else if (sender && argsSupportingBlank.includes(cmdArg.type)) { - //it will be resolved later - } - else { - (0, errors_1.fail)("No value specified for arg ".concat(cmdArg.name, ". Did you type two spaces instead of one?")); - } - } - commonArgs = [args[i], cmdArg, sender, outputArgs]; - _d = cmdArg.type; - switch (_d) { - case "player": return [3 /*break*/, 3]; - case "offlinePlayer": return [3 /*break*/, 5]; - case "team": return [3 /*break*/, 10]; - case "number": return [3 /*break*/, 14]; - case "time": return [3 /*break*/, 15]; - case "string": return [3 /*break*/, 16]; - case "boolean": return [3 /*break*/, 17]; - case "block": return [3 /*break*/, 18]; - case "unittype": return [3 /*break*/, 19]; - case "uuid": return [3 /*break*/, 21]; - case "map": return [3 /*break*/, 22]; - case "mapOrRandom": return [3 /*break*/, 24]; - case "rank": return [3 /*break*/, 26]; - case "roleflag": return [3 /*break*/, 28]; - case "item": return [3 /*break*/, 30]; - } - return [3 /*break*/, 32]; - case 3: return [4 /*yield*/, disambiguateArgument.apply(void 0, __spreadArray(__spreadArray([players_1.FishPlayer.search(players_1.FishPlayer.getAllOnline(), args[i])], __read(commonArgs), false), [function (player) { return (player.marked() ? config_1.prefixes.marked : player.autoflagged ? config_1.prefixes.flagged : "") + (Strings.stripColors(player.name).length >= 3 ? - player.name - : (0, funcs_1.escapeStringColorsClient)(player.name)); }, - 2], false))]; - case 4: - _g.sent(); - return [3 /*break*/, 33]; - case 5: - if (!globals_1.uuidPattern.test(args[i])) return [3 /*break*/, 6]; - player = players_1.FishPlayer.getById(args[i]); - if (player == null) - (0, errors_1.fail)("Player with uuid \"".concat(args[i], "\" not found. Specify \"create:").concat(args[i], "\" to create the player.")); - outputArgs[cmdArg.name] = player; - return [3 /*break*/, 9]; - case 6: - if (!(args[i].startsWith("create:") && globals_1.uuidPattern.test(args[i].split("create:")[1]))) return [3 /*break*/, 7]; - outputArgs[cmdArg.name] = players_1.FishPlayer.getFromInfo(Vars.netServer.admins.getInfo(args[i].split("create:")[1])); - return [3 /*break*/, 9]; - case 7: return [4 /*yield*/, disambiguateArgument.apply(void 0, __spreadArray(__spreadArray([players_1.FishPlayer.search(Object.values(players_1.FishPlayer.cachedPlayers), args[i])], __read(commonArgs), false), [function (player) { return Strings.stripColors(player.name).length >= 3 ? - player.name - : (0, funcs_1.escapeStringColorsClient)(player.name); }, - 2], false))]; - case 8: - _g.sent(); - _g.label = 9; - case 9: return [3 /*break*/, 33]; - case 10: - num = void 0; - if (!(args[i] && (!isNaN(num = Number(args[i])) || - args[i].slice(1) && !isNaN(num = Number(args[i].slice(1))) || //discard leading # - args[i].slice(5) && !isNaN(num = Number(args[i].slice(5))) //discard leading team# - ))) return [3 /*break*/, 11]; - if (num <= 255 && num >= 0 && Number.isInteger(num)) - outputArgs[cmdArg.name] = Team.all[num]; - else - (0, errors_1.fail)("Team ".concat(num, " is not inside the valid range (integers 0-255).")); - return [3 /*break*/, 13]; - case 11: return [4 /*yield*/, disambiguateArgument.apply(void 0, __spreadArray(__spreadArray([(0, utils_1.getTeam)(args[i])], __read(commonArgs), false), [function (t) { return t.coloredName(); }], false))]; - case 12: - _g.sent(); - _g.label = 13; - case 13: return [3 /*break*/, 33]; - case 14: - { - number = Number(args[i]); - if (isNaN(number)) { - if (/\(\d+,/.test(args[i])) - number = Number(args[i].slice(1, -1)); - else if (/\d+\)/.test(args[i])) - number = Number(args[i].slice(0, -1)); - if (isNaN(number)) - (0, errors_1.fail)("Invalid number \"".concat(args[i], "\"")); - } - outputArgs[cmdArg.name] = number; - return [3 /*break*/, 33]; - } - _g.label = 15; - case 15: - { - milliseconds = (0, utils_1.parseTimeString)(args[i]); - if (milliseconds == null) - (0, errors_1.fail)("Invalid time string \"".concat(args[i], "\"")); - outputArgs[cmdArg.name] = milliseconds; - return [3 /*break*/, 33]; - } - _g.label = 16; - case 16: - outputArgs[cmdArg.name] = args[i]; - return [3 /*break*/, 33]; - case 17: - switch (args[i].toLowerCase()) { - case "true": - case "yes": - case "yeah": - case "ya": - case "ye": - case "t": - case "y": - case "1": - outputArgs[cmdArg.name] = true; - break; - case "false": - case "no": - case "nah": - case "nay": - case "nope": - case "f": - case "n": - case "0": - outputArgs[cmdArg.name] = false; - break; - default: (0, errors_1.fail)("Argument ".concat(args[i], " is not a boolean. Try \"true\" or \"false\".")); - } - return [3 /*break*/, 33]; - case 18: - { - block = (0, utils_1.getBlock)(args[i], "air"); - if (typeof block == "string") - (0, errors_1.fail)(block); - outputArgs[cmdArg.name] = block; - return [3 /*break*/, 33]; - } - _g.label = 19; - case 19: return [4 /*yield*/, disambiguateArgument.apply(void 0, __spreadArray(__spreadArray([(0, utils_1.getUnitType)(args[i])], __read(commonArgs), false), [function (u) { return u.emoji() + (0, funcs_1.capitalizeText)(u.name); }], false))]; - case 20: - _g.sent(); - return [3 /*break*/, 33]; - case 21: - if (!globals_1.uuidPattern.test(args[i])) - (0, errors_1.fail)("Invalid uuid string \"".concat(args[i], "\"")); - outputArgs[cmdArg.name] = args[i]; - return [3 /*break*/, 33]; - case 22: return [4 /*yield*/, disambiguateArgument.apply(void 0, __spreadArray(__spreadArray([(0, utils_1.getMap)(args[i])], __read(commonArgs), false), [function (r) { return r.name(); }, - 2], false))]; - case 23: - _g.sent(); - return [3 /*break*/, 33]; - case 24: - if (["rand", "random"].includes((_f = args[i]) === null || _f === void 0 ? void 0 : _f.toLowerCase())) { - outputArgs[cmdArg.name] = "random"; - return [3 /*break*/, 33]; - } - return [4 /*yield*/, disambiguateArgument.apply(void 0, __spreadArray(__spreadArray([(0, utils_1.getMap)(args[i])], __read(commonArgs), false), [function (r) { return r.name(); }, - 2], false))]; - case 25: - _g.sent(); - return [3 /*break*/, 33]; - case 26: return [4 /*yield*/, disambiguateArgument.apply(void 0, __spreadArray(__spreadArray([ranks_1.Rank.search(args[i])], __read(commonArgs), false), [function (r) { return r.coloredName(); }], false))]; - case 27: - _g.sent(); - return [3 /*break*/, 33]; - case 28: return [4 /*yield*/, disambiguateArgument.apply(void 0, __spreadArray(__spreadArray([ranks_1.RoleFlag.search(args[i])], __read(commonArgs), false), [function (f) { return f.coloredName(); }], false))]; - case 29: - _g.sent(); - return [3 /*break*/, 33]; - case 30: return [4 /*yield*/, disambiguateArgument.apply(void 0, __spreadArray(__spreadArray([(0, utils_1.getItem)(args[i])], __read(commonArgs), false), [function (i) { return i.emoji() + (0, funcs_1.capitalizeText)(i.name, "-"); }, - 2], false))]; - case 31: - _g.sent(); - return [3 /*break*/, 33]; - case 32: - cmdArg.type; - (0, funcs_1.crash)("impossible"); - _g.label = 33; - case 33: - _b = _a.next(); - return [3 /*break*/, 2]; - case 34: return [3 /*break*/, 37]; - case 35: - e_2_1 = _g.sent(); - e_2 = { error: e_2_1 }; - return [3 /*break*/, 37]; - case 36: - try { - if (_b && !_b.done && (_e = _a.return)) _e.call(_a); - } - finally { if (e_2) throw e_2.error; } - return [7 /*endfinally*/]; - case 37: return [2 /*return*/, outputArgs]; - } - }); - }); -} -var variadicArgumentTypes = ["player", "string", "map", "mapOrRandom"]; -function isArgOptional(arg, allowMenus) { - return arg.isOptional || (argsSupportingBlank.includes(arg.type) && allowMenus); -} -/** Converts the CommandArg[] to the format accepted by Arc CommandHandler */ -function convertArgs(processedCmdArgs, allowMenus) { - return processedCmdArgs.map(function (arg, index, array) { - var isOptional = isArgOptional(arg, allowMenus) && - !array.slice(index + 1).some(function (c) { return !isArgOptional(c, allowMenus); }); //this is enforced by the arc command handler - //TODO internalize command handler - var brackets = isOptional ? ["[", "]"] : ["<", ">"]; - //if the arg is a string and last argument, make it variadic (so if `/warn player a b c d` is run, the last arg is "a b c d" not "a") - return brackets[0] + arg.name + (variadicArgumentTypes.includes(arg.type) && index + 1 == array.length ? "..." : "") + brackets[1]; - }).join(" "); -} -function handleTapEvent(event) { - var _a; - var sender = players_1.FishPlayer.get(event.player); - if (sender.tapInfo.commandName == null) - return; - var command = exports.allCommands[sender.tapInfo.commandName]; - var usageData = sender.getUsageData(sender.tapInfo.commandName); - var handleTapsUpdated = false; - try { - var failed_1 = false; - (_a = command.tapped) === null || _a === void 0 ? void 0 : _a.call(command, { - args: sender.tapInfo.lastArgs, - data: command.data, - outputFail: function (message) { (0, utils_1.outputFail)(message, sender); failed_1 = true; }, - outputSuccess: function (message) { return (0, utils_1.outputSuccess)(message, sender); }, - output: function (message) { return (0, utils_1.outputMessage)(message, sender); }, - f: formatting_1.outputFormatter_client, - admins: Vars.netServer.admins, - commandLastUsed: usageData.lastUsed, - commandLastUsedSuccessfully: usageData.lastUsedSuccessfully, - lastUsed: usageData.tapLastUsed, - lastUsedSuccessfully: usageData.tapLastUsedSuccessfully, - sender: sender, - tile: event.tile, - x: event.tile.x, - y: event.tile.y, - currentTapMode: sender.tapInfo.commandName == null ? "off" : sender.tapInfo.mode, - handleTaps: function (mode) { - if (mode == "off") { - sender.tapInfo.commandName = null; - return; - } - sender.tapInfo.mode = mode; - handleTapsUpdated = true; - }, - }); - if (!failed_1) - usageData.tapLastUsedSuccessfully = Date.now(); - } - catch (err) { - (0, utils_1.handleError)(err, sender, utils_1.outputFail, "".concat(sender.cleanedName, " ran /").concat(sender.tapInfo.commandName, " and tapped")); - } - finally { - if (sender.tapInfo.mode == "once" && !handleTapsUpdated) { - sender.tapInfo.commandName = null; - } - usageData.tapLastUsed = Date.now(); - } -} -/** - * Registers all commands in a list to a client command handler. - **/ -function register(commands, clientHandler, serverHandler) { - var e_3, _a; - var _loop_1 = function (name, _data) { - //Invoke thunk if necessary - var data = typeof _data == "function" ? _data() : _data; - //Process the args - var processedCmdArgs = data.args.map(processArgString); - clientHandler.removeCommand(name); //The function silently fails if the argument doesn't exist so this is safe - clientHandler.register(name, convertArgs(processedCmdArgs, true), data.description, new CommandHandler.CommandRunner({ accept: function (unjoinedRawArgs, sender) { - return __awaiter(this, void 0, void 0, function () { - var fishSender, rawArgs, resolvedArgs, err_1, usageData, failed, args_1, requirements, err_2; - var _a; - return __generator(this, function (_b) { - switch (_b.label) { - case 0: - if (!initialized) - (0, funcs_1.crash)("Commands not initialized!"); - fishSender = players_1.FishPlayer.get(sender); - players_1.FishPlayer.onPlayerCommand(fishSender, name, unjoinedRawArgs); - //Verify authorization - //as a bonus, this crashes if data.perm is undefined - if (!data.perm.check(fishSender)) { - if (data.customUnauthorizedMessage) { - (0, utils_1.outputFail)(data.customUnauthorizedMessage, sender); - globals_1.FishEvents.fire("commandUnauthorized", [fishSender, name]); - } - else if (data.isHidden) - (0, utils_1.outputMessage)(hiddenUnauthorizedMessage, sender); - else - (0, utils_1.outputFail)(data.perm.unauthorizedMessage, sender); - return [2 /*return*/]; - } - rawArgs = joinArgs(unjoinedRawArgs); - _b.label = 1; - case 1: - _b.trys.push([1, 3, , 4]); - return [4 /*yield*/, processArgs(rawArgs, processedCmdArgs, fishSender)]; - case 2: - resolvedArgs = _b.sent(); - return [3 /*break*/, 4]; - case 3: - err_1 = _b.sent(); - (0, utils_1.handleError)(err_1, fishSender, utils_1.outputFail, "".concat(fishSender.cleanedName, " ran /").concat(name)); - return [2 /*return*/]; - case 4: - usageData = fishSender.getUsageData(name); - failed = false; - _b.label = 5; - case 5: - _b.trys.push([5, 7, 8, 9]); - args_1 = { - rawArgs: rawArgs, - args: resolvedArgs, - sender: fishSender, - data: data.data, - outputFail: function (message) { (0, utils_1.outputFail)(message, sender); failed = true; }, - outputSuccess: function (message) { return (0, utils_1.outputSuccess)(message, sender); }, - output: function (message) { return (0, utils_1.outputMessage)(message, sender); }, - f: formatting_1.f_client, - execServer: function (command) { return serverHandler.handleMessage(command); }, - admins: Vars.netServer.admins, - lastUsedSender: usageData.lastUsed, - lastUsedSuccessfullySender: usageData.lastUsedSuccessfully, - lastUsedSuccessfully: ((_a = globalUsageData[name]) !== null && _a !== void 0 ? _a : (globalUsageData[name] = { lastUsed: -1, lastUsedSuccessfully: -1 })).lastUsedSuccessfully, - allCommands: exports.allCommands, - currentTapMode: fishSender.tapInfo.commandName == null ? "off" : fishSender.tapInfo.mode, - handleTaps: function (mode) { - if (data.tapped == undefined) - (0, funcs_1.crash)("No tap handler to activate: command \"".concat(name, "\"")); - if (mode == "off") { - fishSender.tapInfo.commandName = null; - } - else { - fishSender.tapInfo.commandName = name; - fishSender.tapInfo.mode = mode; - } - fishSender.tapInfo.lastArgs = resolvedArgs; - }, - }; - requirements = typeof data.requirements == "function" ? data.requirements(args_1) : data.requirements; - requirements === null || requirements === void 0 ? void 0 : requirements.forEach(function (r) { return r(args_1); }); - return [4 /*yield*/, data.handler(args_1)]; - case 6: - _b.sent(); - //Update usage data - if (!failed) { - usageData.lastUsedSuccessfully = globalUsageData[name].lastUsedSuccessfully = Date.now(); - } - return [3 /*break*/, 9]; - case 7: - err_2 = _b.sent(); - (0, utils_1.handleError)(err_2, fishSender, utils_1.outputFail, "".concat(fishSender.cleanedName, " ran /").concat(name)); - return [3 /*break*/, 9]; - case 8: - usageData.lastUsed = globalUsageData[name].lastUsed = Date.now(); - return [7 /*endfinally*/]; - case 9: return [2 /*return*/]; - } - }); - }); - } })); - exports.allCommands[name] = data; - }; - try { - for (var _b = __values(Object.entries(commands)), _c = _b.next(); !_c.done; _c = _b.next()) { - var _d = __read(_c.value, 2), name = _d[0], _data = _d[1]; - _loop_1(name, _data); - } - } - catch (e_3_1) { e_3 = { error: e_3_1 }; } - finally { - try { - if (_c && !_c.done && (_a = _b.return)) _a.call(_b); - } - finally { if (e_3) throw e_3.error; } - } -} -function registerConsole(commands, serverHandler) { - var e_4, _a; - var _loop_2 = function (name, data) { - //Process the args - var processedCmdArgs = data.args.map(processArgString); - serverHandler.removeCommand(name); //The function silently fails if the argument doesn't exist so this is safe - serverHandler.register(name, convertArgs(processedCmdArgs, false), data.description, new CommandHandler.CommandRunner({ accept: function (rawArgs) { - return __awaiter(this, void 0, void 0, function () { - var resolvedArgs, err_3, usageData, failed_2; - var _a; - var _b; - return __generator(this, function (_c) { - switch (_c.label) { - case 0: - if (!initialized) - (0, funcs_1.crash)("Commands not initialized!"); - _c.label = 1; - case 1: - _c.trys.push([1, 3, , 4]); - return [4 /*yield*/, processArgs(rawArgs, processedCmdArgs, null)]; - case 2: - resolvedArgs = _c.sent(); - return [3 /*break*/, 4]; - case 3: - err_3 = _c.sent(); - //if args are invalid - Log.err(err_3); - return [2 /*return*/]; - case 4: - usageData = ((_a = globalUsageData[_b = "_console_" + name]) !== null && _a !== void 0 ? _a : (globalUsageData[_b] = { lastUsed: -1, lastUsedSuccessfully: -1 })); - try { - failed_2 = false; - data.handler(__assign({ rawArgs: rawArgs, args: resolvedArgs, data: data.data, outputFail: function (message) { (0, utils_1.outputConsole)(message, Log.err); failed_2 = true; }, outputSuccess: utils_1.outputConsole, output: utils_1.outputConsole, f: formatting_1.f_server, execServer: function (command) { return serverHandler.handleMessage(command); }, admins: Vars.netServer.admins }, usageData)); - usageData.lastUsed = Date.now(); - if (!failed_2) - usageData.lastUsedSuccessfully = Date.now(); - } - catch (err) { - usageData.lastUsed = Date.now(); - if (err instanceof errors_1.CommandError) { - Log.warn(typeof err.data == "function" ? err.data("&fr") : err.data); - } - else { - Log.err("&lrAn error occured while executing the command!&fr"); - Log.err((0, funcs_1.parseError)(err)); - } - } - return [2 /*return*/]; - } - }); - }); - } })); - exports.allConsoleCommands[name] = data; - }; - try { - for (var _b = __values(Object.entries(commands)), _c = _b.next(); !_c.done; _c = _b.next()) { - var _d = __read(_c.value, 2), name = _d[0], data = _d[1]; - _loop_2(name, data); - } - } - catch (e_4_1) { e_4 = { error: e_4_1 }; } - finally { - try { - if (_c && !_c.done && (_a = _b.return)) _a.call(_b); - } - finally { if (e_4) throw e_4.error; } - } -} -function initialize() { - var e_5, _a, e_6, _b; - if (initialized) { - (0, funcs_1.crash)("Already initialized commands."); - } - try { - for (var _c = __values(Object.entries(exports.allConsoleCommands)), _d = _c.next(); !_d.done; _d = _c.next()) { - var _e = __read(_d.value, 2), key = _e[0], command_1 = _e[1]; - if (command_1.init) - command_1.data = command_1.init(); - } - } - catch (e_5_1) { e_5 = { error: e_5_1 }; } - finally { - try { - if (_d && !_d.done && (_a = _c.return)) _a.call(_c); - } - finally { if (e_5) throw e_5.error; } - } - try { - for (var _f = __values(Object.entries(exports.allCommands)), _g = _f.next(); !_g.done; _g = _f.next()) { - var _h = __read(_g.value, 2), key = _h[0], command_2 = _h[1]; - if (command_2.init) - command_2.data = command_2.init(); - } - } - catch (e_6_1) { e_6 = { error: e_6_1 }; } - finally { - try { - if (_g && !_g.done && (_b = _f.return)) _b.call(_f); - } - finally { if (e_6) throw e_6.error; } - } - initialized = true; -} -function reset() { - var e_7, _a, e_8, _b; - initialized = false; - try { - for (var _c = __values(Object.entries(exports.allConsoleCommands)), _d = _c.next(); !_d.done; _d = _c.next()) { - var _e = __read(_d.value, 2), key = _e[0], command_3 = _e[1]; - if (command_3.init) - command_3.data = undefined; - } - } - catch (e_7_1) { e_7 = { error: e_7_1 }; } - finally { - try { - if (_d && !_d.done && (_a = _c.return)) _a.call(_c); - } - finally { if (e_7) throw e_7.error; } - } - try { - for (var _f = __values(Object.entries(exports.allCommands)), _g = _f.next(); !_g.done; _g = _f.next()) { - var _h = __read(_g.value, 2), key = _h[0], command_4 = _h[1]; - if (command_4.init) - command_4.data = undefined; - } - } - catch (e_8_1) { e_8 = { error: e_8_1 }; } - finally { - try { - if (_g && !_g.done && (_b = _f.return)) _b.call(_f); - } - finally { if (e_8) throw e_8.error; } - } -} +"use strict"; +/* +Copyright © BalaM314, 2026. All Rights Reserved. +This file contains the commands framework. +For usage information, see docs/framework-usage-guide.md +For maintenance information, see docs/frameworks.md +*/ +//Behold, the power of typescript! +var __assign = (this && this.__assign) || function () { + __assign = Object.assign || function(t) { + for (var s, i = 1, n = arguments.length; i < n; i++) { + s = arguments[i]; + for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p)) + t[p] = s[p]; + } + return t; + }; + return __assign.apply(this, arguments); +}; +var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { + function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } + return new (P || (P = Promise))(function (resolve, reject) { + function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } + function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } + function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } + step((generator = generator.apply(thisArg, _arguments || [])).next()); + }); +}; +var __generator = (this && this.__generator) || function (thisArg, body) { + var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g = Object.create((typeof Iterator === "function" ? Iterator : Object).prototype); + return g.next = verb(0), g["throw"] = verb(1), g["return"] = verb(2), typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g; + function verb(n) { return function (v) { return step([n, v]); }; } + function step(op) { + if (f) throw new TypeError("Generator is already executing."); + while (g && (g = 0, op[0] && (_ = 0)), _) try { + if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t; + if (y = 0, t) op = [op[0] & 2, t.value]; + switch (op[0]) { + case 0: case 1: t = op; break; + case 4: _.label++; return { value: op[1], done: false }; + case 5: _.label++; y = op[1]; op = [0]; continue; + case 7: op = _.ops.pop(); _.trys.pop(); continue; + default: + if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; } + if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; } + if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; } + if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; } + if (t[2]) _.ops.pop(); + _.trys.pop(); continue; + } + op = body.call(thisArg, _); + } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; } + if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true }; + } +}; +var __read = (this && this.__read) || function (o, n) { + var m = typeof Symbol === "function" && o[Symbol.iterator]; + if (!m) return o; + var i = m.call(o), r, ar = [], e; + try { + while ((n === void 0 || n-- > 0) && !(r = i.next()).done) ar.push(r.value); + } + catch (error) { e = { error: error }; } + finally { + try { + if (r && !r.done && (m = i["return"])) m.call(i); + } + finally { if (e) throw e.error; } + } + return ar; +}; +var __values = (this && this.__values) || function(o) { + var s = typeof Symbol === "function" && Symbol.iterator, m = s && o[s], i = 0; + if (m) return m.call(o); + if (o && typeof o.length === "number") return { + next: function () { + if (o && i >= o.length) o = void 0; + return { value: o && o[i++], done: !o }; + } + }; + throw new TypeError(s ? "Object is not iterable." : "Symbol.iterator is not defined."); +}; +var __spreadArray = (this && this.__spreadArray) || function (to, from, pack) { + if (pack || arguments.length === 2) for (var i = 0, l = from.length, ar; i < l; i++) { + if (ar || !(i in from)) { + if (!ar) ar = Array.prototype.slice.call(from, 0, i); + ar[i] = from[i]; + } + } + return to.concat(ar || Array.prototype.slice.call(from)); +}; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.consoleCommandList = exports.commandList = exports.allConsoleCommands = exports.allCommands = void 0; +exports.command = command; +exports.processArgString = processArgString; +exports.formatArg = formatArg; +exports.joinArgs = joinArgs; +exports.disambiguateArgument = disambiguateArgument; +exports.processArgs = processArgs; +exports.convertArgs = convertArgs; +exports.handleTapEvent = handleTapEvent; +exports.register = register; +exports.registerConsole = registerConsole; +exports.initialize = initialize; +exports.reset = reset; +var config_1 = require("/config"); +var errors_1 = require("/frameworks/commands/errors"); +var formatting_1 = require("/frameworks/commands/formatting"); +var types_1 = require("/frameworks/commands/types"); +var menus_1 = require("/frameworks/menus"); +var funcs_1 = require("/funcs"); +var globals_1 = require("/globals"); +var players_1 = require("/players"); +var ranks_1 = require("/ranks"); +var utils_1 = require("/utils"); +var hiddenUnauthorizedMessage = "[scarlet]Unknown command. Check [lightgray]/help[scarlet]."; +/** Flag to prevent double initialization */ +var initialized = false; +/** Stores all chat comamnds by their name. */ +exports.allCommands = {}; +/** Stores all console commands by their name. */ +exports.allConsoleCommands = {}; +/** Stores the last usage data for chat commands by their name. */ +var globalUsageData = {}; +/** Helper function to get the correct type for command lists. */ +var commandList = function (list) { return list; }; +exports.commandList = commandList; +/** Helper function to get the correct type for command lists. */ +var consoleCommandList = function (list) { return list; }; +exports.consoleCommandList = consoleCommandList; +/** + * Helper function to get the correct type definitions for commands that use "data" or init(). + * Necessary because, while typescript is capable of inferring A1, A2... + * ``` + * { + * prop1: Type; + * prop2: Type; + * } + * ``` + * it cannot handle inferring A1 and B1. + * ``` + * { + * prop1: Type; + * prop2: Type; + * } + * ``` + */ +function command(input) { + return input; +} +/** Takes an arg string, like `reason:string?` and converts it to a CommandArg. */ +function processArgString(str) { + //this was copypasted from mlogx haha + var matchResult = str.match(/(\w+):(\w+)(\?)?/); + if (!matchResult) { + (0, funcs_1.crash)("Bad arg string ".concat(str, ": does not match pattern word:word(?)")); + } + var _a = __read(matchResult, 4), name = _a[1], type = _a[2], isOptional = _a[3]; + if (types_1.commandArgTypes.includes(type)) { + return { name: name, type: type, isOptional: !!isOptional }; + } + else { + (0, funcs_1.crash)("Bad arg string ".concat(str, ": invalid type ").concat(type)); + } +} +function formatArg(a) { + var isOptional = a.at(-1) == "?"; + var brackets = isOptional ? ["[", "]"] : ["<", ">"]; + return brackets[0] + a.split(":")[0] + brackets[1]; +} +/** Joins multi-word arguments that have been groups with quotes. Ex: turns [`"a`, `b"`] into [`a b`]*/ +function joinArgs(rawArgs) { + var e_1, _a; + var outputArgs = []; + var groupedArg = null; + try { + for (var rawArgs_1 = __values(rawArgs), rawArgs_1_1 = rawArgs_1.next(); !rawArgs_1_1.done; rawArgs_1_1 = rawArgs_1.next()) { + var arg = rawArgs_1_1.value; + if (arg.startsWith("\"") && groupedArg == null) { + groupedArg = []; + } + if (groupedArg) { + groupedArg.push(arg); + if (arg.endsWith("\"")) { + outputArgs.push(groupedArg.join(" ").slice(1, -1)); + groupedArg = null; + } + } + else { + outputArgs.push(arg); + } + } + } + catch (e_1_1) { e_1 = { error: e_1_1 }; } + finally { + try { + if (rawArgs_1_1 && !rawArgs_1_1.done && (_a = rawArgs_1.return)) _a.call(rawArgs_1); + } + finally { if (e_1) throw e_1.error; } + } + if (groupedArg != null) { + //return `Unterminated string literal.`; + outputArgs.push(groupedArg.join(" ")); + } + return outputArgs; +} +function disambiguateArgument(options_1, arg_1, _a, sender_1, outputArgs_1, optionStringifier_1) { + return __awaiter(this, arguments, void 0, function (options, arg, _b, sender, outputArgs, optionStringifier, columns) { + var word, a_an_word, _c, _d; + var name = _b.name, type = _b.type; + if (columns === void 0) { columns = 3; } + return __generator(this, function (_e) { + switch (_e.label) { + case 0: + if (!(options == null)) return [3 /*break*/, 1]; + (0, errors_1.fail)("".concat((0, funcs_1.capitalizeText)(types_1.commandArgNames[type]), " \"").concat(arg, "\" not found.")); + return [3 /*break*/, 4]; + case 1: + if (!(options instanceof Array)) return [3 /*break*/, 3]; + word = types_1.commandArgNames[type]; + if (!sender) + (0, errors_1.fail)("Name \"".concat(arg, "\" could refer to more than one ").concat(word, ".")); + a_an_word = (0, funcs_1.indefiniteArticle)(word); + _c = outputArgs; + _d = name; + return [4 /*yield*/, menus_1.Menu.menu("Select ".concat(a_an_word), "Select ".concat(a_an_word, " for the argument \"").concat(name, "\""), options, sender, { + includeCancel: true, + optionStringifier: optionStringifier, + columns: columns, + })]; + case 2: + _c[_d] = _e.sent(); + return [3 /*break*/, 4]; + case 3: + outputArgs[name] = options; + _e.label = 4; + case 4: return [2 /*return*/]; + } + }); + }); +} +var argsSupportingBlank = ["player", "offlinePlayer", "unittype", "map", "mapOrRandom", "rank", "roleflag", "item", "team"]; +/** Takes a list of joined args passed to the command, and processes it, turning it into a kwargs style object. */ +function processArgs(args, processedCmdArgs, sender) { + return __awaiter(this, void 0, void 0, function () { + var outputArgs, _a, _b, _c, i, cmdArg, commonArgs, _d, player, num, number, milliseconds, block, e_2_1; + var e_2, _e; + var _f; + return __generator(this, function (_g) { + switch (_g.label) { + case 0: + outputArgs = {}; + _g.label = 1; + case 1: + _g.trys.push([1, 35, 36, 37]); + _a = __values(processedCmdArgs.entries()), _b = _a.next(); + _g.label = 2; + case 2: + if (!!_b.done) return [3 /*break*/, 34]; + _c = __read(_b.value, 2), i = _c[0], cmdArg = _c[1]; + if (!(i in args) || args[i] === "") { + //if the arg was not provided or it was empty + if (cmdArg.isOptional) { + outputArgs[cmdArg.name] = undefined; + return [3 /*break*/, 33]; + } + else if (sender && argsSupportingBlank.includes(cmdArg.type)) { + //it will be resolved later + } + else { + (0, errors_1.fail)("No value specified for arg ".concat(cmdArg.name, ". Did you type two spaces instead of one?")); + } + } + commonArgs = [args[i], cmdArg, sender, outputArgs]; + _d = cmdArg.type; + switch (_d) { + case "player": return [3 /*break*/, 3]; + case "offlinePlayer": return [3 /*break*/, 5]; + case "team": return [3 /*break*/, 10]; + case "number": return [3 /*break*/, 14]; + case "time": return [3 /*break*/, 15]; + case "string": return [3 /*break*/, 16]; + case "boolean": return [3 /*break*/, 17]; + case "block": return [3 /*break*/, 18]; + case "unittype": return [3 /*break*/, 19]; + case "uuid": return [3 /*break*/, 21]; + case "map": return [3 /*break*/, 22]; + case "mapOrRandom": return [3 /*break*/, 24]; + case "rank": return [3 /*break*/, 26]; + case "roleflag": return [3 /*break*/, 28]; + case "item": return [3 /*break*/, 30]; + } + return [3 /*break*/, 32]; + case 3: return [4 /*yield*/, disambiguateArgument.apply(void 0, __spreadArray(__spreadArray([players_1.FishPlayer.search(players_1.FishPlayer.getAllOnline(), args[i])], __read(commonArgs), false), [function (player) { return (player.marked() ? config_1.prefixes.marked : player.autoflagged ? config_1.prefixes.flagged : "") + (Strings.stripColors(player.name).length >= 3 ? + player.name + : (0, funcs_1.escapeStringColorsClient)(player.name)); }, + 2], false))]; + case 4: + _g.sent(); + return [3 /*break*/, 33]; + case 5: + if (!globals_1.uuidPattern.test(args[i])) return [3 /*break*/, 6]; + player = players_1.FishPlayer.getById(args[i]); + if (player == null) + (0, errors_1.fail)("Player with uuid \"".concat(args[i], "\" not found. Specify \"create:").concat(args[i], "\" to create the player.")); + outputArgs[cmdArg.name] = player; + return [3 /*break*/, 9]; + case 6: + if (!(args[i].startsWith("create:") && globals_1.uuidPattern.test(args[i].split("create:")[1]))) return [3 /*break*/, 7]; + outputArgs[cmdArg.name] = players_1.FishPlayer.getFromInfo(Vars.netServer.admins.getInfo(args[i].split("create:")[1])); + return [3 /*break*/, 9]; + case 7: return [4 /*yield*/, disambiguateArgument.apply(void 0, __spreadArray(__spreadArray([players_1.FishPlayer.search(Object.values(players_1.FishPlayer.cachedPlayers), args[i])], __read(commonArgs), false), [function (player) { return Strings.stripColors(player.name).length >= 3 ? + player.name + : (0, funcs_1.escapeStringColorsClient)(player.name); }, + 2], false))]; + case 8: + _g.sent(); + _g.label = 9; + case 9: return [3 /*break*/, 33]; + case 10: + num = void 0; + if (!(args[i] && (!isNaN(num = Number(args[i])) || + args[i].slice(1) && !isNaN(num = Number(args[i].slice(1))) || //discard leading # + args[i].slice(5) && !isNaN(num = Number(args[i].slice(5))) //discard leading team# + ))) return [3 /*break*/, 11]; + if (num <= 255 && num >= 0 && Number.isInteger(num)) + outputArgs[cmdArg.name] = Team.all[num]; + else + (0, errors_1.fail)("Team ".concat(num, " is not inside the valid range (integers 0-255).")); + return [3 /*break*/, 13]; + case 11: return [4 /*yield*/, disambiguateArgument.apply(void 0, __spreadArray(__spreadArray([(0, utils_1.getTeam)(args[i])], __read(commonArgs), false), [function (t) { return t.coloredName(); }], false))]; + case 12: + _g.sent(); + _g.label = 13; + case 13: return [3 /*break*/, 33]; + case 14: + { + number = Number(args[i]); + if (isNaN(number)) { + if (/\(\d+,/.test(args[i])) + number = Number(args[i].slice(1, -1)); + else if (/\d+\)/.test(args[i])) + number = Number(args[i].slice(0, -1)); + if (isNaN(number)) + (0, errors_1.fail)("Invalid number \"".concat(args[i], "\"")); + } + outputArgs[cmdArg.name] = number; + return [3 /*break*/, 33]; + } + _g.label = 15; + case 15: + { + milliseconds = (0, utils_1.parseTimeString)(args[i]); + if (milliseconds == null) + (0, errors_1.fail)("Invalid time string \"".concat(args[i], "\"")); + outputArgs[cmdArg.name] = milliseconds; + return [3 /*break*/, 33]; + } + _g.label = 16; + case 16: + outputArgs[cmdArg.name] = args[i]; + return [3 /*break*/, 33]; + case 17: + switch (args[i].toLowerCase()) { + case "true": + case "yes": + case "yeah": + case "ya": + case "ye": + case "t": + case "y": + case "1": + outputArgs[cmdArg.name] = true; + break; + case "false": + case "no": + case "nah": + case "nay": + case "nope": + case "f": + case "n": + case "0": + outputArgs[cmdArg.name] = false; + break; + default: (0, errors_1.fail)("Argument ".concat(args[i], " is not a boolean. Try \"true\" or \"false\".")); + } + return [3 /*break*/, 33]; + case 18: + { + block = (0, utils_1.getBlock)(args[i], "air"); + if (typeof block == "string") + (0, errors_1.fail)(block); + outputArgs[cmdArg.name] = block; + return [3 /*break*/, 33]; + } + _g.label = 19; + case 19: return [4 /*yield*/, disambiguateArgument.apply(void 0, __spreadArray(__spreadArray([(0, utils_1.getUnitType)(args[i])], __read(commonArgs), false), [function (u) { return u.emoji() + (0, funcs_1.capitalizeText)(u.name); }], false))]; + case 20: + _g.sent(); + return [3 /*break*/, 33]; + case 21: + if (!globals_1.uuidPattern.test(args[i])) + (0, errors_1.fail)("Invalid uuid string \"".concat(args[i], "\"")); + outputArgs[cmdArg.name] = args[i]; + return [3 /*break*/, 33]; + case 22: return [4 /*yield*/, disambiguateArgument.apply(void 0, __spreadArray(__spreadArray([(0, utils_1.getMap)(args[i])], __read(commonArgs), false), [function (r) { return r.name(); }, + 2], false))]; + case 23: + _g.sent(); + return [3 /*break*/, 33]; + case 24: + if (["rand", "random"].includes((_f = args[i]) === null || _f === void 0 ? void 0 : _f.toLowerCase())) { + outputArgs[cmdArg.name] = "random"; + return [3 /*break*/, 33]; + } + return [4 /*yield*/, disambiguateArgument.apply(void 0, __spreadArray(__spreadArray([(0, utils_1.getMap)(args[i])], __read(commonArgs), false), [function (r) { return r.name(); }, + 2], false))]; + case 25: + _g.sent(); + return [3 /*break*/, 33]; + case 26: return [4 /*yield*/, disambiguateArgument.apply(void 0, __spreadArray(__spreadArray([ranks_1.Rank.search(args[i])], __read(commonArgs), false), [function (r) { return r.coloredName(); }], false))]; + case 27: + _g.sent(); + return [3 /*break*/, 33]; + case 28: return [4 /*yield*/, disambiguateArgument.apply(void 0, __spreadArray(__spreadArray([ranks_1.RoleFlag.search(args[i])], __read(commonArgs), false), [function (f) { return f.coloredName(); }], false))]; + case 29: + _g.sent(); + return [3 /*break*/, 33]; + case 30: return [4 /*yield*/, disambiguateArgument.apply(void 0, __spreadArray(__spreadArray([(0, utils_1.getItem)(args[i])], __read(commonArgs), false), [function (i) { return i.emoji() + (0, funcs_1.capitalizeText)(i.name, "-"); }, + 2], false))]; + case 31: + _g.sent(); + return [3 /*break*/, 33]; + case 32: + cmdArg.type; + (0, funcs_1.crash)("impossible"); + _g.label = 33; + case 33: + _b = _a.next(); + return [3 /*break*/, 2]; + case 34: return [3 /*break*/, 37]; + case 35: + e_2_1 = _g.sent(); + e_2 = { error: e_2_1 }; + return [3 /*break*/, 37]; + case 36: + try { + if (_b && !_b.done && (_e = _a.return)) _e.call(_a); + } + finally { if (e_2) throw e_2.error; } + return [7 /*endfinally*/]; + case 37: return [2 /*return*/, outputArgs]; + } + }); + }); +} +var variadicArgumentTypes = ["player", "string", "map", "mapOrRandom"]; +function isArgOptional(arg, allowMenus) { + return arg.isOptional || (argsSupportingBlank.includes(arg.type) && allowMenus); +} +/** Converts the CommandArg[] to the format accepted by Arc CommandHandler */ +function convertArgs(processedCmdArgs, allowMenus) { + return processedCmdArgs.map(function (arg, index, array) { + var isOptional = isArgOptional(arg, allowMenus) && + !array.slice(index + 1).some(function (c) { return !isArgOptional(c, allowMenus); }); //this is enforced by the arc command handler + //TODO internalize command handler + var brackets = isOptional ? ["[", "]"] : ["<", ">"]; + //if the arg is a string and last argument, make it variadic (so if `/warn player a b c d` is run, the last arg is "a b c d" not "a") + return brackets[0] + arg.name + (variadicArgumentTypes.includes(arg.type) && index + 1 == array.length ? "..." : "") + brackets[1]; + }).join(" "); +} +function handleTapEvent(event) { + var _a; + var sender = players_1.FishPlayer.get(event.player); + if (sender.tapInfo.commandName == null) + return; + var command = exports.allCommands[sender.tapInfo.commandName]; + var usageData = sender.getUsageData(sender.tapInfo.commandName); + var handleTapsUpdated = false; + try { + var failed_1 = false; + (_a = command.tapped) === null || _a === void 0 ? void 0 : _a.call(command, { + args: sender.tapInfo.lastArgs, + data: command.data, + outputFail: function (message) { (0, utils_1.outputFail)(message, sender); failed_1 = true; }, + outputSuccess: function (message) { return (0, utils_1.outputSuccess)(message, sender); }, + output: function (message) { return (0, utils_1.outputMessage)(message, sender); }, + f: formatting_1.outputFormatter_client, + admins: Vars.netServer.admins, + commandLastUsed: usageData.lastUsed, + commandLastUsedSuccessfully: usageData.lastUsedSuccessfully, + lastUsed: usageData.tapLastUsed, + lastUsedSuccessfully: usageData.tapLastUsedSuccessfully, + sender: sender, + tile: event.tile, + x: event.tile.x, + y: event.tile.y, + currentTapMode: sender.tapInfo.commandName == null ? "off" : sender.tapInfo.mode, + handleTaps: function (mode) { + if (mode == "off") { + sender.tapInfo.commandName = null; + return; + } + sender.tapInfo.mode = mode; + handleTapsUpdated = true; + }, + }); + if (!failed_1) + usageData.tapLastUsedSuccessfully = Date.now(); + } + catch (err) { + (0, utils_1.handleError)(err, sender, utils_1.outputFail, "".concat(sender.cleanedName, " ran /").concat(sender.tapInfo.commandName, " and tapped")); + } + finally { + if (sender.tapInfo.mode == "once" && !handleTapsUpdated) { + sender.tapInfo.commandName = null; + } + usageData.tapLastUsed = Date.now(); + } +} +/** + * Registers all commands in a list to a client command handler. + **/ +function register(commands, clientHandler, serverHandler) { + var e_3, _a; + var _loop_1 = function (name, _data) { + //Invoke thunk if necessary + var data = typeof _data == "function" ? _data() : _data; + //Process the args + var processedCmdArgs = data.args.map(processArgString); + clientHandler.removeCommand(name); //The function silently fails if the argument doesn't exist so this is safe + clientHandler.register(name, convertArgs(processedCmdArgs, true), data.description, new CommandHandler.CommandRunner({ accept: function (unjoinedRawArgs, sender) { + return __awaiter(this, void 0, void 0, function () { + var fishSender, rawArgs, resolvedArgs, err_1, usageData, failed, args_1, requirements, err_2; + var _a; + return __generator(this, function (_b) { + switch (_b.label) { + case 0: + if (!initialized) + (0, funcs_1.crash)("Commands not initialized!"); + fishSender = players_1.FishPlayer.get(sender); + players_1.FishPlayer.onPlayerCommand(fishSender, name, unjoinedRawArgs); + //Verify authorization + //as a bonus, this crashes if data.perm is undefined + if (!data.perm.check(fishSender)) { + if (data.customUnauthorizedMessage) { + (0, utils_1.outputFail)(data.customUnauthorizedMessage, sender); + globals_1.FishEvents.fire("commandUnauthorized", [fishSender, name]); + } + else if (data.isHidden) + (0, utils_1.outputMessage)(hiddenUnauthorizedMessage, sender); + else + (0, utils_1.outputFail)(data.perm.unauthorizedMessage, sender); + return [2 /*return*/]; + } + rawArgs = joinArgs(unjoinedRawArgs); + _b.label = 1; + case 1: + _b.trys.push([1, 3, , 4]); + return [4 /*yield*/, processArgs(rawArgs, processedCmdArgs, fishSender)]; + case 2: + resolvedArgs = _b.sent(); + return [3 /*break*/, 4]; + case 3: + err_1 = _b.sent(); + (0, utils_1.handleError)(err_1, fishSender, utils_1.outputFail, "".concat(fishSender.cleanedName, " ran /").concat(name)); + return [2 /*return*/]; + case 4: + usageData = fishSender.getUsageData(name); + failed = false; + _b.label = 5; + case 5: + _b.trys.push([5, 7, 8, 9]); + args_1 = { + rawArgs: rawArgs, + args: resolvedArgs, + sender: fishSender, + data: data.data, + outputFail: function (message) { (0, utils_1.outputFail)(message, sender); failed = true; }, + outputSuccess: function (message) { return (0, utils_1.outputSuccess)(message, sender); }, + output: function (message) { return (0, utils_1.outputMessage)(message, sender); }, + f: formatting_1.f_client, + execServer: function (command) { return serverHandler.handleMessage(command); }, + admins: Vars.netServer.admins, + lastUsedSender: usageData.lastUsed, + lastUsedSuccessfullySender: usageData.lastUsedSuccessfully, + lastUsedSuccessfully: ((_a = globalUsageData[name]) !== null && _a !== void 0 ? _a : (globalUsageData[name] = { lastUsed: -1, lastUsedSuccessfully: -1 })).lastUsedSuccessfully, + allCommands: exports.allCommands, + currentTapMode: fishSender.tapInfo.commandName == null ? "off" : fishSender.tapInfo.mode, + handleTaps: function (mode) { + if (data.tapped == undefined) + (0, funcs_1.crash)("No tap handler to activate: command \"".concat(name, "\"")); + if (mode == "off") { + fishSender.tapInfo.commandName = null; + } + else { + fishSender.tapInfo.commandName = name; + fishSender.tapInfo.mode = mode; + } + fishSender.tapInfo.lastArgs = resolvedArgs; + }, + }; + requirements = typeof data.requirements == "function" ? data.requirements(args_1) : data.requirements; + requirements === null || requirements === void 0 ? void 0 : requirements.forEach(function (r) { return r(args_1); }); + return [4 /*yield*/, data.handler(args_1)]; + case 6: + _b.sent(); + //Update usage data + if (!failed) { + usageData.lastUsedSuccessfully = globalUsageData[name].lastUsedSuccessfully = Date.now(); + } + return [3 /*break*/, 9]; + case 7: + err_2 = _b.sent(); + (0, utils_1.handleError)(err_2, fishSender, utils_1.outputFail, "".concat(fishSender.cleanedName, " ran /").concat(name)); + return [3 /*break*/, 9]; + case 8: + usageData.lastUsed = globalUsageData[name].lastUsed = Date.now(); + return [7 /*endfinally*/]; + case 9: return [2 /*return*/]; + } + }); + }); + } })); + exports.allCommands[name] = data; + }; + try { + for (var _b = __values(Object.entries(commands)), _c = _b.next(); !_c.done; _c = _b.next()) { + var _d = __read(_c.value, 2), name = _d[0], _data = _d[1]; + _loop_1(name, _data); + } + } + catch (e_3_1) { e_3 = { error: e_3_1 }; } + finally { + try { + if (_c && !_c.done && (_a = _b.return)) _a.call(_b); + } + finally { if (e_3) throw e_3.error; } + } +} +function registerConsole(commands, serverHandler) { + var e_4, _a; + var _loop_2 = function (name, data) { + //Process the args + var processedCmdArgs = data.args.map(processArgString); + serverHandler.removeCommand(name); //The function silently fails if the argument doesn't exist so this is safe + serverHandler.register(name, convertArgs(processedCmdArgs, false), data.description, new CommandHandler.CommandRunner({ accept: function (rawArgs) { + return __awaiter(this, void 0, void 0, function () { + var resolvedArgs, err_3, usageData, failed_2; + var _a; + var _b; + return __generator(this, function (_c) { + switch (_c.label) { + case 0: + if (!initialized) + (0, funcs_1.crash)("Commands not initialized!"); + _c.label = 1; + case 1: + _c.trys.push([1, 3, , 4]); + return [4 /*yield*/, processArgs(rawArgs, processedCmdArgs, null)]; + case 2: + resolvedArgs = _c.sent(); + return [3 /*break*/, 4]; + case 3: + err_3 = _c.sent(); + //if args are invalid + Log.err(err_3); + return [2 /*return*/]; + case 4: + usageData = ((_a = globalUsageData[_b = "_console_" + name]) !== null && _a !== void 0 ? _a : (globalUsageData[_b] = { lastUsed: -1, lastUsedSuccessfully: -1 })); + try { + failed_2 = false; + data.handler(__assign({ rawArgs: rawArgs, args: resolvedArgs, data: data.data, outputFail: function (message) { (0, utils_1.outputConsole)(message, Log.err); failed_2 = true; }, outputSuccess: utils_1.outputConsole, output: utils_1.outputConsole, f: formatting_1.f_server, execServer: function (command) { return serverHandler.handleMessage(command); }, admins: Vars.netServer.admins }, usageData)); + usageData.lastUsed = Date.now(); + if (!failed_2) + usageData.lastUsedSuccessfully = Date.now(); + } + catch (err) { + usageData.lastUsed = Date.now(); + if (err instanceof errors_1.CommandError) { + Log.warn(typeof err.data == "function" ? err.data("&fr") : err.data); + } + else { + Log.err("&lrAn error occured while executing the command!&fr"); + Log.err((0, funcs_1.parseError)(err)); + } + } + return [2 /*return*/]; + } + }); + }); + } })); + exports.allConsoleCommands[name] = data; + }; + try { + for (var _b = __values(Object.entries(commands)), _c = _b.next(); !_c.done; _c = _b.next()) { + var _d = __read(_c.value, 2), name = _d[0], data = _d[1]; + _loop_2(name, data); + } + } + catch (e_4_1) { e_4 = { error: e_4_1 }; } + finally { + try { + if (_c && !_c.done && (_a = _b.return)) _a.call(_b); + } + finally { if (e_4) throw e_4.error; } + } +} +function initialize() { + var e_5, _a, e_6, _b; + if (initialized) { + (0, funcs_1.crash)("Already initialized commands."); + } + try { + for (var _c = __values(Object.entries(exports.allConsoleCommands)), _d = _c.next(); !_d.done; _d = _c.next()) { + var _e = __read(_d.value, 2), key = _e[0], command_1 = _e[1]; + if (command_1.init) + command_1.data = command_1.init(); + } + } + catch (e_5_1) { e_5 = { error: e_5_1 }; } + finally { + try { + if (_d && !_d.done && (_a = _c.return)) _a.call(_c); + } + finally { if (e_5) throw e_5.error; } + } + try { + for (var _f = __values(Object.entries(exports.allCommands)), _g = _f.next(); !_g.done; _g = _f.next()) { + var _h = __read(_g.value, 2), key = _h[0], command_2 = _h[1]; + if (command_2.init) + command_2.data = command_2.init(); + } + } + catch (e_6_1) { e_6 = { error: e_6_1 }; } + finally { + try { + if (_g && !_g.done && (_b = _f.return)) _b.call(_f); + } + finally { if (e_6) throw e_6.error; } + } + initialized = true; +} +function reset() { + var e_7, _a, e_8, _b; + initialized = false; + try { + for (var _c = __values(Object.entries(exports.allConsoleCommands)), _d = _c.next(); !_d.done; _d = _c.next()) { + var _e = __read(_d.value, 2), key = _e[0], command_3 = _e[1]; + if (command_3.init) + command_3.data = undefined; + } + } + catch (e_7_1) { e_7 = { error: e_7_1 }; } + finally { + try { + if (_d && !_d.done && (_a = _c.return)) _a.call(_c); + } + finally { if (e_7) throw e_7.error; } + } + try { + for (var _f = __values(Object.entries(exports.allCommands)), _g = _f.next(); !_g.done; _g = _f.next()) { + var _h = __read(_g.value, 2), key = _h[0], command_4 = _h[1]; + if (command_4.init) + command_4.data = undefined; + } + } + catch (e_8_1) { e_8 = { error: e_8_1 }; } + finally { + try { + if (_g && !_g.done && (_b = _f.return)) _b.call(_f); + } + finally { if (e_8) throw e_8.error; } + } +} diff --git a/build/scripts/frameworks/commands/errors.js b/build/scripts/frameworks/commands/errors.js index ba915110..a7fa23f2 100644 --- a/build/scripts/frameworks/commands/errors.js +++ b/build/scripts/frameworks/commands/errors.js @@ -1,20 +1,20 @@ -"use strict"; -/* -Copyright © BalaM314, 2026. All Rights Reserved. -This file contains the error handling framework. -For usage information, see docs/framework-usage-guide.md -For maintenance information, see docs/frameworks.md -*/ -//Behold, the power of typescript! -Object.defineProperty(exports, "__esModule", { value: true }); -exports.CommandError = void 0; -exports.fail = fail; -exports.CommandError = (function () { }); -Object.setPrototypeOf(exports.CommandError.prototype, Error.prototype); -function fail(message) { - var err = new Error(typeof message == "string" ? message : ""); - //oh no it's even worse now because i have to smuggle a function through here - err.data = message; - Object.setPrototypeOf(err, exports.CommandError.prototype); - throw err; -} +"use strict"; +/* +Copyright © BalaM314, 2026. All Rights Reserved. +This file contains the error handling framework. +For usage information, see docs/framework-usage-guide.md +For maintenance information, see docs/frameworks.md +*/ +//Behold, the power of typescript! +Object.defineProperty(exports, "__esModule", { value: true }); +exports.CommandError = void 0; +exports.fail = fail; +exports.CommandError = (function () { }); +Object.setPrototypeOf(exports.CommandError.prototype, Error.prototype); +function fail(message) { + var err = new Error(typeof message == "string" ? message : ""); + //oh no it's even worse now because i have to smuggle a function through here + err.data = message; + Object.setPrototypeOf(err, exports.CommandError.prototype); + throw err; +} diff --git a/build/scripts/frameworks/commands/formatting.js b/build/scripts/frameworks/commands/formatting.js index 08e169af..ecd73206 100644 --- a/build/scripts/frameworks/commands/formatting.js +++ b/build/scripts/frameworks/commands/formatting.js @@ -1,183 +1,183 @@ -"use strict"; -var __read = (this && this.__read) || function (o, n) { - var m = typeof Symbol === "function" && o[Symbol.iterator]; - if (!m) return o; - var i = m.call(o), r, ar = [], e; - try { - while ((n === void 0 || n-- > 0) && !(r = i.next()).done) ar.push(r.value); - } - catch (error) { e = { error: error }; } - finally { - try { - if (r && !r.done && (m = i["return"])) m.call(i); - } - finally { if (e) throw e.error; } - } - return ar; -}; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.f_server = exports.f_client = exports.processedFFunctions = exports.fFunctions = exports.outputFormatter_client = exports.outputFormatter_server = void 0; -var funcs_1 = require("/funcs"); -var globals_1 = require("/globals"); -var players_1 = require("/players"); -var ranks_1 = require("/ranks"); -exports.outputFormatter_server = (0, funcs_1.tagProcessorPartial)(function (chunk) { - if (chunk instanceof players_1.FishPlayer) { - return "&c(".concat((0, funcs_1.escapeStringColorsServer)(chunk.cleanedName), ")&fr"); - } - else if (chunk instanceof ranks_1.Rank) { - return "&p".concat(chunk.name, "&fr"); - } - else if (chunk instanceof ranks_1.RoleFlag) { - return "&p".concat(chunk.name, "&fr"); - } - else if (chunk instanceof Error) { - return "&r".concat((0, funcs_1.escapeStringColorsServer)(chunk.toString()), "&fr"); - } - else if (chunk instanceof Player) { - var player = chunk; //not sure why this is necessary, typescript randomly converts any to unknown - return "&cPlayer#".concat(player.id, " (").concat((0, funcs_1.escapeStringColorsServer)(Strings.stripColors(player.name)), ")&fr"); - } - else if (typeof chunk == "string") { - if (globals_1.uuidPattern.test(chunk)) { - return "&b".concat(chunk, "&fr"); - } - else if (globals_1.ipPattern.test(chunk)) { - return "&b".concat(chunk, "&fr"); - } - else { - return "".concat(chunk); - } - } - else if (typeof chunk == "boolean") { - return "&b".concat(chunk.toString(), "&fr"); - } - else if (typeof chunk == "number") { - return "&b".concat(chunk.toString(), "&fr"); - } - else if (chunk instanceof Administration.PlayerInfo) { - return "&c".concat((0, funcs_1.escapeStringColorsServer)(chunk.plainLastName()), "&fr"); - } - else if (chunk instanceof UnitType) { - return "&c".concat(chunk.localizedName, "&fr"); - } - else if (chunk instanceof Block) { - return "&c".concat(chunk.localizedName, "&fr"); - } - else if (chunk instanceof Team) { - return "&c".concat(chunk.name, "&fr"); - } - else if (chunk instanceof Item) { - return "&c".concat((0, funcs_1.capitalizeText)(chunk.name, "-"), "&fr"); - } - else { - chunk; - Log.err("Invalid format object!"); - Log.info(chunk); - return chunk; //let it get stringified by the JS engine - } -}); -exports.outputFormatter_client = (0, funcs_1.tagProcessorPartial)(function (chunk, i, data, stringChunks) { - var _a, _b; - var reset = (_b = data !== null && data !== void 0 ? data : (_a = stringChunks[0].match(/^\[.+?\]/)) === null || _a === void 0 ? void 0 : _a[0]) !== null && _b !== void 0 ? _b : ""; - if (chunk instanceof players_1.FishPlayer) { - return "[cyan](".concat(chunk.name, "[cyan])") + reset; - } - else if (chunk instanceof ranks_1.Rank) { - return "".concat(chunk.color).concat(chunk.name, "[]") + reset; - } - else if (chunk instanceof ranks_1.RoleFlag) { - return "".concat(chunk.color).concat(chunk.name, "[]") + reset; - } - else if (chunk instanceof Error) { - return "[red]".concat(chunk.toString()) + reset; - } - else if (chunk instanceof Player) { - var fishP = players_1.FishPlayer.get(chunk); - return "[cyan](".concat(fishP.name, "[cyan])") + reset; - } - else if (typeof chunk == "string") { - if (globals_1.uuidPattern.test(chunk)) { - return "[blue]".concat(chunk, "[]"); - } - else if (globals_1.ipPattern.test(chunk)) { - return "[blue]".concat(chunk, "[]"); - } - else { - //TODO reset color? - return chunk; - } - } - else if (typeof chunk == "boolean") { - return "[blue]".concat(chunk.toString(), "[]"); - } - else if (typeof chunk == "number") { - return "[blue]".concat(chunk.toString(), "[]"); - } - else if (chunk instanceof Administration.PlayerInfo) { - return chunk.lastName + reset; - } - else if (chunk instanceof UnitType) { - return "[cyan]".concat(chunk.localizedName, "[]"); - } - else if (chunk instanceof Block) { - return "[cyan]".concat(chunk.localizedName, "[]"); - } - else if (chunk instanceof Team) { - return "[white]".concat(chunk.coloredName(), "[][]"); - } - else if (chunk instanceof Item) { - return "[cyan]".concat((0, funcs_1.capitalizeText)(chunk.name, "-"), "[]"); - } - else { - chunk; - Log.err("Invalid format object!"); - Log.info(chunk); - return chunk; //allow it to get stringified by the engine - } -}); -exports.fFunctions = { - boolGood: function (value) { - return [ - value ? "[green]true[]" : "[red]false[]", - value ? "&lgtrue&fr" : "&lrfalse&fr", - ]; - }, - boolBad: function (value) { - return [ - value ? "[red]true[]" : "[green]false[]", - value ? "&lrtrue&fr" : "&lgfalse&fr", - ]; - }, - percent: function (value, decimals) { - if (decimals === void 0) { decimals = 0; } - if (isNaN(value) || !isFinite(value)) - return ["[gray]N/A[]", "N/A"]; - var percent = (value * 100).toFixed(decimals) + "%"; - return ["".concat(percent), "".concat(percent)]; - }, - number: function (value, decimals) { - if (decimals === void 0) { decimals = null; } - if (isNaN(value) || !isFinite(value)) - return ["[gray]N/A[]", "N/A"]; - if (decimals !== null) - return [value.toFixed(decimals), value.toFixed(decimals)]; - return [value.toString(), value.toString()]; - } -}; -exports.processedFFunctions = [0, 1].map(function (i) { - return Object.fromEntries(Object.entries(exports.fFunctions).map(function (_a) { - var _b = __read(_a, 2), k = _b[0], v = _b[1]; - return [k, - function () { - var args = []; - for (var _i = 0; _i < arguments.length; _i++) { - args[_i] = arguments[_i]; - } - return v.apply(exports.processedFFunctions[i], args)[i]; - } - ]; - })); -}); -exports.f_client = Object.assign(exports.outputFormatter_client, exports.processedFFunctions[0]); -exports.f_server = Object.assign(exports.outputFormatter_server, exports.processedFFunctions[1]); +"use strict"; +var __read = (this && this.__read) || function (o, n) { + var m = typeof Symbol === "function" && o[Symbol.iterator]; + if (!m) return o; + var i = m.call(o), r, ar = [], e; + try { + while ((n === void 0 || n-- > 0) && !(r = i.next()).done) ar.push(r.value); + } + catch (error) { e = { error: error }; } + finally { + try { + if (r && !r.done && (m = i["return"])) m.call(i); + } + finally { if (e) throw e.error; } + } + return ar; +}; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.f_server = exports.f_client = exports.processedFFunctions = exports.fFunctions = exports.outputFormatter_client = exports.outputFormatter_server = void 0; +var funcs_1 = require("/funcs"); +var globals_1 = require("/globals"); +var players_1 = require("/players"); +var ranks_1 = require("/ranks"); +exports.outputFormatter_server = (0, funcs_1.tagProcessorPartial)(function (chunk) { + if (chunk instanceof players_1.FishPlayer) { + return "&c(".concat((0, funcs_1.escapeStringColorsServer)(chunk.cleanedName), ")&fr"); + } + else if (chunk instanceof ranks_1.Rank) { + return "&p".concat(chunk.name, "&fr"); + } + else if (chunk instanceof ranks_1.RoleFlag) { + return "&p".concat(chunk.name, "&fr"); + } + else if (chunk instanceof Error) { + return "&r".concat((0, funcs_1.escapeStringColorsServer)(chunk.toString()), "&fr"); + } + else if (chunk instanceof Player) { + var player = chunk; //not sure why this is necessary, typescript randomly converts any to unknown + return "&cPlayer#".concat(player.id, " (").concat((0, funcs_1.escapeStringColorsServer)(Strings.stripColors(player.name)), ")&fr"); + } + else if (typeof chunk == "string") { + if (globals_1.uuidPattern.test(chunk)) { + return "&b".concat(chunk, "&fr"); + } + else if (globals_1.ipPattern.test(chunk)) { + return "&b".concat(chunk, "&fr"); + } + else { + return "".concat(chunk); + } + } + else if (typeof chunk == "boolean") { + return "&b".concat(chunk.toString(), "&fr"); + } + else if (typeof chunk == "number") { + return "&b".concat(chunk.toString(), "&fr"); + } + else if (chunk instanceof Administration.PlayerInfo) { + return "&c".concat((0, funcs_1.escapeStringColorsServer)(chunk.plainLastName()), "&fr"); + } + else if (chunk instanceof UnitType) { + return "&c".concat(chunk.localizedName, "&fr"); + } + else if (chunk instanceof Block) { + return "&c".concat(chunk.localizedName, "&fr"); + } + else if (chunk instanceof Team) { + return "&c".concat(chunk.name, "&fr"); + } + else if (chunk instanceof Item) { + return "&c".concat((0, funcs_1.capitalizeText)(chunk.name, "-"), "&fr"); + } + else { + chunk; + Log.err("Invalid format object!"); + Log.info(chunk); + return chunk; //let it get stringified by the JS engine + } +}); +exports.outputFormatter_client = (0, funcs_1.tagProcessorPartial)(function (chunk, i, data, stringChunks) { + var _a, _b; + var reset = (_b = data !== null && data !== void 0 ? data : (_a = stringChunks[0].match(/^\[.+?\]/)) === null || _a === void 0 ? void 0 : _a[0]) !== null && _b !== void 0 ? _b : ""; + if (chunk instanceof players_1.FishPlayer) { + return "[cyan](".concat(chunk.name, "[cyan])") + reset; + } + else if (chunk instanceof ranks_1.Rank) { + return "".concat(chunk.color).concat(chunk.name, "[]") + reset; + } + else if (chunk instanceof ranks_1.RoleFlag) { + return "".concat(chunk.color).concat(chunk.name, "[]") + reset; + } + else if (chunk instanceof Error) { + return "[red]".concat(chunk.toString()) + reset; + } + else if (chunk instanceof Player) { + var fishP = players_1.FishPlayer.get(chunk); + return "[cyan](".concat(fishP.name, "[cyan])") + reset; + } + else if (typeof chunk == "string") { + if (globals_1.uuidPattern.test(chunk)) { + return "[blue]".concat(chunk, "[]"); + } + else if (globals_1.ipPattern.test(chunk)) { + return "[blue]".concat(chunk, "[]"); + } + else { + //TODO reset color? + return chunk; + } + } + else if (typeof chunk == "boolean") { + return "[blue]".concat(chunk.toString(), "[]"); + } + else if (typeof chunk == "number") { + return "[blue]".concat(chunk.toString(), "[]"); + } + else if (chunk instanceof Administration.PlayerInfo) { + return chunk.lastName + reset; + } + else if (chunk instanceof UnitType) { + return "[cyan]".concat(chunk.localizedName, "[]"); + } + else if (chunk instanceof Block) { + return "[cyan]".concat(chunk.localizedName, "[]"); + } + else if (chunk instanceof Team) { + return "[white]".concat(chunk.coloredName(), "[][]"); + } + else if (chunk instanceof Item) { + return "[cyan]".concat((0, funcs_1.capitalizeText)(chunk.name, "-"), "[]"); + } + else { + chunk; + Log.err("Invalid format object!"); + Log.info(chunk); + return chunk; //allow it to get stringified by the engine + } +}); +exports.fFunctions = { + boolGood: function (value) { + return [ + value ? "[green]true[]" : "[red]false[]", + value ? "&lgtrue&fr" : "&lrfalse&fr", + ]; + }, + boolBad: function (value) { + return [ + value ? "[red]true[]" : "[green]false[]", + value ? "&lrtrue&fr" : "&lgfalse&fr", + ]; + }, + percent: function (value, decimals) { + if (decimals === void 0) { decimals = 0; } + if (isNaN(value) || !isFinite(value)) + return ["[gray]N/A[]", "N/A"]; + var percent = (value * 100).toFixed(decimals) + "%"; + return ["".concat(percent), "".concat(percent)]; + }, + number: function (value, decimals) { + if (decimals === void 0) { decimals = null; } + if (isNaN(value) || !isFinite(value)) + return ["[gray]N/A[]", "N/A"]; + if (decimals !== null) + return [value.toFixed(decimals), value.toFixed(decimals)]; + return [value.toString(), value.toString()]; + } +}; +exports.processedFFunctions = [0, 1].map(function (i) { + return Object.fromEntries(Object.entries(exports.fFunctions).map(function (_a) { + var _b = __read(_a, 2), k = _b[0], v = _b[1]; + return [k, + function () { + var args = []; + for (var _i = 0; _i < arguments.length; _i++) { + args[_i] = arguments[_i]; + } + return v.apply(exports.processedFFunctions[i], args)[i]; + } + ]; + })); +}); +exports.f_client = Object.assign(exports.outputFormatter_client, exports.processedFFunctions[0]); +exports.f_server = Object.assign(exports.outputFormatter_server, exports.processedFFunctions[1]); diff --git a/build/scripts/frameworks/commands/perm.js b/build/scripts/frameworks/commands/perm.js index 04b73ef1..33ef8dca 100644 --- a/build/scripts/frameworks/commands/perm.js +++ b/build/scripts/frameworks/commands/perm.js @@ -1,95 +1,95 @@ -"use strict"; -var __makeTemplateObject = (this && this.__makeTemplateObject) || function (cooked, raw) { - if (Object.defineProperty) { Object.defineProperty(cooked, "raw", { value: raw }); } else { cooked.raw = raw; } - return cooked; -}; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.Perm = void 0; -/* -Copyright © BalaM314, 2026. All Rights Reserved. -This file contains the Perm class. -*/ -var config_1 = require("/config"); -var funcs_1 = require("/funcs"); -var ranks_1 = require("/ranks"); -/** Represents a permission that is required to do something. */ -var Perm = /** @class */ (function () { - function Perm(name, check, color, unauthorizedMessage) { - if (color === void 0) { color = ""; } - if (unauthorizedMessage === void 0) { unauthorizedMessage = "You do not have the required permission (".concat(name, ") to execute this command"); } - this.name = name; - this.color = color; - this.unauthorizedMessage = unauthorizedMessage; - if (typeof check == "string") { - if (ranks_1.Rank.getByName(check) == null) - (0, funcs_1.crash)("Invalid perm ".concat(name, ": invalid rank name ").concat(check)); - this.check = function (fishP) { return fishP.ranksAtLeast(check); }; - } - else { - this.check = check; - } - Perm.perms[name] = this; - } - /** Creates a new Perm with overrides for specified gamemodes. */ - Perm.prototype.exceptModes = function (modes, unauthorizedMessage) { - var _this = this; - if (unauthorizedMessage === void 0) { unauthorizedMessage = this.unauthorizedMessage; } - return new Perm(this.name, function (fishP) { - var _a; - var effectivePerm = (_a = modes[config_1.Gamemode.name()]) !== null && _a !== void 0 ? _a : _this; - return effectivePerm.check(fishP); - }, this.color, unauthorizedMessage); - }; - Perm.fromRank = function (rank) { - return new Perm(rank.name, function (fishP) { return fishP.ranksAtLeast(rank); }, rank.color); - }; - Perm.getByName = function (name) { - var _a; - return (_a = Perm.perms[name]) !== null && _a !== void 0 ? _a : (0, funcs_1.crash)("Invalid requiredPerm"); - }; - Perm.perms = {}; - Perm.none = new Perm("none", function (fishP) { return true; }, "[sky]"); - Perm.trusted = Perm.fromRank(ranks_1.Rank.trusted); - Perm.mod = Perm.fromRank(ranks_1.Rank.mod); - Perm.admin = Perm.fromRank(ranks_1.Rank.admin); - Perm.member = new Perm("member", function (fishP) { return fishP.hasFlag("member"); }, "[pink]", "You must have a ".concat(config_1.FColor.member(templateObject_1 || (templateObject_1 = __makeTemplateObject(["Fish Membership"], ["Fish Membership"]))), " to use this command. Get a Fish Membership at[sky] ").concat(config_1.text.membershipURL, " []")); - Perm.chat = new Perm("chat", function (fishP) { return (!fishP.muted && !fishP.autoflagged) || fishP.ranksAtLeast("mod"); }); - Perm.bypassChatFilter = new Perm("bypassChatFilter", "admin"); - Perm.seeMutedMessages = new Perm("seeMutedMessages", function (fishP) { return fishP.muted || fishP.autoflagged || fishP.ranksAtLeast("mod"); }); - Perm.play = new Perm("play", function (fishP) { return !fishP.stelled() || fishP.ranksAtLeast("mod"); }); - Perm.seeErrorMessages = new Perm("seeErrorMessages", "admin"); - Perm.viewUUIDs = new Perm("viewUUIDs", "admin"); - Perm.viewIPs = new Perm("viewIPs", "admin"); - Perm.blockTrolling = new Perm("blockTrolling", function (fishP) { return fishP.rank === ranks_1.Rank.pi; }); - Perm.visualEffects = new Perm("visualEffects", function (fishP) { return (!fishP.stelled() || fishP.ranksAtLeast("mod")) && !fishP.hasFlag("no_effects"); }); - Perm.bulkVisualEffects = new Perm("bulkVisualEffects", function (fishP) { return ((fishP.hasFlag("developer") || fishP.hasFlag("illusionist") || fishP.hasFlag("member")) && !fishP.stelled()) - || fishP.ranksAtLeast("mod"); }); - Perm.bypassVoteFreeze = new Perm("bypassVoteFreeze", "trusted"); - Perm.bypassVotekick = new Perm("bypassVotekick", "mod"); - Perm.warn = new Perm("warn", "mod"); - Perm.vanish = new Perm("vanish", "mod"); - Perm.changeTeam = new Perm("changeTeam", "admin").exceptModes({ - sandbox: Perm.trusted, - attack: Perm.admin, - hexed: Perm.mod, - pvp: Perm.trusted, - minigame: Perm.trusted, - testsrv: Perm.trusted, - }); - /** Whether players should be allowed to change the team of a unit or building. If not, they will be kicked out of their current unit or building before switching teams. */ - Perm.changeTeamExternal = new Perm("changeTeamExternal", "admin").exceptModes({ - sandbox: Perm.trusted, - }); - Perm.usidCheck = new Perm("usidCheck", "trusted"); - Perm.runJS = new Perm("runJS", "manager"); - Perm.bypassNameCheck = new Perm("bypassNameCheck", "fish"); - Perm.hardcore = new Perm("hardcore", "trusted"); - Perm.massKill = new Perm("massKill", "admin").exceptModes({ - sandbox: Perm.mod, - }); - Perm.voteOtherTeams = new Perm("voteOtherTeams", "trusted"); - Perm.immediatelyVotekickNewPlayers = new Perm("immediatelyVotekickNewPlayers", "trusted"); - return Perm; -}()); -exports.Perm = Perm; -var templateObject_1; +"use strict"; +var __makeTemplateObject = (this && this.__makeTemplateObject) || function (cooked, raw) { + if (Object.defineProperty) { Object.defineProperty(cooked, "raw", { value: raw }); } else { cooked.raw = raw; } + return cooked; +}; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.Perm = void 0; +/* +Copyright © BalaM314, 2026. All Rights Reserved. +This file contains the Perm class. +*/ +var config_1 = require("/config"); +var funcs_1 = require("/funcs"); +var ranks_1 = require("/ranks"); +/** Represents a permission that is required to do something. */ +var Perm = /** @class */ (function () { + function Perm(name, check, color, unauthorizedMessage) { + if (color === void 0) { color = ""; } + if (unauthorizedMessage === void 0) { unauthorizedMessage = "You do not have the required permission (".concat(name, ") to execute this command"); } + this.name = name; + this.color = color; + this.unauthorizedMessage = unauthorizedMessage; + if (typeof check == "string") { + if (ranks_1.Rank.getByName(check) == null) + (0, funcs_1.crash)("Invalid perm ".concat(name, ": invalid rank name ").concat(check)); + this.check = function (fishP) { return fishP.ranksAtLeast(check); }; + } + else { + this.check = check; + } + Perm.perms[name] = this; + } + /** Creates a new Perm with overrides for specified gamemodes. */ + Perm.prototype.exceptModes = function (modes, unauthorizedMessage) { + var _this = this; + if (unauthorizedMessage === void 0) { unauthorizedMessage = this.unauthorizedMessage; } + return new Perm(this.name, function (fishP) { + var _a; + var effectivePerm = (_a = modes[config_1.Gamemode.name()]) !== null && _a !== void 0 ? _a : _this; + return effectivePerm.check(fishP); + }, this.color, unauthorizedMessage); + }; + Perm.fromRank = function (rank) { + return new Perm(rank.name, function (fishP) { return fishP.ranksAtLeast(rank); }, rank.color); + }; + Perm.getByName = function (name) { + var _a; + return (_a = Perm.perms[name]) !== null && _a !== void 0 ? _a : (0, funcs_1.crash)("Invalid requiredPerm"); + }; + Perm.perms = {}; + Perm.none = new Perm("none", function (fishP) { return true; }, "[sky]"); + Perm.trusted = Perm.fromRank(ranks_1.Rank.trusted); + Perm.mod = Perm.fromRank(ranks_1.Rank.mod); + Perm.admin = Perm.fromRank(ranks_1.Rank.admin); + Perm.member = new Perm("member", function (fishP) { return fishP.hasFlag("member"); }, "[pink]", "You must have a ".concat(config_1.FColor.member(templateObject_1 || (templateObject_1 = __makeTemplateObject(["Fish Membership"], ["Fish Membership"]))), " to use this command. Get a Fish Membership at[sky] ").concat(config_1.text.membershipURL, " []")); + Perm.chat = new Perm("chat", function (fishP) { return (!fishP.muted && !fishP.autoflagged) || fishP.ranksAtLeast("mod"); }); + Perm.bypassChatFilter = new Perm("bypassChatFilter", "admin"); + Perm.seeMutedMessages = new Perm("seeMutedMessages", function (fishP) { return fishP.muted || fishP.autoflagged || fishP.ranksAtLeast("mod"); }); + Perm.play = new Perm("play", function (fishP) { return !fishP.stelled() || fishP.ranksAtLeast("mod"); }); + Perm.seeErrorMessages = new Perm("seeErrorMessages", "admin"); + Perm.viewUUIDs = new Perm("viewUUIDs", "admin"); + Perm.viewIPs = new Perm("viewIPs", "admin"); + Perm.blockTrolling = new Perm("blockTrolling", function (fishP) { return fishP.rank === ranks_1.Rank.pi; }); + Perm.visualEffects = new Perm("visualEffects", function (fishP) { return (!fishP.stelled() || fishP.ranksAtLeast("mod")) && !fishP.hasFlag("no_effects"); }); + Perm.bulkVisualEffects = new Perm("bulkVisualEffects", function (fishP) { return ((fishP.hasFlag("developer") || fishP.hasFlag("illusionist") || fishP.hasFlag("member")) && !fishP.stelled()) + || fishP.ranksAtLeast("mod"); }); + Perm.bypassVoteFreeze = new Perm("bypassVoteFreeze", "trusted"); + Perm.bypassVotekick = new Perm("bypassVotekick", "mod"); + Perm.warn = new Perm("warn", "mod"); + Perm.vanish = new Perm("vanish", "mod"); + Perm.changeTeam = new Perm("changeTeam", "admin").exceptModes({ + sandbox: Perm.trusted, + attack: Perm.admin, + hexed: Perm.mod, + pvp: Perm.trusted, + minigame: Perm.trusted, + testsrv: Perm.trusted, + }); + /** Whether players should be allowed to change the team of a unit or building. If not, they will be kicked out of their current unit or building before switching teams. */ + Perm.changeTeamExternal = new Perm("changeTeamExternal", "admin").exceptModes({ + sandbox: Perm.trusted, + }); + Perm.usidCheck = new Perm("usidCheck", "trusted"); + Perm.runJS = new Perm("runJS", "manager"); + Perm.bypassNameCheck = new Perm("bypassNameCheck", "fish"); + Perm.hardcore = new Perm("hardcore", "trusted"); + Perm.massKill = new Perm("massKill", "admin").exceptModes({ + sandbox: Perm.mod, + }); + Perm.voteOtherTeams = new Perm("voteOtherTeams", "trusted"); + Perm.immediatelyVotekickNewPlayers = new Perm("immediatelyVotekickNewPlayers", "trusted"); + return Perm; +}()); +exports.Perm = Perm; +var templateObject_1; diff --git a/build/scripts/frameworks/commands/requirements.js b/build/scripts/frameworks/commands/requirements.js index 444ca5d8..6ecb4aab 100644 --- a/build/scripts/frameworks/commands/requirements.js +++ b/build/scripts/frameworks/commands/requirements.js @@ -1,92 +1,92 @@ -"use strict"; -/* -Copyright © BalaM314, 2026. All Rights Reserved. -This file contains the requirements system, which is part of the commands framework. -*/ -Object.defineProperty(exports, "__esModule", { value: true }); -exports.Req = void 0; -var config_1 = require("/config"); -var errors_1 = require("/frameworks/commands/errors"); -var utils_1 = require("/utils"); -exports.Req = { - mode: function () { - var modes = []; - for (var _i = 0; _i < arguments.length; _i++) { - modes[_i] = arguments[_i]; - } - return function () { - return modes.map(function (mode) { return config_1.Gamemode[mode](); }).some(Boolean) - || (0, errors_1.fail)("This command is only available in ".concat(modes.map(utils_1.formatModeName).join(" or "))); - }; - }, - modeNot: function (mode) { return function () { - return !config_1.Gamemode[mode]() - || (0, errors_1.fail)("This command is disabled in ".concat((0, utils_1.formatModeName)(mode))); - }; }, - moderate: function (argName, allowSameRank, minimumLevel, allowSelfIfUnauthorized) { - if (allowSameRank === void 0) { allowSameRank = false; } - if (minimumLevel === void 0) { minimumLevel = "mod"; } - if (allowSelfIfUnauthorized === void 0) { allowSelfIfUnauthorized = false; } - return function (_a) { - var args = _a.args, sender = _a.sender; - return (args[argName] == undefined || sender.canModerate(args[argName], !allowSameRank, minimumLevel, allowSelfIfUnauthorized) - || (0, errors_1.fail)("You do not have permission to perform moderation actions on this player.")); - }; - }, - cooldown: function (durationMS) { return function (_a) { - var lastUsedSuccessfullySender = _a.lastUsedSuccessfullySender; - return Date.now() - lastUsedSuccessfullySender >= durationMS - || (0, errors_1.fail)("This command was run recently and is on cooldown."); - }; }, - cooldownGlobal: function (durationMS) { return function (_a) { - var lastUsedSuccessfully = _a.lastUsedSuccessfully; - return Date.now() - lastUsedSuccessfully >= durationMS - || (0, errors_1.fail)("This command was run recently and is on cooldown."); - }; }, - gameRunning: function () { - return !Vars.state.gameOver - || (0, errors_1.fail)("This game is over, please wait for the next map to load."); - }, - teamAlive: function (_a) { - var sender = _a.sender; - return sender.team().isAlive() - || (0, errors_1.fail)(Math.random() > 0.9 ? "You are already dead." : "Your team is dead."); - }, - unitExists: function (message) { - if (message === void 0) { message = "You must be in a unit to use this command."; } - return function (_a) { - var _b; - var sender = _a.sender; - return (sender.connected() && ((_b = sender.unit()) === null || _b === void 0 ? void 0 : _b.added) && !sender.unit().dead) - || (0, errors_1.fail)(message); - }; - }, - numberRange: function (argName, min, max) { - return function (_a) { - var args = _a.args; - return args[argName] == undefined || min <= args[argName] && args[argName] <= max - || (0, errors_1.fail)("".concat(argName, " must be between ").concat(min, " and ").concat(max)); - }; - }, - integer: function (argName) { - return function (_a) { - var args = _a.args; - return args[argName] == undefined || Number.isSafeInteger(args[argName]) - || (0, errors_1.fail)("".concat(argName, " must be an integer")); - }; - }, - integerRange: function (argName, min, max) { - return function (_a) { - var args = _a.args; - return exports.Req.integer(argName)({ args: args }) && exports.Req.numberRange(argName, min, max)({ args: args }); - }; - }, - positiveInteger: function (argName) { - return function (_a) { - var args = _a.args; - return exports.Req.integer(argName)({ args: args }) && - (args[argName] == undefined || args[argName] > 0 - || (0, errors_1.fail)("".concat(argName, " must be positive"))); - }; - }, -}; +"use strict"; +/* +Copyright © BalaM314, 2026. All Rights Reserved. +This file contains the requirements system, which is part of the commands framework. +*/ +Object.defineProperty(exports, "__esModule", { value: true }); +exports.Req = void 0; +var config_1 = require("/config"); +var errors_1 = require("/frameworks/commands/errors"); +var utils_1 = require("/utils"); +exports.Req = { + mode: function () { + var modes = []; + for (var _i = 0; _i < arguments.length; _i++) { + modes[_i] = arguments[_i]; + } + return function () { + return modes.map(function (mode) { return config_1.Gamemode[mode](); }).some(Boolean) + || (0, errors_1.fail)("This command is only available in ".concat(modes.map(utils_1.formatModeName).join(" or "))); + }; + }, + modeNot: function (mode) { return function () { + return !config_1.Gamemode[mode]() + || (0, errors_1.fail)("This command is disabled in ".concat((0, utils_1.formatModeName)(mode))); + }; }, + moderate: function (argName, allowSameRank, minimumLevel, allowSelfIfUnauthorized) { + if (allowSameRank === void 0) { allowSameRank = false; } + if (minimumLevel === void 0) { minimumLevel = "mod"; } + if (allowSelfIfUnauthorized === void 0) { allowSelfIfUnauthorized = false; } + return function (_a) { + var args = _a.args, sender = _a.sender; + return (args[argName] == undefined || sender.canModerate(args[argName], !allowSameRank, minimumLevel, allowSelfIfUnauthorized) + || (0, errors_1.fail)("You do not have permission to perform moderation actions on this player.")); + }; + }, + cooldown: function (durationMS) { return function (_a) { + var lastUsedSuccessfullySender = _a.lastUsedSuccessfullySender; + return Date.now() - lastUsedSuccessfullySender >= durationMS + || (0, errors_1.fail)("This command was run recently and is on cooldown."); + }; }, + cooldownGlobal: function (durationMS) { return function (_a) { + var lastUsedSuccessfully = _a.lastUsedSuccessfully; + return Date.now() - lastUsedSuccessfully >= durationMS + || (0, errors_1.fail)("This command was run recently and is on cooldown."); + }; }, + gameRunning: function () { + return !Vars.state.gameOver + || (0, errors_1.fail)("This game is over, please wait for the next map to load."); + }, + teamAlive: function (_a) { + var sender = _a.sender; + return sender.team().isAlive() + || (0, errors_1.fail)(Math.random() > 0.9 ? "You are already dead." : "Your team is dead."); + }, + unitExists: function (message) { + if (message === void 0) { message = "You must be in a unit to use this command."; } + return function (_a) { + var _b; + var sender = _a.sender; + return (sender.connected() && ((_b = sender.unit()) === null || _b === void 0 ? void 0 : _b.added) && !sender.unit().dead) + || (0, errors_1.fail)(message); + }; + }, + numberRange: function (argName, min, max) { + return function (_a) { + var args = _a.args; + return args[argName] == undefined || min <= args[argName] && args[argName] <= max + || (0, errors_1.fail)("".concat(argName, " must be between ").concat(min, " and ").concat(max)); + }; + }, + integer: function (argName) { + return function (_a) { + var args = _a.args; + return args[argName] == undefined || Number.isSafeInteger(args[argName]) + || (0, errors_1.fail)("".concat(argName, " must be an integer")); + }; + }, + integerRange: function (argName, min, max) { + return function (_a) { + var args = _a.args; + return exports.Req.integer(argName)({ args: args }) && exports.Req.numberRange(argName, min, max)({ args: args }); + }; + }, + positiveInteger: function (argName) { + return function (_a) { + var args = _a.args; + return exports.Req.integer(argName)({ args: args }) && + (args[argName] == undefined || args[argName] > 0 + || (0, errors_1.fail)("".concat(argName, " must be positive"))); + }; + }, +}; diff --git a/build/scripts/frameworks/commands/types.js b/build/scripts/frameworks/commands/types.js index 6f4b15c0..d5e540cc 100644 --- a/build/scripts/frameworks/commands/types.js +++ b/build/scripts/frameworks/commands/types.js @@ -1,29 +1,29 @@ -"use strict"; -/* -Copyright © BalaM314, 2026. All Rights Reserved. -This file contains types for the commands framework. -*/ -Object.defineProperty(exports, "__esModule", { value: true }); -exports.commandArgNames = exports.commandArgTypes = void 0; -/** All valid command arg types. */ -exports.commandArgTypes = [ - "string", "number", "boolean", "player", /*"menuPlayer",*/ "team", "time", "unittype", "block", - "uuid", "offlinePlayer", "map", "mapOrRandom", "rank", "roleflag", "item" -]; -exports.commandArgNames = { - string: "text", - number: "number", - boolean: "boolean", - player: "player", - team: "team", - time: "duration", - unittype: "unit type", - block: "block", - uuid: "UUID", - offlinePlayer: "player", - map: "map", - mapOrRandom: "map", - rank: "rank", - roleflag: "role flag", - item: "item" -}; +"use strict"; +/* +Copyright © BalaM314, 2026. All Rights Reserved. +This file contains types for the commands framework. +*/ +Object.defineProperty(exports, "__esModule", { value: true }); +exports.commandArgNames = exports.commandArgTypes = void 0; +/** All valid command arg types. */ +exports.commandArgTypes = [ + "string", "number", "boolean", "player", /*"menuPlayer",*/ "team", "time", "unittype", "block", + "uuid", "offlinePlayer", "map", "mapOrRandom", "rank", "roleflag", "item" +]; +exports.commandArgNames = { + string: "text", + number: "number", + boolean: "boolean", + player: "player", + team: "team", + time: "duration", + unittype: "unit type", + block: "block", + uuid: "UUID", + offlinePlayer: "player", + map: "map", + mapOrRandom: "map", + rank: "rank", + roleflag: "role flag", + item: "item" +}; diff --git a/build/scripts/frameworks/io.js b/build/scripts/frameworks/io.js index 05f31124..f4ad2059 100644 --- a/build/scripts/frameworks/io.js +++ b/build/scripts/frameworks/io.js @@ -1,388 +1,388 @@ -"use strict"; -var __extends = (this && this.__extends) || (function () { - var extendStatics = function (d, b) { - extendStatics = Object.setPrototypeOf || - ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || - function (d, b) { for (var p in b) if (Object.prototype.hasOwnProperty.call(b, p)) d[p] = b[p]; }; - return extendStatics(d, b); - }; - return function (d, b) { - if (typeof b !== "function" && b !== null) - throw new TypeError("Class extends value " + String(b) + " is not a constructor or null"); - extendStatics(d, b); - function __() { this.constructor = d; } - d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); - }; -})(); -var __values = (this && this.__values) || function(o) { - var s = typeof Symbol === "function" && Symbol.iterator, m = s && o[s], i = 0; - if (m) return m.call(o); - if (o && typeof o.length === "number") return { - next: function () { - if (o && i >= o.length) o = void 0; - return { value: o && o[i++], done: !o }; - } - }; - throw new TypeError(s ? "Object is not iterable." : "Symbol.iterator is not defined."); -}; -var __read = (this && this.__read) || function (o, n) { - var m = typeof Symbol === "function" && o[Symbol.iterator]; - if (!m) return o; - var i = m.call(o), r, ar = [], e; - try { - while ((n === void 0 || n-- > 0) && !(r = i.next()).done) ar.push(r.value); - } - catch (error) { e = { error: error }; } - finally { - try { - if (r && !r.done && (m = i["return"])) m.call(i); - } - finally { if (e) throw e.error; } - } - return ar; -}; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.SettingsSerializer = exports.Serializer = exports.DataClass = void 0; -exports.dataClass = dataClass; -exports.serialize = serialize; -/* -Copyright © BalaM314, 2026. All Rights Reserved. -This file contains the serialization framework. -For usage information, see docs/framework-usage-guide.md -For maintenance information, see docs/frameworks.md -*/ -var funcs_1 = require("/funcs"); -var globals_1 = require("/globals"); -var DataClass = /** @class */ (function () { - function DataClass(data) { - Object.assign(this, data); - } - return DataClass; -}()); -exports.DataClass = DataClass; -function dataClass() { - return DataClass; -} -function checkBounds(type, value, min, max) { - if (value < min) { - Log.warn("Integer underflow when serializing ".concat(type, ": value ").concat(value, " was less than ").concat(min)); - return min; - } - if (value >= max) { - Log.warn("Integer overflow when serializing ".concat(type, ": value ").concat(value, " was greater than ").concat(max)); - return max; - } - return value; -} -var Serializer = /** @class */ (function () { - function Serializer(schema, oldSchema) { - this.schema = schema; - this.oldSchema = oldSchema; - } - Serializer.prototype.write = function (object, output) { - Serializer.writeNode(this.schema, object, output); - }; - Serializer.prototype.read = function (input) { - input.mark(0xFFFF); //1MB - try { - return Serializer.readNode(this.schema, input); - } - catch (err) { - Log.warn("Using fallback schema: this message should go away after a restart"); - input.reset(); - if (this.oldSchema) - return Serializer.readNode(this.oldSchema, input); - else - throw err; - } - }; - Serializer.writeNode = function (schema, value, output) { - var e_1, _a, e_2, _b; - var checkNumbers = false; - switch (schema[0]) { - case 'string': - output.writeUTF(value); - break; - case 'number': - if (checkNumbers) { - switch (schema[1]) { - case 'u8': - output.writeByte(checkBounds(schema[1], value, 0, Math.pow(2, 8))); - break; - case 'u16': - output.writeShort(checkBounds(schema[1], value, 0, Math.pow(2, 16))); - break; - case 'u32': - output.writeInt(checkBounds(schema[1], value, 0, Math.pow(2, 32)) & 0xFFFFFFFF); - break; - case 'i8': - output.writeByte(checkBounds(schema[1], value, -(Math.pow(2, 7)), (Math.pow(2, 7)))); - break; - case 'i16': - output.writeShort(checkBounds(schema[1], value, -(Math.pow(2, 15)), (Math.pow(2, 15)))); - break; - case 'i32': - output.writeInt(checkBounds(schema[1], value, -(Math.pow(2, 31)), (Math.pow(2, 31)))); - break; - case 'i64': - output.writeLong(checkBounds(schema[1], value, -(Math.pow(2, 63)), (Math.pow(2, 63)))); - break; - case 'f32': - output.writeFloat(isNaN(value) || !isFinite(value) ? (Log.warn('Attempted to write a NaN floating-point value, defaulting to 0'), 0) : value); - break; - case 'f64': - output.writeDouble(isNaN(value) || !isFinite(value) ? (Log.warn('Attempted to write a NaN floating-point value, defaulting to 0'), 0) : value); - break; - } - } - else { - switch (schema[1]) { - case 'u8': - output.writeByte(value); - break; - case 'u16': - output.writeShort(value); - break; - case 'u32': - output.writeInt(value & 0xFFFFFFFF); - break; //If the value is greater than 0x7FFFFFFF, make it negative so Java writes it correctly - case 'i8': - output.writeByte(value); - break; - case 'i16': - output.writeShort(value); - break; - case 'i32': - output.writeInt(value); - break; - case 'i64': - output.writeLong(value); - break; - case 'f32': - output.writeFloat(value); - break; - case 'f64': - output.writeDouble(value); - break; - } - } - break; - case 'boolean': - output.writeBoolean(value); - break; - case 'team': - if (!value) - Log.err("attempting to serialize a Team, but it was null"); //temporary debug message - output.writeByte(value.id); - break; - case 'object': - try { - for (var _c = __values(schema[1]), _d = _c.next(); !_d.done; _d = _c.next()) { - var _e = __read(_d.value, 2), key = _e[0], childSchema = _e[1]; - //correspondence - this.writeNode(childSchema, value[key], output); - } - } - catch (e_1_1) { e_1 = { error: e_1_1 }; } - finally { - try { - if (_d && !_d.done && (_a = _c.return)) _a.call(_c); - } - finally { if (e_1) throw e_1.error; } - } - break; - case 'class': - try { - for (var _f = __values(schema[2]), _g = _f.next(); !_g.done; _g = _f.next()) { - var _h = __read(_g.value, 2), key = _h[0], childSchema = _h[1]; - //correspondence - this.writeNode(childSchema, value[key], output); - } - } - catch (e_2_1) { e_2 = { error: e_2_1 }; } - finally { - try { - if (_g && !_g.done && (_b = _f.return)) _b.call(_f); - } - finally { if (e_2) throw e_2.error; } - } - break; - case 'array': - if (typeof schema[1] == "string") { - this.writeNode(["number", schema[1]], value.length, output); - } - else { - if (schema[1] !== value.length) { - Log.err('SERIALIZATION WARNING: received invalid data: array with greater length than specified by schema'); - value.length = schema[1]; - } - } - // eslint-disable-next-line @typescript-eslint/prefer-for-of - for (var i = 0; i < value.length; i++) { - this.writeNode(schema[2], value[i], output); - } - break; - case 'version': - output.writeByte(schema[1]); - this.writeNode(schema[2], value, output); - break; - } - }; - Serializer.readNode = function (schema, input) { - var e_3, _a, e_4, _b; - switch (schema[0]) { - case 'string': - return input.readUTF(); - case 'number': - switch (schema[1]) { - case 'u8': return input.readUnsignedByte(); - case 'u16': return input.readUnsignedShort(); - case 'u32': { - var value = input.readInt(); //Java does not support unsigned ints - return value < 0 ? value + Math.pow(2, 32) : value; - } - case 'i8': return input.readByte(); - case 'i16': return input.readShort(); - case 'i32': return input.readInt(); - case 'i64': return input.readLong(); - case 'f32': return input.readFloat(); - case 'f64': return input.readDouble(); - } - schema[1]; - break; - case 'boolean': - return input.readBoolean(); - case 'team': - return Team.all[input.readByte()]; - case 'object': { - var output = {}; - try { - for (var _c = __values(schema[1]), _d = _c.next(); !_d.done; _d = _c.next()) { - var _e = __read(_d.value, 2), key = _e[0], childSchema = _e[1]; - output[key] = this.readNode(childSchema, input); - } - } - catch (e_3_1) { e_3 = { error: e_3_1 }; } - finally { - try { - if (_d && !_d.done && (_a = _c.return)) _a.call(_c); - } - finally { if (e_3) throw e_3.error; } - } - return output; - } - case 'class': { - var classData = {}; - try { - for (var _f = __values(schema[2]), _g = _f.next(); !_g.done; _g = _f.next()) { - var _h = __read(_g.value, 2), key = _h[0], childSchema = _h[1]; - classData[key] = this.readNode(childSchema, input); - } - } - catch (e_4_1) { e_4 = { error: e_4_1 }; } - finally { - try { - if (_g && !_g.done && (_b = _f.return)) _b.call(_f); - } - finally { if (e_4) throw e_4.error; } - } - return new schema[1](classData); - } - case 'array': { - var length = typeof schema[1] === "number" ? - schema[1] - : this.readNode(["number", schema[1]], input); - var array = new Array(length); - for (var i = 0; i < length; i++) { - array[i] = this.readNode(schema[2], input); - } - return array; - } - case 'version': { - var version = input.readByte(); - if (version !== schema[1]) - (0, funcs_1.crash)("Expected version ".concat(schema[1], ", but read ").concat(version)); - return this.readNode(schema[2], input); - } - } - }; - return Serializer; -}()); -exports.Serializer = Serializer; -var SettingsSerializer = /** @class */ (function (_super) { - __extends(SettingsSerializer, _super); - function SettingsSerializer(settingsKey, schema, oldSchema) { - var _this = _super.call(this, schema, oldSchema) || this; - _this.settingsKey = settingsKey; - _this.schema = schema; - _this.oldSchema = oldSchema; - return _this; - } - SettingsSerializer.prototype.writeSettings = function (object) { - var output = new ByteArrayOutputStream(); - this.write(object, new DataOutputStream(output)); - Core.settings.put(this.settingsKey, output.toByteArray()); - }; - SettingsSerializer.prototype.readSettings = function () { - var data = Core.settings.getBytes(this.settingsKey); - if (data) - return this.read(new DataInputStream(new ByteArrayInputStream(data))); - else - return null; - }; - return SettingsSerializer; -}(Serializer)); -exports.SettingsSerializer = SettingsSerializer; -if (!Symbol.metadata) - Object.defineProperty(Symbol, "metadata", { - writable: false, - enumerable: false, - configurable: false, - value: Symbol("Symbol.metadata") - }); -var valuesToSerialize = new AtomicInteger(); -function serialize(settingsKey, schema, oldSchema, fixer, saveEvent) { - if (saveEvent === void 0) { saveEvent = "saveData"; } - return function decorate(_, _a) { - var addInitializer = _a.addInitializer, access = _a.access, name = _a.name; - valuesToSerialize.getAndIncrement(); - addInitializer(function () { - var _this = this; - var serializer = (0, funcs_1.lazy)(function () { - return new SettingsSerializer(settingsKey, schema(), oldSchema === null || oldSchema === void 0 ? void 0 : oldSchema()); - }); - globals_1.FishEvents.on("loadData", function () { return Threads.daemon(function () { - //Load data multithreaded - var start = Time.nanos(); - var value = serializer().readSettings(); - if (value) { - if (fixer) - value = fixer(value); - access.set(_this, value); - } - if (valuesToSerialize.decrementAndGet() == 0) - globals_1.FishEvents.fire("dataLoaded", []); - Log.debug("serialize read @ @", settingsKey, (Time.nanos() - start) / 1e6); - }); }); - globals_1.FishEvents.on(saveEvent, function () { - try { - Time.mark(); - var value = access.get(_this); - if (value == null) - return; - serializer().writeSettings(value); - Log.debug("serialize save @ @", settingsKey, Time.elapsed()); - } - catch (err) { - Log.err("Error while saving field ".concat(String(name), " on ").concat(String(_this === null || _this === void 0 ? void 0 : _this.name), " using settings key ").concat(settingsKey)); - Log.info(JSON.stringify(access.get(_this))); - throw err; - } - }); - }); - }; -} -globals_1.FishEvents.on("loadData", function () { - if (valuesToSerialize.get() == 0) - globals_1.FishEvents.fire("dataLoaded", []); -}); +"use strict"; +var __extends = (this && this.__extends) || (function () { + var extendStatics = function (d, b) { + extendStatics = Object.setPrototypeOf || + ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || + function (d, b) { for (var p in b) if (Object.prototype.hasOwnProperty.call(b, p)) d[p] = b[p]; }; + return extendStatics(d, b); + }; + return function (d, b) { + if (typeof b !== "function" && b !== null) + throw new TypeError("Class extends value " + String(b) + " is not a constructor or null"); + extendStatics(d, b); + function __() { this.constructor = d; } + d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); + }; +})(); +var __values = (this && this.__values) || function(o) { + var s = typeof Symbol === "function" && Symbol.iterator, m = s && o[s], i = 0; + if (m) return m.call(o); + if (o && typeof o.length === "number") return { + next: function () { + if (o && i >= o.length) o = void 0; + return { value: o && o[i++], done: !o }; + } + }; + throw new TypeError(s ? "Object is not iterable." : "Symbol.iterator is not defined."); +}; +var __read = (this && this.__read) || function (o, n) { + var m = typeof Symbol === "function" && o[Symbol.iterator]; + if (!m) return o; + var i = m.call(o), r, ar = [], e; + try { + while ((n === void 0 || n-- > 0) && !(r = i.next()).done) ar.push(r.value); + } + catch (error) { e = { error: error }; } + finally { + try { + if (r && !r.done && (m = i["return"])) m.call(i); + } + finally { if (e) throw e.error; } + } + return ar; +}; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.SettingsSerializer = exports.Serializer = exports.DataClass = void 0; +exports.dataClass = dataClass; +exports.serialize = serialize; +/* +Copyright © BalaM314, 2026. All Rights Reserved. +This file contains the serialization framework. +For usage information, see docs/framework-usage-guide.md +For maintenance information, see docs/frameworks.md +*/ +var funcs_1 = require("/funcs"); +var globals_1 = require("/globals"); +var DataClass = /** @class */ (function () { + function DataClass(data) { + Object.assign(this, data); + } + return DataClass; +}()); +exports.DataClass = DataClass; +function dataClass() { + return DataClass; +} +function checkBounds(type, value, min, max) { + if (value < min) { + Log.warn("Integer underflow when serializing ".concat(type, ": value ").concat(value, " was less than ").concat(min)); + return min; + } + if (value >= max) { + Log.warn("Integer overflow when serializing ".concat(type, ": value ").concat(value, " was greater than ").concat(max)); + return max; + } + return value; +} +var Serializer = /** @class */ (function () { + function Serializer(schema, oldSchema) { + this.schema = schema; + this.oldSchema = oldSchema; + } + Serializer.prototype.write = function (object, output) { + Serializer.writeNode(this.schema, object, output); + }; + Serializer.prototype.read = function (input) { + input.mark(0xFFFF); //1MB + try { + return Serializer.readNode(this.schema, input); + } + catch (err) { + Log.warn("Using fallback schema: this message should go away after a restart"); + input.reset(); + if (this.oldSchema) + return Serializer.readNode(this.oldSchema, input); + else + throw err; + } + }; + Serializer.writeNode = function (schema, value, output) { + var e_1, _a, e_2, _b; + var checkNumbers = false; + switch (schema[0]) { + case 'string': + output.writeUTF(value); + break; + case 'number': + if (checkNumbers) { + switch (schema[1]) { + case 'u8': + output.writeByte(checkBounds(schema[1], value, 0, Math.pow(2, 8))); + break; + case 'u16': + output.writeShort(checkBounds(schema[1], value, 0, Math.pow(2, 16))); + break; + case 'u32': + output.writeInt(checkBounds(schema[1], value, 0, Math.pow(2, 32)) & 0xFFFFFFFF); + break; + case 'i8': + output.writeByte(checkBounds(schema[1], value, -(Math.pow(2, 7)), (Math.pow(2, 7)))); + break; + case 'i16': + output.writeShort(checkBounds(schema[1], value, -(Math.pow(2, 15)), (Math.pow(2, 15)))); + break; + case 'i32': + output.writeInt(checkBounds(schema[1], value, -(Math.pow(2, 31)), (Math.pow(2, 31)))); + break; + case 'i64': + output.writeLong(checkBounds(schema[1], value, -(Math.pow(2, 63)), (Math.pow(2, 63)))); + break; + case 'f32': + output.writeFloat(isNaN(value) || !isFinite(value) ? (Log.warn('Attempted to write a NaN floating-point value, defaulting to 0'), 0) : value); + break; + case 'f64': + output.writeDouble(isNaN(value) || !isFinite(value) ? (Log.warn('Attempted to write a NaN floating-point value, defaulting to 0'), 0) : value); + break; + } + } + else { + switch (schema[1]) { + case 'u8': + output.writeByte(value); + break; + case 'u16': + output.writeShort(value); + break; + case 'u32': + output.writeInt(value & 0xFFFFFFFF); + break; //If the value is greater than 0x7FFFFFFF, make it negative so Java writes it correctly + case 'i8': + output.writeByte(value); + break; + case 'i16': + output.writeShort(value); + break; + case 'i32': + output.writeInt(value); + break; + case 'i64': + output.writeLong(value); + break; + case 'f32': + output.writeFloat(value); + break; + case 'f64': + output.writeDouble(value); + break; + } + } + break; + case 'boolean': + output.writeBoolean(value); + break; + case 'team': + if (!value) + Log.err("attempting to serialize a Team, but it was null"); //temporary debug message + output.writeByte(value.id); + break; + case 'object': + try { + for (var _c = __values(schema[1]), _d = _c.next(); !_d.done; _d = _c.next()) { + var _e = __read(_d.value, 2), key = _e[0], childSchema = _e[1]; + //correspondence + this.writeNode(childSchema, value[key], output); + } + } + catch (e_1_1) { e_1 = { error: e_1_1 }; } + finally { + try { + if (_d && !_d.done && (_a = _c.return)) _a.call(_c); + } + finally { if (e_1) throw e_1.error; } + } + break; + case 'class': + try { + for (var _f = __values(schema[2]), _g = _f.next(); !_g.done; _g = _f.next()) { + var _h = __read(_g.value, 2), key = _h[0], childSchema = _h[1]; + //correspondence + this.writeNode(childSchema, value[key], output); + } + } + catch (e_2_1) { e_2 = { error: e_2_1 }; } + finally { + try { + if (_g && !_g.done && (_b = _f.return)) _b.call(_f); + } + finally { if (e_2) throw e_2.error; } + } + break; + case 'array': + if (typeof schema[1] == "string") { + this.writeNode(["number", schema[1]], value.length, output); + } + else { + if (schema[1] !== value.length) { + Log.err('SERIALIZATION WARNING: received invalid data: array with greater length than specified by schema'); + value.length = schema[1]; + } + } + // eslint-disable-next-line @typescript-eslint/prefer-for-of + for (var i = 0; i < value.length; i++) { + this.writeNode(schema[2], value[i], output); + } + break; + case 'version': + output.writeByte(schema[1]); + this.writeNode(schema[2], value, output); + break; + } + }; + Serializer.readNode = function (schema, input) { + var e_3, _a, e_4, _b; + switch (schema[0]) { + case 'string': + return input.readUTF(); + case 'number': + switch (schema[1]) { + case 'u8': return input.readUnsignedByte(); + case 'u16': return input.readUnsignedShort(); + case 'u32': { + var value = input.readInt(); //Java does not support unsigned ints + return value < 0 ? value + Math.pow(2, 32) : value; + } + case 'i8': return input.readByte(); + case 'i16': return input.readShort(); + case 'i32': return input.readInt(); + case 'i64': return input.readLong(); + case 'f32': return input.readFloat(); + case 'f64': return input.readDouble(); + } + schema[1]; + break; + case 'boolean': + return input.readBoolean(); + case 'team': + return Team.all[input.readByte()]; + case 'object': { + var output = {}; + try { + for (var _c = __values(schema[1]), _d = _c.next(); !_d.done; _d = _c.next()) { + var _e = __read(_d.value, 2), key = _e[0], childSchema = _e[1]; + output[key] = this.readNode(childSchema, input); + } + } + catch (e_3_1) { e_3 = { error: e_3_1 }; } + finally { + try { + if (_d && !_d.done && (_a = _c.return)) _a.call(_c); + } + finally { if (e_3) throw e_3.error; } + } + return output; + } + case 'class': { + var classData = {}; + try { + for (var _f = __values(schema[2]), _g = _f.next(); !_g.done; _g = _f.next()) { + var _h = __read(_g.value, 2), key = _h[0], childSchema = _h[1]; + classData[key] = this.readNode(childSchema, input); + } + } + catch (e_4_1) { e_4 = { error: e_4_1 }; } + finally { + try { + if (_g && !_g.done && (_b = _f.return)) _b.call(_f); + } + finally { if (e_4) throw e_4.error; } + } + return new schema[1](classData); + } + case 'array': { + var length = typeof schema[1] === "number" ? + schema[1] + : this.readNode(["number", schema[1]], input); + var array = new Array(length); + for (var i = 0; i < length; i++) { + array[i] = this.readNode(schema[2], input); + } + return array; + } + case 'version': { + var version = input.readByte(); + if (version !== schema[1]) + (0, funcs_1.crash)("Expected version ".concat(schema[1], ", but read ").concat(version)); + return this.readNode(schema[2], input); + } + } + }; + return Serializer; +}()); +exports.Serializer = Serializer; +var SettingsSerializer = /** @class */ (function (_super) { + __extends(SettingsSerializer, _super); + function SettingsSerializer(settingsKey, schema, oldSchema) { + var _this = _super.call(this, schema, oldSchema) || this; + _this.settingsKey = settingsKey; + _this.schema = schema; + _this.oldSchema = oldSchema; + return _this; + } + SettingsSerializer.prototype.writeSettings = function (object) { + var output = new ByteArrayOutputStream(); + this.write(object, new DataOutputStream(output)); + Core.settings.put(this.settingsKey, output.toByteArray()); + }; + SettingsSerializer.prototype.readSettings = function () { + var data = Core.settings.getBytes(this.settingsKey); + if (data) + return this.read(new DataInputStream(new ByteArrayInputStream(data))); + else + return null; + }; + return SettingsSerializer; +}(Serializer)); +exports.SettingsSerializer = SettingsSerializer; +if (!Symbol.metadata) + Object.defineProperty(Symbol, "metadata", { + writable: false, + enumerable: false, + configurable: false, + value: Symbol("Symbol.metadata") + }); +var valuesToSerialize = new AtomicInteger(); +function serialize(settingsKey, schema, oldSchema, fixer, saveEvent) { + if (saveEvent === void 0) { saveEvent = "saveData"; } + return function decorate(_, _a) { + var addInitializer = _a.addInitializer, access = _a.access, name = _a.name; + valuesToSerialize.getAndIncrement(); + addInitializer(function () { + var _this = this; + var serializer = (0, funcs_1.lazy)(function () { + return new SettingsSerializer(settingsKey, schema(), oldSchema === null || oldSchema === void 0 ? void 0 : oldSchema()); + }); + globals_1.FishEvents.on("loadData", function () { return Threads.daemon(function () { + //Load data multithreaded + var start = Time.nanos(); + var value = serializer().readSettings(); + if (value) { + if (fixer) + value = fixer(value); + access.set(_this, value); + } + if (valuesToSerialize.decrementAndGet() == 0) + globals_1.FishEvents.fire("dataLoaded", []); + Log.debug("serialize read @ @", settingsKey, (Time.nanos() - start) / 1e6); + }); }); + globals_1.FishEvents.on(saveEvent, function () { + try { + Time.mark(); + var value = access.get(_this); + if (value == null) + return; + serializer().writeSettings(value); + Log.debug("serialize save @ @", settingsKey, Time.elapsed()); + } + catch (err) { + Log.err("Error while saving field ".concat(String(name), " on ").concat(String(_this === null || _this === void 0 ? void 0 : _this.name), " using settings key ").concat(settingsKey)); + Log.info(JSON.stringify(access.get(_this))); + throw err; + } + }); + }); + }; +} +globals_1.FishEvents.on("loadData", function () { + if (valuesToSerialize.get() == 0) + globals_1.FishEvents.fire("dataLoaded", []); +}); diff --git a/build/scripts/frameworks/menus.js b/build/scripts/frameworks/menus.js index 95176556..740c2f50 100644 --- a/build/scripts/frameworks/menus.js +++ b/build/scripts/frameworks/menus.js @@ -1,453 +1,453 @@ -"use strict"; -/* eslint-disable @typescript-eslint/array-type */ -/* -Copyright © BalaM314, 2026. All Rights Reserved. -This file contains the menu framework. -For usage information, see docs/framework-usage-guide.md -For maintenance information, see docs/frameworks.md -*/ -var __assign = (this && this.__assign) || function () { - __assign = Object.assign || function(t) { - for (var s, i = 1, n = arguments.length; i < n; i++) { - s = arguments[i]; - for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p)) - t[p] = s[p]; - } - return t; - }; - return __assign.apply(this, arguments); -}; -var __rest = (this && this.__rest) || function (s, e) { - var t = {}; - for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0) - t[p] = s[p]; - if (s != null && typeof Object.getOwnPropertySymbols === "function") - for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) { - if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i])) - t[p[i]] = s[p[i]]; - } - return t; -}; -var __values = (this && this.__values) || function(o) { - var s = typeof Symbol === "function" && Symbol.iterator, m = s && o[s], i = 0; - if (m) return m.call(o); - if (o && typeof o.length === "number") return { - next: function () { - if (o && i >= o.length) o = void 0; - return { value: o && o[i++], done: !o }; - } - }; - throw new TypeError(s ? "Object is not iterable." : "Symbol.iterator is not defined."); -}; -var __read = (this && this.__read) || function (o, n) { - var m = typeof Symbol === "function" && o[Symbol.iterator]; - if (!m) return o; - var i = m.call(o), r, ar = [], e; - try { - while ((n === void 0 || n-- > 0) && !(r = i.next()).done) ar.push(r.value); - } - catch (error) { e = { error: error }; } - finally { - try { - if (r && !r.done && (m = i["return"])) m.call(i); - } - finally { if (e) throw e.error; } - } - return ar; -}; -var __spreadArray = (this && this.__spreadArray) || function (to, from, pack) { - if (pack || arguments.length === 2) for (var i = 0, l = from.length, ar; i < l; i++) { - if (ar || !(i in from)) { - if (!ar) ar = Array.prototype.slice.call(from, 0, i); - ar[i] = from[i]; - } - } - return to.concat(ar || Array.prototype.slice.call(from)); -}; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.textListeners = exports.listeners = exports.Menu = exports.Cancel = void 0; -exports.registerListeners = registerListeners; -var commands_1 = require("/frameworks/commands"); -var funcs_1 = require("/funcs"); -var players_1 = require("/players"); -var promise_1 = require("/promise"); -var utils_1 = require("/utils"); -/** Used to change the behavior of adding another menu when being run in a menu callback. */ -var isInMenuCallback = false; -/** Stores a mapping from name to the numeric id of a listener that has been registered. */ -var registeredListeners = {}; -exports.listeners = registeredListeners; -/** Stores all listeners in use by fish-commands. */ -var listeners = { - generic: function (player, option) { - var fishSender = players_1.FishPlayer.get(player); - var prevCallback = fishSender.activeMenus.shift(); - if (!prevCallback) - return; //No menu to process, do nothing - if (prevCallback.type != "menu") - return; - isInMenuCallback = true; - prevCallback.callback(option); - isInMenuCallback = false; - }, - none: function (player, option) { - //do nothing - } -}; -/** Stores a mapping from name to the numeric id of a listener that has been registered. */ -var registeredTextListeners = {}; -exports.textListeners = registeredTextListeners; -/** Stores all listeners in use by fish-commands. */ -var textListeners = { - generic: function (player, text) { - var fishSender = players_1.FishPlayer.get(player); - var prevCallback = fishSender.activeMenus.shift(); - if (!prevCallback) - return; //No menu to process, do nothing - if (prevCallback.type != "text") - return; - isInMenuCallback = true; - prevCallback.callback(text); - isInMenuCallback = false; - }, - none: function (player, option) { - //do nothing - } -}; -exports.Cancel = Symbol("Cancel"); -/** Registers all listeners, should be called on server load. */ -function registerListeners() { - var e_1, _a, e_2, _b; - var _c, _d; - try { - for (var _e = __values(Object.entries(listeners)), _f = _e.next(); !_f.done; _f = _e.next()) { - var _g = __read(_f.value, 2), key = _g[0], listener = _g[1]; - (_c = registeredListeners[key]) !== null && _c !== void 0 ? _c : (registeredListeners[key] = Menus.registerMenu(listener)); - } - } - catch (e_1_1) { e_1 = { error: e_1_1 }; } - finally { - try { - if (_f && !_f.done && (_a = _e.return)) _a.call(_e); - } - finally { if (e_1) throw e_1.error; } - } - try { - for (var _h = __values(Object.entries(textListeners)), _j = _h.next(); !_j.done; _j = _h.next()) { - var _k = __read(_j.value, 2), key = _k[0], listener = _k[1]; - (_d = registeredTextListeners[key]) !== null && _d !== void 0 ? _d : (registeredTextListeners[key] = Menus.registerTextInput(listener)); - } - } - catch (e_2_1) { e_2 = { error: e_2_1 }; } - finally { - try { - if (_j && !_j.done && (_b = _h.return)) _b.call(_h); - } - finally { if (e_2) throw e_2.error; } - } -} -exports.Menu = { - /** Displays a menu to a player, returning a Promise. */ - raw: function (title, description, arrangedOptions, target, _a) { - var _b = _a === void 0 ? {} : _a, _c = _b.optionStringifier, optionStringifier = _c === void 0 ? String : _c, _d = _b.onCancel, onCancel = _d === void 0 ? "reject" : _d, _e = _b.cancelOptionId, cancelOptionId = _e === void 0 ? -1 : _e; - var _f = promise_1.Promise.withResolvers(), promise = _f.promise, reject = _f.reject, resolve = _f.resolve; - //The target fishPlayer has a property called activeMenu, which stores information about the last menu triggered. - //If menu() is being called from a menu calback, add it to the front of the queue so it is processed before any other menus. - //Otherwise, two multi-step menus queued together would alternate, which would confuse the player. - target.activeMenus[isInMenuCallback ? "unshift" : "push"]({ type: "menu", callback: function (option) { - //Additional permission validation could be done here, but the only way that callback() can be called is if the above statement executed, - //and on sensitive menus such as the stop menu, the only way to reach that is if menu() was called by the /stop command, - //which already checks permissions. - //Additionally, the callback is cleared by the generic menu listener after it is executed. - try { - var options = arrangedOptions.flat(); - //We do need to validate option though, as it can be any number. - if (option === -1 || option === cancelOptionId || !(option in options)) { - //Consider any invalid option to be a cancellation - if (onCancel == "null") - resolve(null); - else if (onCancel == "reject") - reject(exports.Cancel); - else - return; - } - else { - resolve(options[option]); - } - } - catch (err) { - (0, utils_1.handleError)(err, target, utils_1.outputFail, "".concat(target.cleanedName, " submitted menu \"").concat(title, "\" \"").concat(description, "\"")); - } - } }); - var i = 0; - var stringifiedOptions = arrangedOptions.map(function (r) { return r.map(function (item) { - if (i === cancelOptionId) - return item; - i++; - return optionStringifier(item); - }); }); - Call.menu(target.con, registeredListeners.generic, title, description, stringifiedOptions); - return promise; - }, - /** Displays a text input menu to a player, returning a Promise. */ - text: function (title, description, target, _a) { - var _b = _a === void 0 ? {} : _a, _c = _b.onCancel, onCancel = _c === void 0 ? "reject" : _c, _d = _b.defaultValue, defaultValue = _d === void 0 ? "" : _d, _e = _b.maxTextLength, maxTextLength = _e === void 0 ? 9999 : _e, _f = _b.positiveIntegersOnly, positiveIntegersOnly = _f === void 0 ? false : _f, _g = _b.allowEmpty, allowEmpty = _g === void 0 ? false : _g; - var _h = promise_1.Promise.withResolvers(), promise = _h.promise, reject = _h.reject, resolve = _h.resolve; - //The target fishPlayer has a property called activeMenu, which stores information about the last menu triggered. - //If menu() is being called from a menu calback, add it to the front of the queue so it is processed before any other menus. - //Otherwise, two multi-step menus queued together would alternate, which would confuse the player. - target.activeMenus[isInMenuCallback ? "unshift" : "push"]({ type: "text", callback: function (text) { - //Additional permission validation could be done here, but the only way that callback() can be called is if the above statement executed, - //and on sensitive menus such as the stop menu, the only way to reach that is if menu() was called by the /stop command, - //which already checks permissions. - //Additionally, the callback is cleared by the generic menu listener after it is executed. - try { - if (positiveIntegersOnly && text != null) { - var number = Number(text); - if (isNaN(number)) { - if (onCancel == "reject") - reject(exports.Cancel); - else - resolve(null); - } - else - resolve(number); - } - else if (text == null && onCancel == "reject") - reject(exports.Cancel); - else - resolve(text); - } - catch (err) { - (0, utils_1.handleError)(err, target, utils_1.outputFail, "".concat(target.cleanedName, " submitted menu \"").concat(title, "\" \"").concat(description, "\"")); - } - } }); - Call.textInput(target.con, registeredListeners.generic, title, description, maxTextLength, defaultValue, positiveIntegersOnly, allowEmpty); - return promise; - }, - /** Displays a menu to a player, returning a Promise. Arranges provided options into a 2D array, and can add a Cancel option. */ - menu: function (title, description, options, target, _a) { - var _b = _a === void 0 ? {} : _a, _c = _b.includeCancel, includeCancel = _c === void 0 ? false : _c, _d = _b.optionStringifier, optionStringifier = _d === void 0 ? String : _d, _e = _b.columns, columns = _e === void 0 ? 3 : _e, _f = _b.onCancel, onCancel = _f === void 0 ? "reject" : _f, _g = _b.cancelOptionId, cancelOptionId = _g === void 0 ? -1 : _g; - //Set up the 2D array of options, and maybe add cancel - //Call.menu() with [[]] will cause a client crash, make sure to pass [] instead - var arrangedOptions = (options.length == 0 && !includeCancel) ? [] : (0, funcs_1.to2DArray)(options, columns); - if (includeCancel) { - arrangedOptions.push([(typeof includeCancel == "string" ? includeCancel : "[red]Cancel[]")]); - //This is safe because cancelOptionId is set, - //so the handler will never get called with "Cancel". - cancelOptionId = options.length; - } - return exports.Menu.raw(title, description, arrangedOptions, target, { - cancelOptionId: cancelOptionId, - onCancel: onCancel, - optionStringifier: optionStringifier - }); - }, - /** Rejects with a CommandError if the user chooses to cancel. */ - confirm: function (target, description, _a) { - var _b = _a === void 0 ? {} : _a, _c = _b.cancelOutput, cancelOutput = _c === void 0 ? "Cancelled." : _c, _d = _b.title, title = _d === void 0 ? "Confirm" : _d, _e = _b.confirmText, confirmText = _e === void 0 ? "[green]Confirm" : _e, _f = _b.cancelText, cancelText = _f === void 0 ? "[red]Cancel" : _f; - return exports.Menu.menu(title, description, [confirmText, cancelText], target, { onCancel: "reject", cancelOptionId: 1 }).catch(function (e) { - if (e === exports.Cancel) - (0, commands_1.fail)(cancelOutput); - throw e; //some random error, rethrow it - }); - }, - /** Same as confirm(), but with inverted colors, for potentially dangerous actions. */ - confirmDangerous: function (target, description, _a) { - if (_a === void 0) { _a = {}; } - var _b = _a.confirmText, confirmText = _b === void 0 ? "[red]Confirm" : _b, _c = _a.cancelText, cancelText = _c === void 0 ? "[green]Cancel" : _c, rest = __rest(_a, ["confirmText", "cancelText"]); - return exports.Menu.confirm(target, description, __assign({ cancelText: cancelText, confirmText: confirmText }, rest)); - }, - /** - * Displays a menu to a player, returning a Promise. - * Accepts pre-generated data and text. Alternative to optionStringifier if the text is already generated. - */ - buttons: function (target, title, description, options, cfg) { - if (cfg === void 0) { cfg = {}; } - return exports.Menu.raw(title, description, options, target, __assign(__assign({}, cfg), { optionStringifier: function (o) { return o.text; } })).then(function (o) { return o === null || o === void 0 ? void 0 : o.data; }); - }, - /** - * Displays a menu to a player, returning a Promise. - * Adds left and right arrows to switch pages. - * Shows different options based on the page. - */ - pages: function (target, title, description, options, cfg) { - var _a = promise_1.Promise.withResolvers(), promise = _a.promise, reject = _a.reject, resolve = _a.resolve; - function showPage(index) { - var opts = __spreadArray(__spreadArray([], __read(options[index].map(function (r) { return r.map(function (d) { return ({ text: d.text, data: [d.data] }); }); })), false), [ - [ - { data: "left", text: "[".concat(index == 0 ? "gray" : "accent", "]<--") }, - { data: "numbers", text: "[accent]".concat(index + 1, "/").concat(options.length) }, - { data: "right", text: "[".concat(index == options.length - 1 ? "gray" : "accent", "]-->") } - ] - ], false); - void exports.Menu.buttons(target, title, description, opts, __assign(__assign({}, cfg), { onCancel: "null" })).then(function (response) { - if (response instanceof Array) - resolve(response[0]); - else if (response === "right") - showPage(Math.min(index + 1, options.length - 1)); - else if (response === "left") - showPage(Math.max(index - 1, 0)); - else { - //Treat numbers as cancel - if (cfg.onCancel == "null") - resolve(null); - else if (cfg.onCancel == "reject") - reject(exports.Cancel); - //otherwise, just let the promise hang - } - }); - } - showPage(0); - return promise; - }, - /** - * Displays a menu to a player, returning a Promise. - * Adds left and right arrows to switch pages. - * Shows different text based on the current page. - */ - textPages: function (target, pages, options, cfg) { - if (options === void 0) { options = []; } - if (cfg === void 0) { cfg = {}; } - var _a = promise_1.Promise.withResolvers(), promise = _a.promise, reject = _a.reject, resolve = _a.resolve; - var pageSkipSize = Math.max(Math.floor(pages.length / 8), 5); - function showPage(index) { - var opts = __spreadArray(__spreadArray([ - [ - { data: ["left", pageSkipSize], text: "[".concat(index == 0 ? "gray" : "accent", "]<<<") }, - { data: ["left", 1], text: "[".concat(index == 0 ? "gray" : "accent", "]<--") }, - { data: ["right", 1], text: "[".concat(index == pages.length - 1 ? "gray" : "accent", "]-->") }, - { data: ["right", pageSkipSize], text: "[".concat(index == pages.length - 1 ? "gray" : "accent", "]>>>") }, - ], [ - { data: ["numbers"], text: "[accent]Page ".concat(index + 1, "/").concat(pages.length) }, - ] - ], __read(options.map(function (d, i) { return [{ data: [i], text: d }]; })), false), [ - [ - { data: ["cancel"], text: "[lightgray]Close" }, - ], - ], false); - void exports.Menu.buttons(target, pages[index][0], pages[index][1](), opts, __assign(__assign({}, cfg), { onCancel: "null" })).then(function (response) { - if ((response === null || response === void 0 ? void 0 : response[0]) === "right") - showPage(Math.min(index + response[1], pages.length - 1)); - else if ((response === null || response === void 0 ? void 0 : response[0]) === "left") - showPage(Math.max(index - response[1], 0)); - else if (typeof (response === null || response === void 0 ? void 0 : response[0]) === "number") { - resolve([index, options[response[0]]]); - } - else { - //Treat numbers as cancel - if (cfg.onCancel == "null") - resolve(null); - else if (cfg.onCancel == "reject") - reject(exports.Cancel); - //otherwise, just let the promise hang - } - }); - } - var index = (function () { - if (cfg.startPage == undefined) - return 0; - if (typeof cfg.startPage === 'number') { - if (cfg.startPage < 0) - return 0; - return cfg.startPage; - } - var index = pages.findIndex(function (_a) { - var _b = __read(_a, 1), title = _b[0]; - return title === cfg.startPage; - }); - if (index === -1) - return 0; - return index; - })(); - showPage(index); - return promise; - }, - /** - * Displays a menu to a player, returning a Promise. - * Accepts a 2D array of options and shows a region of that 2D grid. - * Adds arrows to scroll left/right/up/down. - * Resolves to the selected option. - */ - scroll2D: function (target, title, description, options, cfg) { - var _a, _b; - if (cfg === void 0) { cfg = {}; } - var _c = promise_1.Promise.withResolvers(), promise = _c.promise, reject = _c.reject, resolve = _c.resolve; - var _d = cfg.rows, rows = _d === void 0 ? 5 : _d, _e = cfg.columns, cols = _e === void 0 ? 5 : _e; - var height = options.length; - var width = options[0].length; - function showPage(x, y) { - var _a, _b; - var opts = __spreadArray(__spreadArray([], __read(options.slice(y, y + rows).map(function (r) { return r.concat(Array(width - r.length).fill({ data: "blank", text: "" })); }).map(function (r) { - return r.slice(x, x + cols).map(function (d) { return ({ text: d.text, data: [d.data] }); }); - })), false), [ - [ - { data: "blank", text: "" }, - ], - [ - { data: "blank", text: "" }, - { data: "up", text: "[".concat(y == 0 ? "gray" : "accent", "]").concat(String.fromCharCode(Iconc.up)) }, - { data: "blank", text: "" }, - ], [ - { data: "left", text: "[".concat(x == 0 ? "gray" : "accent", "]").concat(String.fromCharCode(Iconc.left)) }, - { data: "blank", text: (_b = (_a = cfg.getCenterText) === null || _a === void 0 ? void 0 : _a.call(cfg, x, y)) !== null && _b !== void 0 ? _b : '' }, - { data: "right", text: "[".concat(x == width - cols ? "gray" : "accent", "]").concat(String.fromCharCode(Iconc.right)) }, - ], [ - { data: "blank", text: "" }, - { data: "down", text: "[".concat(y == height - rows ? "gray" : "accent", "]").concat(String.fromCharCode(Iconc.down)) }, - { data: "blank", text: "" }, - ] - ], false); - void exports.Menu.buttons(target, title, description, opts, cfg).then(function (response) { - if (response instanceof Array) - resolve([response[0], x, y]); - else if (response === "right") - showPage(Math.min(x + 1, width - cols), y); - else if (response === "left") - showPage(Math.max(x - 1, 0), y); - else if (response === "up") - showPage(x, Math.max(y - 1, 0)); - else if (response === "down") - showPage(x, Math.min(y + 1, height - rows)); - else { - //Treat numbers as cancel - if (cfg.onCancel == "null") - resolve(null); - else if (cfg.onCancel == "reject") - reject(exports.Cancel); - //otherwise, just let the promise hang - } - }); - } - showPage(Math.min((_a = cfg.x) !== null && _a !== void 0 ? _a : 0, width - cols), Math.min((_b = cfg.y) !== null && _b !== void 0 ? _b : 0, height - rows)); - return promise; - }, - /** - * Displays a menu to a player, returning a Promise. - * Adds left and right arrows to switch pages. Automatically paginates provided options. - * Accepts pre-generated data and text. Alternative to optionStringifier if the text is already generated. - */ - pagedListButtons: function (target, title, description, options, _a) { - var _b; - var _c = _a.rowsPerPage, rowsPerPage = _c === void 0 ? 10 : _c, _d = _a.columns, columns = _d === void 0 ? 3 : _d, cfg = __rest(_a, ["rowsPerPage", "columns"]); - //Generate pages - var pages = (0, funcs_1.to2DArray)((0, funcs_1.to2DArray)(options, columns), rowsPerPage); - if (pages.length <= 1) - return exports.Menu.buttons(target, title, description, (_b = pages[0]) !== null && _b !== void 0 ? _b : [], cfg); - return exports.Menu.pages(target, title, description, pages, cfg); - }, - /** - * Displays a menu to a player, returning a Promise. - * Adds left and right arrows to switch pages. Automatically paginates provided options. - */ - pagedList: function (target, title, description, options, _a) { - var _b; - if (_a === void 0) { _a = {}; } - var _c = _a.rowsPerPage, rowsPerPage = _c === void 0 ? 10 : _c, _d = _a.columns, columns = _d === void 0 ? 3 : _d, _e = _a.optionStringifier, optionStringifier = _e === void 0 ? String : _e, cfg = __rest(_a, ["rowsPerPage", "columns", "optionStringifier"]); - //Generate pages - var pages = (0, funcs_1.to2DArray)((0, funcs_1.to2DArray)(options.map(function (o) { return ({ data: o, get text() { return optionStringifier(o); } }); }), columns), rowsPerPage); - if (pages.length <= 1) - return exports.Menu.buttons(target, title, description, (_b = pages[0]) !== null && _b !== void 0 ? _b : [], cfg); - return exports.Menu.pages(target, title, description, pages, cfg); - } -}; +"use strict"; +/* eslint-disable @typescript-eslint/array-type */ +/* +Copyright © BalaM314, 2026. All Rights Reserved. +This file contains the menu framework. +For usage information, see docs/framework-usage-guide.md +For maintenance information, see docs/frameworks.md +*/ +var __assign = (this && this.__assign) || function () { + __assign = Object.assign || function(t) { + for (var s, i = 1, n = arguments.length; i < n; i++) { + s = arguments[i]; + for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p)) + t[p] = s[p]; + } + return t; + }; + return __assign.apply(this, arguments); +}; +var __rest = (this && this.__rest) || function (s, e) { + var t = {}; + for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0) + t[p] = s[p]; + if (s != null && typeof Object.getOwnPropertySymbols === "function") + for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) { + if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i])) + t[p[i]] = s[p[i]]; + } + return t; +}; +var __values = (this && this.__values) || function(o) { + var s = typeof Symbol === "function" && Symbol.iterator, m = s && o[s], i = 0; + if (m) return m.call(o); + if (o && typeof o.length === "number") return { + next: function () { + if (o && i >= o.length) o = void 0; + return { value: o && o[i++], done: !o }; + } + }; + throw new TypeError(s ? "Object is not iterable." : "Symbol.iterator is not defined."); +}; +var __read = (this && this.__read) || function (o, n) { + var m = typeof Symbol === "function" && o[Symbol.iterator]; + if (!m) return o; + var i = m.call(o), r, ar = [], e; + try { + while ((n === void 0 || n-- > 0) && !(r = i.next()).done) ar.push(r.value); + } + catch (error) { e = { error: error }; } + finally { + try { + if (r && !r.done && (m = i["return"])) m.call(i); + } + finally { if (e) throw e.error; } + } + return ar; +}; +var __spreadArray = (this && this.__spreadArray) || function (to, from, pack) { + if (pack || arguments.length === 2) for (var i = 0, l = from.length, ar; i < l; i++) { + if (ar || !(i in from)) { + if (!ar) ar = Array.prototype.slice.call(from, 0, i); + ar[i] = from[i]; + } + } + return to.concat(ar || Array.prototype.slice.call(from)); +}; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.textListeners = exports.listeners = exports.Menu = exports.Cancel = void 0; +exports.registerListeners = registerListeners; +var commands_1 = require("/frameworks/commands"); +var funcs_1 = require("/funcs"); +var players_1 = require("/players"); +var promise_1 = require("/promise"); +var utils_1 = require("/utils"); +/** Used to change the behavior of adding another menu when being run in a menu callback. */ +var isInMenuCallback = false; +/** Stores a mapping from name to the numeric id of a listener that has been registered. */ +var registeredListeners = {}; +exports.listeners = registeredListeners; +/** Stores all listeners in use by fish-commands. */ +var listeners = { + generic: function (player, option) { + var fishSender = players_1.FishPlayer.get(player); + var prevCallback = fishSender.activeMenus.shift(); + if (!prevCallback) + return; //No menu to process, do nothing + if (prevCallback.type != "menu") + return; + isInMenuCallback = true; + prevCallback.callback(option); + isInMenuCallback = false; + }, + none: function (player, option) { + //do nothing + } +}; +/** Stores a mapping from name to the numeric id of a listener that has been registered. */ +var registeredTextListeners = {}; +exports.textListeners = registeredTextListeners; +/** Stores all listeners in use by fish-commands. */ +var textListeners = { + generic: function (player, text) { + var fishSender = players_1.FishPlayer.get(player); + var prevCallback = fishSender.activeMenus.shift(); + if (!prevCallback) + return; //No menu to process, do nothing + if (prevCallback.type != "text") + return; + isInMenuCallback = true; + prevCallback.callback(text); + isInMenuCallback = false; + }, + none: function (player, option) { + //do nothing + } +}; +exports.Cancel = Symbol("Cancel"); +/** Registers all listeners, should be called on server load. */ +function registerListeners() { + var e_1, _a, e_2, _b; + var _c, _d; + try { + for (var _e = __values(Object.entries(listeners)), _f = _e.next(); !_f.done; _f = _e.next()) { + var _g = __read(_f.value, 2), key = _g[0], listener = _g[1]; + (_c = registeredListeners[key]) !== null && _c !== void 0 ? _c : (registeredListeners[key] = Menus.registerMenu(listener)); + } + } + catch (e_1_1) { e_1 = { error: e_1_1 }; } + finally { + try { + if (_f && !_f.done && (_a = _e.return)) _a.call(_e); + } + finally { if (e_1) throw e_1.error; } + } + try { + for (var _h = __values(Object.entries(textListeners)), _j = _h.next(); !_j.done; _j = _h.next()) { + var _k = __read(_j.value, 2), key = _k[0], listener = _k[1]; + (_d = registeredTextListeners[key]) !== null && _d !== void 0 ? _d : (registeredTextListeners[key] = Menus.registerTextInput(listener)); + } + } + catch (e_2_1) { e_2 = { error: e_2_1 }; } + finally { + try { + if (_j && !_j.done && (_b = _h.return)) _b.call(_h); + } + finally { if (e_2) throw e_2.error; } + } +} +exports.Menu = { + /** Displays a menu to a player, returning a Promise. */ + raw: function (title, description, arrangedOptions, target, _a) { + var _b = _a === void 0 ? {} : _a, _c = _b.optionStringifier, optionStringifier = _c === void 0 ? String : _c, _d = _b.onCancel, onCancel = _d === void 0 ? "reject" : _d, _e = _b.cancelOptionId, cancelOptionId = _e === void 0 ? -1 : _e; + var _f = promise_1.Promise.withResolvers(), promise = _f.promise, reject = _f.reject, resolve = _f.resolve; + //The target fishPlayer has a property called activeMenu, which stores information about the last menu triggered. + //If menu() is being called from a menu calback, add it to the front of the queue so it is processed before any other menus. + //Otherwise, two multi-step menus queued together would alternate, which would confuse the player. + target.activeMenus[isInMenuCallback ? "unshift" : "push"]({ type: "menu", callback: function (option) { + //Additional permission validation could be done here, but the only way that callback() can be called is if the above statement executed, + //and on sensitive menus such as the stop menu, the only way to reach that is if menu() was called by the /stop command, + //which already checks permissions. + //Additionally, the callback is cleared by the generic menu listener after it is executed. + try { + var options = arrangedOptions.flat(); + //We do need to validate option though, as it can be any number. + if (option === -1 || option === cancelOptionId || !(option in options)) { + //Consider any invalid option to be a cancellation + if (onCancel == "null") + resolve(null); + else if (onCancel == "reject") + reject(exports.Cancel); + else + return; + } + else { + resolve(options[option]); + } + } + catch (err) { + (0, utils_1.handleError)(err, target, utils_1.outputFail, "".concat(target.cleanedName, " submitted menu \"").concat(title, "\" \"").concat(description, "\"")); + } + } }); + var i = 0; + var stringifiedOptions = arrangedOptions.map(function (r) { return r.map(function (item) { + if (i === cancelOptionId) + return item; + i++; + return optionStringifier(item); + }); }); + Call.menu(target.con, registeredListeners.generic, title, description, stringifiedOptions); + return promise; + }, + /** Displays a text input menu to a player, returning a Promise. */ + text: function (title, description, target, _a) { + var _b = _a === void 0 ? {} : _a, _c = _b.onCancel, onCancel = _c === void 0 ? "reject" : _c, _d = _b.defaultValue, defaultValue = _d === void 0 ? "" : _d, _e = _b.maxTextLength, maxTextLength = _e === void 0 ? 9999 : _e, _f = _b.positiveIntegersOnly, positiveIntegersOnly = _f === void 0 ? false : _f, _g = _b.allowEmpty, allowEmpty = _g === void 0 ? false : _g; + var _h = promise_1.Promise.withResolvers(), promise = _h.promise, reject = _h.reject, resolve = _h.resolve; + //The target fishPlayer has a property called activeMenu, which stores information about the last menu triggered. + //If menu() is being called from a menu calback, add it to the front of the queue so it is processed before any other menus. + //Otherwise, two multi-step menus queued together would alternate, which would confuse the player. + target.activeMenus[isInMenuCallback ? "unshift" : "push"]({ type: "text", callback: function (text) { + //Additional permission validation could be done here, but the only way that callback() can be called is if the above statement executed, + //and on sensitive menus such as the stop menu, the only way to reach that is if menu() was called by the /stop command, + //which already checks permissions. + //Additionally, the callback is cleared by the generic menu listener after it is executed. + try { + if (positiveIntegersOnly && text != null) { + var number = Number(text); + if (isNaN(number)) { + if (onCancel == "reject") + reject(exports.Cancel); + else + resolve(null); + } + else + resolve(number); + } + else if (text == null && onCancel == "reject") + reject(exports.Cancel); + else + resolve(text); + } + catch (err) { + (0, utils_1.handleError)(err, target, utils_1.outputFail, "".concat(target.cleanedName, " submitted menu \"").concat(title, "\" \"").concat(description, "\"")); + } + } }); + Call.textInput(target.con, registeredListeners.generic, title, description, maxTextLength, defaultValue, positiveIntegersOnly, allowEmpty); + return promise; + }, + /** Displays a menu to a player, returning a Promise. Arranges provided options into a 2D array, and can add a Cancel option. */ + menu: function (title, description, options, target, _a) { + var _b = _a === void 0 ? {} : _a, _c = _b.includeCancel, includeCancel = _c === void 0 ? false : _c, _d = _b.optionStringifier, optionStringifier = _d === void 0 ? String : _d, _e = _b.columns, columns = _e === void 0 ? 3 : _e, _f = _b.onCancel, onCancel = _f === void 0 ? "reject" : _f, _g = _b.cancelOptionId, cancelOptionId = _g === void 0 ? -1 : _g; + //Set up the 2D array of options, and maybe add cancel + //Call.menu() with [[]] will cause a client crash, make sure to pass [] instead + var arrangedOptions = (options.length == 0 && !includeCancel) ? [] : (0, funcs_1.to2DArray)(options, columns); + if (includeCancel) { + arrangedOptions.push([(typeof includeCancel == "string" ? includeCancel : "[red]Cancel[]")]); + //This is safe because cancelOptionId is set, + //so the handler will never get called with "Cancel". + cancelOptionId = options.length; + } + return exports.Menu.raw(title, description, arrangedOptions, target, { + cancelOptionId: cancelOptionId, + onCancel: onCancel, + optionStringifier: optionStringifier + }); + }, + /** Rejects with a CommandError if the user chooses to cancel. */ + confirm: function (target, description, _a) { + var _b = _a === void 0 ? {} : _a, _c = _b.cancelOutput, cancelOutput = _c === void 0 ? "Cancelled." : _c, _d = _b.title, title = _d === void 0 ? "Confirm" : _d, _e = _b.confirmText, confirmText = _e === void 0 ? "[green]Confirm" : _e, _f = _b.cancelText, cancelText = _f === void 0 ? "[red]Cancel" : _f; + return exports.Menu.menu(title, description, [confirmText, cancelText], target, { onCancel: "reject", cancelOptionId: 1 }).catch(function (e) { + if (e === exports.Cancel) + (0, commands_1.fail)(cancelOutput); + throw e; //some random error, rethrow it + }); + }, + /** Same as confirm(), but with inverted colors, for potentially dangerous actions. */ + confirmDangerous: function (target, description, _a) { + if (_a === void 0) { _a = {}; } + var _b = _a.confirmText, confirmText = _b === void 0 ? "[red]Confirm" : _b, _c = _a.cancelText, cancelText = _c === void 0 ? "[green]Cancel" : _c, rest = __rest(_a, ["confirmText", "cancelText"]); + return exports.Menu.confirm(target, description, __assign({ cancelText: cancelText, confirmText: confirmText }, rest)); + }, + /** + * Displays a menu to a player, returning a Promise. + * Accepts pre-generated data and text. Alternative to optionStringifier if the text is already generated. + */ + buttons: function (target, title, description, options, cfg) { + if (cfg === void 0) { cfg = {}; } + return exports.Menu.raw(title, description, options, target, __assign(__assign({}, cfg), { optionStringifier: function (o) { return o.text; } })).then(function (o) { return o === null || o === void 0 ? void 0 : o.data; }); + }, + /** + * Displays a menu to a player, returning a Promise. + * Adds left and right arrows to switch pages. + * Shows different options based on the page. + */ + pages: function (target, title, description, options, cfg) { + var _a = promise_1.Promise.withResolvers(), promise = _a.promise, reject = _a.reject, resolve = _a.resolve; + function showPage(index) { + var opts = __spreadArray(__spreadArray([], __read(options[index].map(function (r) { return r.map(function (d) { return ({ text: d.text, data: [d.data] }); }); })), false), [ + [ + { data: "left", text: "[".concat(index == 0 ? "gray" : "accent", "]<--") }, + { data: "numbers", text: "[accent]".concat(index + 1, "/").concat(options.length) }, + { data: "right", text: "[".concat(index == options.length - 1 ? "gray" : "accent", "]-->") } + ] + ], false); + void exports.Menu.buttons(target, title, description, opts, __assign(__assign({}, cfg), { onCancel: "null" })).then(function (response) { + if (response instanceof Array) + resolve(response[0]); + else if (response === "right") + showPage(Math.min(index + 1, options.length - 1)); + else if (response === "left") + showPage(Math.max(index - 1, 0)); + else { + //Treat numbers as cancel + if (cfg.onCancel == "null") + resolve(null); + else if (cfg.onCancel == "reject") + reject(exports.Cancel); + //otherwise, just let the promise hang + } + }); + } + showPage(0); + return promise; + }, + /** + * Displays a menu to a player, returning a Promise. + * Adds left and right arrows to switch pages. + * Shows different text based on the current page. + */ + textPages: function (target, pages, options, cfg) { + if (options === void 0) { options = []; } + if (cfg === void 0) { cfg = {}; } + var _a = promise_1.Promise.withResolvers(), promise = _a.promise, reject = _a.reject, resolve = _a.resolve; + var pageSkipSize = Math.max(Math.floor(pages.length / 8), 5); + function showPage(index) { + var opts = __spreadArray(__spreadArray([ + [ + { data: ["left", pageSkipSize], text: "[".concat(index == 0 ? "gray" : "accent", "]<<<") }, + { data: ["left", 1], text: "[".concat(index == 0 ? "gray" : "accent", "]<--") }, + { data: ["right", 1], text: "[".concat(index == pages.length - 1 ? "gray" : "accent", "]-->") }, + { data: ["right", pageSkipSize], text: "[".concat(index == pages.length - 1 ? "gray" : "accent", "]>>>") }, + ], [ + { data: ["numbers"], text: "[accent]Page ".concat(index + 1, "/").concat(pages.length) }, + ] + ], __read(options.map(function (d, i) { return [{ data: [i], text: d }]; })), false), [ + [ + { data: ["cancel"], text: "[lightgray]Close" }, + ], + ], false); + void exports.Menu.buttons(target, pages[index][0], pages[index][1](), opts, __assign(__assign({}, cfg), { onCancel: "null" })).then(function (response) { + if ((response === null || response === void 0 ? void 0 : response[0]) === "right") + showPage(Math.min(index + response[1], pages.length - 1)); + else if ((response === null || response === void 0 ? void 0 : response[0]) === "left") + showPage(Math.max(index - response[1], 0)); + else if (typeof (response === null || response === void 0 ? void 0 : response[0]) === "number") { + resolve([index, options[response[0]]]); + } + else { + //Treat numbers as cancel + if (cfg.onCancel == "null") + resolve(null); + else if (cfg.onCancel == "reject") + reject(exports.Cancel); + //otherwise, just let the promise hang + } + }); + } + var index = (function () { + if (cfg.startPage == undefined) + return 0; + if (typeof cfg.startPage === 'number') { + if (cfg.startPage < 0) + return 0; + return cfg.startPage; + } + var index = pages.findIndex(function (_a) { + var _b = __read(_a, 1), title = _b[0]; + return title === cfg.startPage; + }); + if (index === -1) + return 0; + return index; + })(); + showPage(index); + return promise; + }, + /** + * Displays a menu to a player, returning a Promise. + * Accepts a 2D array of options and shows a region of that 2D grid. + * Adds arrows to scroll left/right/up/down. + * Resolves to the selected option. + */ + scroll2D: function (target, title, description, options, cfg) { + var _a, _b; + if (cfg === void 0) { cfg = {}; } + var _c = promise_1.Promise.withResolvers(), promise = _c.promise, reject = _c.reject, resolve = _c.resolve; + var _d = cfg.rows, rows = _d === void 0 ? 5 : _d, _e = cfg.columns, cols = _e === void 0 ? 5 : _e; + var height = options.length; + var width = options[0].length; + function showPage(x, y) { + var _a, _b; + var opts = __spreadArray(__spreadArray([], __read(options.slice(y, y + rows).map(function (r) { return r.concat(Array(width - r.length).fill({ data: "blank", text: "" })); }).map(function (r) { + return r.slice(x, x + cols).map(function (d) { return ({ text: d.text, data: [d.data] }); }); + })), false), [ + [ + { data: "blank", text: "" }, + ], + [ + { data: "blank", text: "" }, + { data: "up", text: "[".concat(y == 0 ? "gray" : "accent", "]").concat(String.fromCharCode(Iconc.up)) }, + { data: "blank", text: "" }, + ], [ + { data: "left", text: "[".concat(x == 0 ? "gray" : "accent", "]").concat(String.fromCharCode(Iconc.left)) }, + { data: "blank", text: (_b = (_a = cfg.getCenterText) === null || _a === void 0 ? void 0 : _a.call(cfg, x, y)) !== null && _b !== void 0 ? _b : '' }, + { data: "right", text: "[".concat(x == width - cols ? "gray" : "accent", "]").concat(String.fromCharCode(Iconc.right)) }, + ], [ + { data: "blank", text: "" }, + { data: "down", text: "[".concat(y == height - rows ? "gray" : "accent", "]").concat(String.fromCharCode(Iconc.down)) }, + { data: "blank", text: "" }, + ] + ], false); + void exports.Menu.buttons(target, title, description, opts, cfg).then(function (response) { + if (response instanceof Array) + resolve([response[0], x, y]); + else if (response === "right") + showPage(Math.min(x + 1, width - cols), y); + else if (response === "left") + showPage(Math.max(x - 1, 0), y); + else if (response === "up") + showPage(x, Math.max(y - 1, 0)); + else if (response === "down") + showPage(x, Math.min(y + 1, height - rows)); + else { + //Treat numbers as cancel + if (cfg.onCancel == "null") + resolve(null); + else if (cfg.onCancel == "reject") + reject(exports.Cancel); + //otherwise, just let the promise hang + } + }); + } + showPage(Math.min((_a = cfg.x) !== null && _a !== void 0 ? _a : 0, width - cols), Math.min((_b = cfg.y) !== null && _b !== void 0 ? _b : 0, height - rows)); + return promise; + }, + /** + * Displays a menu to a player, returning a Promise. + * Adds left and right arrows to switch pages. Automatically paginates provided options. + * Accepts pre-generated data and text. Alternative to optionStringifier if the text is already generated. + */ + pagedListButtons: function (target, title, description, options, _a) { + var _b; + var _c = _a.rowsPerPage, rowsPerPage = _c === void 0 ? 10 : _c, _d = _a.columns, columns = _d === void 0 ? 3 : _d, cfg = __rest(_a, ["rowsPerPage", "columns"]); + //Generate pages + var pages = (0, funcs_1.to2DArray)((0, funcs_1.to2DArray)(options, columns), rowsPerPage); + if (pages.length <= 1) + return exports.Menu.buttons(target, title, description, (_b = pages[0]) !== null && _b !== void 0 ? _b : [], cfg); + return exports.Menu.pages(target, title, description, pages, cfg); + }, + /** + * Displays a menu to a player, returning a Promise. + * Adds left and right arrows to switch pages. Automatically paginates provided options. + */ + pagedList: function (target, title, description, options, _a) { + var _b; + if (_a === void 0) { _a = {}; } + var _c = _a.rowsPerPage, rowsPerPage = _c === void 0 ? 10 : _c, _d = _a.columns, columns = _d === void 0 ? 3 : _d, _e = _a.optionStringifier, optionStringifier = _e === void 0 ? String : _e, cfg = __rest(_a, ["rowsPerPage", "columns", "optionStringifier"]); + //Generate pages + var pages = (0, funcs_1.to2DArray)((0, funcs_1.to2DArray)(options.map(function (o) { return ({ data: o, get text() { return optionStringifier(o); } }); }), columns), rowsPerPage); + if (pages.length <= 1) + return exports.Menu.buttons(target, title, description, (_b = pages[0]) !== null && _b !== void 0 ? _b : [], cfg); + return exports.Menu.pages(target, title, description, pages, cfg); + } +}; diff --git a/build/scripts/funcs.js b/build/scripts/funcs.js index ee7f6bac..4a811ba2 100644 --- a/build/scripts/funcs.js +++ b/build/scripts/funcs.js @@ -1,474 +1,474 @@ -"use strict"; -var __read = (this && this.__read) || function (o, n) { - var m = typeof Symbol === "function" && o[Symbol.iterator]; - if (!m) return o; - var i = m.call(o), r, ar = [], e; - try { - while ((n === void 0 || n-- > 0) && !(r = i.next()).done) ar.push(r.value); - } - catch (error) { e = { error: error }; } - finally { - try { - if (r && !r.done && (m = i["return"])) m.call(i); - } - finally { if (e) throw e.error; } - } - return ar; -}; -var __spreadArray = (this && this.__spreadArray) || function (to, from, pack) { - if (pack || arguments.length === 2) for (var i = 0, l = from.length, ar; i < l; i++) { - if (ar || !(i in from)) { - if (!ar) ar = Array.prototype.slice.call(from, 0, i); - ar[i] = from[i]; - } - } - return to.concat(ar || Array.prototype.slice.call(from)); -}; -var __values = (this && this.__values) || function(o) { - var s = typeof Symbol === "function" && Symbol.iterator, m = s && o[s], i = 0; - if (m) return m.call(o); - if (o && typeof o.length === "number") return { - next: function () { - if (o && i >= o.length) o = void 0; - return { value: o && o[i++], done: !o }; - } - }; - throw new TypeError(s ? "Object is not iterable." : "Symbol.iterator is not defined."); -}; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.DurationSecs = exports.Duration = exports.EventEmitter = exports.StringIO = exports.StringBuilder = void 0; -exports.memoize = memoize; -exports.to2DArray = to2DArray; -exports.setToArray = setToArray; -exports.crash = crash; -exports.capitalizeText = capitalizeText; -exports.indefiniteArticle = indefiniteArticle; -exports.escapeTextDiscord = escapeTextDiscord; -exports.escapeStringColorsClient = escapeStringColorsClient; -exports.escapeStringColorsServer = escapeStringColorsServer; -exports.parseError = parseError; -exports.tagProcessor = tagProcessor; -exports.tagProcessorPartial = tagProcessorPartial; -exports.random = random; -exports.getIPAddress = getIPAddress; -exports.lazy = lazy; -exports.invalidtoNull = invalidtoNull; -exports.cleanColors = cleanColors; -exports.computeStatistics = computeStatistics; -exports.search = search; -exports.searchFixed = searchFixed; -exports.delay = delay; -var storedValues = {}; -/** - * Stores the output of a function and returns that value - * instead of running the function again unless any - * dependencies have changed to improve performance with - * functions that have expensive computation. - * @param callback function to run if a dependancy has changed - * @param dep dependency array of values to monitor - * @param id arbitrary unique id of the function for storage purposes. - */ -function memoize(callback, dep, id) { - if (!storedValues[id]) { - storedValues[id] = { value: callback(), dep: dep }; - } - else if (dep.some(function (d, ind) { return d !== storedValues[id].dep[ind]; })) { - //If the value changed - storedValues[id].value = callback(); - storedValues[id].dep = dep; - } - return storedValues[id].value; -} /** - * Converts a 1D array into a 2D array. - * @param width the max length of each row. - * The last row may not be full. - */ -function to2DArray(array, width) { - if (array.length == 0) - return []; - var output = [[]]; - array.forEach(function (el) { - if (output.at(-1).length >= width) { - output.push([]); - } - output.at(-1).push(el); - }); - return output; -} -function setToArray(set) { - var array = []; - set.each(function (item) { return array.push(item); }); - return array; -} -var StringBuilder = /** @class */ (function () { - function StringBuilder(str) { - if (str === void 0) { str = ""; } - this.str = str; - } - StringBuilder.prototype.add = function (str) { - this.str += str; - return this; - }; - StringBuilder.prototype.chunk = function (str) { - if (Strings.stripColors(str).length > 0) { - this.str = this.str + " " + str; - } - return this; - }; - return StringBuilder; -}()); -exports.StringBuilder = StringBuilder; -/** - * Used for serialization to strings. - * @deprecated use Serializer instead, which serializes to byte[] - */ -var StringIO = /** @class */ (function () { - function StringIO(string) { - if (string === void 0) { string = ""; } - this.string = string; - this.offset = 0; - } - StringIO.prototype.read = function (length) { - if (length === void 0) { length = 1; } - if (this.offset + length > this.string.length) - crash("Unexpected EOF"); - return this.string.slice(this.offset, this.offset += length); - }; - StringIO.prototype.write = function (str) { - this.string += str; - }; - StringIO.prototype.readString = function (/** The length of the written length. */ lenlen) { - if (lenlen === void 0) { lenlen = 3; } - var length = parseInt(this.read(lenlen)); - if (length == 0) - return null; - return this.read(length); - }; - StringIO.prototype.writeString = function (str, lenlen, truncate) { - if (lenlen === void 0) { lenlen = 3; } - if (truncate === void 0) { truncate = false; } - if (str === null) { - this.string += "0".repeat(lenlen); - } - else if (typeof str !== "string") { - crash("Attempted to serialize string ".concat(String(str), ", but it was not a string")); - } - else if (str.length > (Math.pow(10, lenlen) - 1)) { - if (truncate) { - Log.err("Cannot write strings with length greater than ".concat((Math.pow(10, lenlen) - 1), " (was ").concat(str.length, "), truncating")); - this.string += (Math.pow(10, lenlen) - 1).toString().padStart(lenlen, "0"); - this.string += str.slice(0, (Math.pow(10, lenlen) - 1)); - } - else { - crash("Cannot write strings with length greater than ".concat((Math.pow(10, lenlen) - 1), " (was ").concat(str.length, ")\n String was: \"").concat(str, "\"")); - } - } - else { - this.string += str.length.toString().padStart(lenlen, "0"); - this.string += str; - } - }; - StringIO.prototype.readEnumString = function (options) { - var length = (options.length - 1).toString().length; - var option = this.readNumber(length); - return options[option]; - }; - StringIO.prototype.writeEnumString = function (value, options) { - var length = (options.length - 1).toString().length; - var option = options.indexOf(value); - if (option == -1) - crash("Attempted to write invalid value \"".concat(String(value), "\" for enum, valid values are (").concat(options.join(", "), ")")); - this.writeNumber(option, length); - }; - StringIO.prototype.readNumber = function (size) { - if (size === void 0) { size = 4; } - var data = this.read(size); - if (/^0*-\d+$/.test(data)) { - //negative numbers were incorrectly stored in previous versions - data = "-" + data.split("-")[1]; - } - if (isNaN(Number(data))) - crash("Attempted to read invalid number: ".concat(data)); - return Number(data); - }; - StringIO.prototype.writeNumber = function (num, size, clamp) { - if (size === void 0) { size = 4; } - if (clamp === void 0) { clamp = false; } - if (typeof num != "number") - crash("".concat(String(num), " was not a number!")); - if (num.toString().length > size) { - if (clamp) { - if (num > (Math.pow(10, size)) - 1) - this.string += (Math.pow(10, size)) - 1; - else - this.string += num.toString().slice(0, size); - } - else - crash("Cannot write number ".concat(num, " with length ").concat(size, ": too long")); - } - this.string += num.toString().padStart(size, "0"); - }; - StringIO.prototype.readBool = function () { - return this.read(1) == "T" ? true : false; - }; - StringIO.prototype.writeBool = function (val) { - this.write(val ? "T" : "F"); - }; - StringIO.prototype.writeArray = function (array, func, lenlen) { - var _this = this; - this.writeNumber(array.length, lenlen); - array.forEach(function (e) { return func(e, _this); }); - }; - StringIO.prototype.readArray = function (func, lenlen) { - var length = this.readNumber(lenlen); - var array = []; - for (var i = 0; i < length; i++) { - array[i] = func(this); - } - return array; - }; - StringIO.prototype.expectEOF = function () { - if (this.string.length > this.offset) - crash("Expected EOF, but found extra data: \"".concat(this.string.slice(this.offset), "\"")); - }; - StringIO.read = function (data, func) { - var str = new StringIO(data); - try { - return func(str); - } - catch (err) { - Log.err("Error while reading compressed data!"); - Log.err(data); - throw err; - } - }; - StringIO.write = function (data, func) { - var str = new StringIO(); - func(str, data); - return str.string; - }; - return StringIO; -}()); -exports.StringIO = StringIO; -/** Something that emits events. */ -var EventEmitter = /** @class */ (function () { - function EventEmitter() { - this.listeners = {}; - } - EventEmitter.prototype.on = function (event, callback) { - var _a; - var _b; - ((_a = (_b = this.listeners)[event]) !== null && _a !== void 0 ? _a : (_b[event] = [])).push(callback); - return this; - }; - EventEmitter.prototype.fire = function (event, args) { - var _a; - var listeners = (_a = this.listeners[event]) !== null && _a !== void 0 ? _a : []; - // eslint-disable-next-line @typescript-eslint/prefer-for-of - for (var i = 0; i < listeners.length; i++) { - listeners[i].apply(listeners, __spreadArray([this], __read(args), false)); - } - }; - return EventEmitter; -}()); -exports.EventEmitter = EventEmitter; -function crash(message) { - throw new Error(message); -} -/** Best effort title-capitalization of a word. */ -function capitalizeText(text, separator) { - if (separator === void 0) { separator = " "; } - return text - .split(separator) - .map(function (word, i, arr) { return (["a", "an", "the", "in", "and", "of", "it", "is"].includes(word) && - i !== 0 && i !== arr.length - 1) ? word - : word[0].toUpperCase() + word.substring(1).toLowerCase(); }).join(" "); -} -/** Best effort prepends an indefinite article (either "a" or "an") to provided text. */ -function indefiniteArticle(text) { - var cText = Strings.stripColors(text); - if (/^[aeiou]/.test(cText) || cText == "hour") - return "an " + text; - else - return "a " + text; -} -var pattern = Pattern.compile("([*\\_~`|:])"); -function escapeTextDiscord(text) { - return pattern.matcher(text).replaceAll("\\\\$1\u200B"); -} -/** Prevents Mindustry from displaying color tags in a string by escaping them. Example: turns [scarlet]red to [[scarlet]red. */ -function escapeStringColorsClient(str) { - return str.replace(/\[/g, "[["); -} -// export function highlightStringColorsClient(str:string):string { -// return str.replace(/(? 1) - return { value: result }; - }; - try { - for (var filters_1 = __values(filters), filters_1_1 = filters_1.next(); !filters_1_1.done; filters_1_1 = filters_1.next()) { - var filter = filters_1_1.value; - var state_1 = _loop_1(filter); - if (typeof state_1 === "object") - return state_1.value; - } - } - catch (e_1_1) { e_1 = { error: e_1_1 }; } - finally { - try { - if (filters_1_1 && !filters_1_1.done && (_a = filters_1.return)) _a.call(filters_1); - } - finally { if (e_1) throw e_1.error; } - } - return null; - }; -} -function searchFixed(options, filters, recomputeOptions) { - var func = search.apply(void 0, __spreadArray([], __read(filters), false)); - var _options = options; - return function (query) { - if (typeof _options == "function") { - if (recomputeOptions) - return func(_options(), query); - else - return func(_options = _options(), query); - } - else - return func(_options, query); - }; -} -function delay(millis) { - return new Promise(function (res) { return Timer.schedule(res, millis / 1000); }); -} -exports.Duration = { - seconds: function (x) { return x * 1000; }, - minutes: function (x) { return x * 60000; }, - hours: function (x) { return x * 3600000; }, - days: function (x) { return x * 86400000; }, - months: function (x) { return x * 2592000000; }, -}; -exports.DurationSecs = { - minutes: function (x) { return x * 60; }, - hours: function (x) { return x * 3600; }, - days: function (x) { return x * 86400; }, - months: function (x) { return x * 2592000; }, -}; +"use strict"; +var __read = (this && this.__read) || function (o, n) { + var m = typeof Symbol === "function" && o[Symbol.iterator]; + if (!m) return o; + var i = m.call(o), r, ar = [], e; + try { + while ((n === void 0 || n-- > 0) && !(r = i.next()).done) ar.push(r.value); + } + catch (error) { e = { error: error }; } + finally { + try { + if (r && !r.done && (m = i["return"])) m.call(i); + } + finally { if (e) throw e.error; } + } + return ar; +}; +var __spreadArray = (this && this.__spreadArray) || function (to, from, pack) { + if (pack || arguments.length === 2) for (var i = 0, l = from.length, ar; i < l; i++) { + if (ar || !(i in from)) { + if (!ar) ar = Array.prototype.slice.call(from, 0, i); + ar[i] = from[i]; + } + } + return to.concat(ar || Array.prototype.slice.call(from)); +}; +var __values = (this && this.__values) || function(o) { + var s = typeof Symbol === "function" && Symbol.iterator, m = s && o[s], i = 0; + if (m) return m.call(o); + if (o && typeof o.length === "number") return { + next: function () { + if (o && i >= o.length) o = void 0; + return { value: o && o[i++], done: !o }; + } + }; + throw new TypeError(s ? "Object is not iterable." : "Symbol.iterator is not defined."); +}; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.DurationSecs = exports.Duration = exports.EventEmitter = exports.StringIO = exports.StringBuilder = void 0; +exports.memoize = memoize; +exports.to2DArray = to2DArray; +exports.setToArray = setToArray; +exports.crash = crash; +exports.capitalizeText = capitalizeText; +exports.indefiniteArticle = indefiniteArticle; +exports.escapeTextDiscord = escapeTextDiscord; +exports.escapeStringColorsClient = escapeStringColorsClient; +exports.escapeStringColorsServer = escapeStringColorsServer; +exports.parseError = parseError; +exports.tagProcessor = tagProcessor; +exports.tagProcessorPartial = tagProcessorPartial; +exports.random = random; +exports.getIPAddress = getIPAddress; +exports.lazy = lazy; +exports.invalidtoNull = invalidtoNull; +exports.cleanColors = cleanColors; +exports.computeStatistics = computeStatistics; +exports.search = search; +exports.searchFixed = searchFixed; +exports.delay = delay; +var storedValues = {}; +/** + * Stores the output of a function and returns that value + * instead of running the function again unless any + * dependencies have changed to improve performance with + * functions that have expensive computation. + * @param callback function to run if a dependancy has changed + * @param dep dependency array of values to monitor + * @param id arbitrary unique id of the function for storage purposes. + */ +function memoize(callback, dep, id) { + if (!storedValues[id]) { + storedValues[id] = { value: callback(), dep: dep }; + } + else if (dep.some(function (d, ind) { return d !== storedValues[id].dep[ind]; })) { + //If the value changed + storedValues[id].value = callback(); + storedValues[id].dep = dep; + } + return storedValues[id].value; +} /** + * Converts a 1D array into a 2D array. + * @param width the max length of each row. + * The last row may not be full. + */ +function to2DArray(array, width) { + if (array.length == 0) + return []; + var output = [[]]; + array.forEach(function (el) { + if (output.at(-1).length >= width) { + output.push([]); + } + output.at(-1).push(el); + }); + return output; +} +function setToArray(set) { + var array = []; + set.each(function (item) { return array.push(item); }); + return array; +} +var StringBuilder = /** @class */ (function () { + function StringBuilder(str) { + if (str === void 0) { str = ""; } + this.str = str; + } + StringBuilder.prototype.add = function (str) { + this.str += str; + return this; + }; + StringBuilder.prototype.chunk = function (str) { + if (Strings.stripColors(str).length > 0) { + this.str = this.str + " " + str; + } + return this; + }; + return StringBuilder; +}()); +exports.StringBuilder = StringBuilder; +/** + * Used for serialization to strings. + * @deprecated use Serializer instead, which serializes to byte[] + */ +var StringIO = /** @class */ (function () { + function StringIO(string) { + if (string === void 0) { string = ""; } + this.string = string; + this.offset = 0; + } + StringIO.prototype.read = function (length) { + if (length === void 0) { length = 1; } + if (this.offset + length > this.string.length) + crash("Unexpected EOF"); + return this.string.slice(this.offset, this.offset += length); + }; + StringIO.prototype.write = function (str) { + this.string += str; + }; + StringIO.prototype.readString = function (/** The length of the written length. */ lenlen) { + if (lenlen === void 0) { lenlen = 3; } + var length = parseInt(this.read(lenlen)); + if (length == 0) + return null; + return this.read(length); + }; + StringIO.prototype.writeString = function (str, lenlen, truncate) { + if (lenlen === void 0) { lenlen = 3; } + if (truncate === void 0) { truncate = false; } + if (str === null) { + this.string += "0".repeat(lenlen); + } + else if (typeof str !== "string") { + crash("Attempted to serialize string ".concat(String(str), ", but it was not a string")); + } + else if (str.length > (Math.pow(10, lenlen) - 1)) { + if (truncate) { + Log.err("Cannot write strings with length greater than ".concat((Math.pow(10, lenlen) - 1), " (was ").concat(str.length, "), truncating")); + this.string += (Math.pow(10, lenlen) - 1).toString().padStart(lenlen, "0"); + this.string += str.slice(0, (Math.pow(10, lenlen) - 1)); + } + else { + crash("Cannot write strings with length greater than ".concat((Math.pow(10, lenlen) - 1), " (was ").concat(str.length, ")\n String was: \"").concat(str, "\"")); + } + } + else { + this.string += str.length.toString().padStart(lenlen, "0"); + this.string += str; + } + }; + StringIO.prototype.readEnumString = function (options) { + var length = (options.length - 1).toString().length; + var option = this.readNumber(length); + return options[option]; + }; + StringIO.prototype.writeEnumString = function (value, options) { + var length = (options.length - 1).toString().length; + var option = options.indexOf(value); + if (option == -1) + crash("Attempted to write invalid value \"".concat(String(value), "\" for enum, valid values are (").concat(options.join(", "), ")")); + this.writeNumber(option, length); + }; + StringIO.prototype.readNumber = function (size) { + if (size === void 0) { size = 4; } + var data = this.read(size); + if (/^0*-\d+$/.test(data)) { + //negative numbers were incorrectly stored in previous versions + data = "-" + data.split("-")[1]; + } + if (isNaN(Number(data))) + crash("Attempted to read invalid number: ".concat(data)); + return Number(data); + }; + StringIO.prototype.writeNumber = function (num, size, clamp) { + if (size === void 0) { size = 4; } + if (clamp === void 0) { clamp = false; } + if (typeof num != "number") + crash("".concat(String(num), " was not a number!")); + if (num.toString().length > size) { + if (clamp) { + if (num > (Math.pow(10, size)) - 1) + this.string += (Math.pow(10, size)) - 1; + else + this.string += num.toString().slice(0, size); + } + else + crash("Cannot write number ".concat(num, " with length ").concat(size, ": too long")); + } + this.string += num.toString().padStart(size, "0"); + }; + StringIO.prototype.readBool = function () { + return this.read(1) == "T" ? true : false; + }; + StringIO.prototype.writeBool = function (val) { + this.write(val ? "T" : "F"); + }; + StringIO.prototype.writeArray = function (array, func, lenlen) { + var _this = this; + this.writeNumber(array.length, lenlen); + array.forEach(function (e) { return func(e, _this); }); + }; + StringIO.prototype.readArray = function (func, lenlen) { + var length = this.readNumber(lenlen); + var array = []; + for (var i = 0; i < length; i++) { + array[i] = func(this); + } + return array; + }; + StringIO.prototype.expectEOF = function () { + if (this.string.length > this.offset) + crash("Expected EOF, but found extra data: \"".concat(this.string.slice(this.offset), "\"")); + }; + StringIO.read = function (data, func) { + var str = new StringIO(data); + try { + return func(str); + } + catch (err) { + Log.err("Error while reading compressed data!"); + Log.err(data); + throw err; + } + }; + StringIO.write = function (data, func) { + var str = new StringIO(); + func(str, data); + return str.string; + }; + return StringIO; +}()); +exports.StringIO = StringIO; +/** Something that emits events. */ +var EventEmitter = /** @class */ (function () { + function EventEmitter() { + this.listeners = {}; + } + EventEmitter.prototype.on = function (event, callback) { + var _a; + var _b; + ((_a = (_b = this.listeners)[event]) !== null && _a !== void 0 ? _a : (_b[event] = [])).push(callback); + return this; + }; + EventEmitter.prototype.fire = function (event, args) { + var _a; + var listeners = (_a = this.listeners[event]) !== null && _a !== void 0 ? _a : []; + // eslint-disable-next-line @typescript-eslint/prefer-for-of + for (var i = 0; i < listeners.length; i++) { + listeners[i].apply(listeners, __spreadArray([this], __read(args), false)); + } + }; + return EventEmitter; +}()); +exports.EventEmitter = EventEmitter; +function crash(message) { + throw new Error(message); +} +/** Best effort title-capitalization of a word. */ +function capitalizeText(text, separator) { + if (separator === void 0) { separator = " "; } + return text + .split(separator) + .map(function (word, i, arr) { return (["a", "an", "the", "in", "and", "of", "it", "is"].includes(word) && + i !== 0 && i !== arr.length - 1) ? word + : word[0].toUpperCase() + word.substring(1).toLowerCase(); }).join(" "); +} +/** Best effort prepends an indefinite article (either "a" or "an") to provided text. */ +function indefiniteArticle(text) { + var cText = Strings.stripColors(text); + if (/^[aeiou]/.test(cText) || cText == "hour") + return "an " + text; + else + return "a " + text; +} +var pattern = Pattern.compile("([*\\_~`|:])"); +function escapeTextDiscord(text) { + return pattern.matcher(text).replaceAll("\\\\$1\u200B"); +} +/** Prevents Mindustry from displaying color tags in a string by escaping them. Example: turns [scarlet]red to [[scarlet]red. */ +function escapeStringColorsClient(str) { + return str.replace(/\[/g, "[["); +} +// export function highlightStringColorsClient(str:string):string { +// return str.replace(/(? 1) + return { value: result }; + }; + try { + for (var filters_1 = __values(filters), filters_1_1 = filters_1.next(); !filters_1_1.done; filters_1_1 = filters_1.next()) { + var filter = filters_1_1.value; + var state_1 = _loop_1(filter); + if (typeof state_1 === "object") + return state_1.value; + } + } + catch (e_1_1) { e_1 = { error: e_1_1 }; } + finally { + try { + if (filters_1_1 && !filters_1_1.done && (_a = filters_1.return)) _a.call(filters_1); + } + finally { if (e_1) throw e_1.error; } + } + return null; + }; +} +function searchFixed(options, filters, recomputeOptions) { + var func = search.apply(void 0, __spreadArray([], __read(filters), false)); + var _options = options; + return function (query) { + if (typeof _options == "function") { + if (recomputeOptions) + return func(_options(), query); + else + return func(_options = _options(), query); + } + else + return func(_options, query); + }; +} +function delay(millis) { + return new Promise(function (res) { return Timer.schedule(res, millis / 1000); }); +} +exports.Duration = { + seconds: function (x) { return x * 1000; }, + minutes: function (x) { return x * 60000; }, + hours: function (x) { return x * 3600000; }, + days: function (x) { return x * 86400000; }, + months: function (x) { return x * 2592000000; }, +}; +exports.DurationSecs = { + minutes: function (x) { return x * 60; }, + hours: function (x) { return x * 3600; }, + days: function (x) { return x * 86400; }, + months: function (x) { return x * 2592000; }, +}; diff --git a/build/scripts/globals.js b/build/scripts/globals.js index 10e7e366..d606d314 100644 --- a/build/scripts/globals.js +++ b/build/scripts/globals.js @@ -1,34 +1,34 @@ -"use strict"; -/* -Copyright © BalaM314, 2026. All Rights Reserved. -This file contains mutable global variables, and global constants. -*/ -Object.defineProperty(exports, "__esModule", { value: true }); -exports.FishEvents = exports.unitsT5 = exports.maxTime = exports.ipRangeWildcardPattern = exports.ipRangeCIDRPattern = exports.ipPortPattern = exports.ipPattern = exports.uuidPattern = exports.ipJoins = exports.fishPlugin = exports.fishState = exports.recentWhispers = exports.tileHistory = void 0; -var funcs_1 = require("/funcs"); -exports.tileHistory = {}; -exports.recentWhispers = {}; -exports.fishState = { - restartQueued: false, - restartLoopTask: null, - corruption_t1: null, - corruption_t2: null, - lastPranked: Date.now(), - labels: [], - peacefulMode: false, - joinBell: false, - startTime: Date.now(), -}; -exports.fishPlugin = { - directory: null, - version: null, -}; -exports.ipJoins = new ObjectIntMap(); -exports.uuidPattern = /^[a-zA-Z0-9+/]{22}==$/; -exports.ipPattern = /^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}$/; -exports.ipPortPattern = /^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}:\d{1,5}$/; -exports.ipRangeCIDRPattern = /^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}\/(1[2-9]|2[0-4])$/; //Disallow anything bigger than a /12 -exports.ipRangeWildcardPattern = /^(\d{1,3}\.\d{1,3})\.(?:(\d{1,3}\.\*)|\*)$/; //Disallow anything bigger than a /16 -exports.maxTime = 9999999999999; -exports.unitsT5 = [UnitTypes.reign, UnitTypes.toxopid, UnitTypes.corvus, UnitTypes.eclipse, UnitTypes.oct, UnitTypes.omura, UnitTypes.navanax, UnitTypes.conquer, UnitTypes.collaris, UnitTypes.disrupt]; -exports.FishEvents = new funcs_1.EventEmitter(); +"use strict"; +/* +Copyright © BalaM314, 2026. All Rights Reserved. +This file contains mutable global variables, and global constants. +*/ +Object.defineProperty(exports, "__esModule", { value: true }); +exports.FishEvents = exports.unitsT5 = exports.maxTime = exports.ipRangeWildcardPattern = exports.ipRangeCIDRPattern = exports.ipPortPattern = exports.ipPattern = exports.uuidPattern = exports.ipJoins = exports.fishPlugin = exports.fishState = exports.recentWhispers = exports.tileHistory = void 0; +var funcs_1 = require("/funcs"); +exports.tileHistory = {}; +exports.recentWhispers = {}; +exports.fishState = { + restartQueued: false, + restartLoopTask: null, + corruption_t1: null, + corruption_t2: null, + lastPranked: Date.now(), + labels: [], + peacefulMode: false, + joinBell: false, + startTime: Date.now(), +}; +exports.fishPlugin = { + directory: null, + version: null, +}; +exports.ipJoins = new ObjectIntMap(); +exports.uuidPattern = /^[a-zA-Z0-9+/]{22}==$/; +exports.ipPattern = /^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}$/; +exports.ipPortPattern = /^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}:\d{1,5}$/; +exports.ipRangeCIDRPattern = /^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}\/(1[2-9]|2[0-4])$/; //Disallow anything bigger than a /12 +exports.ipRangeWildcardPattern = /^(\d{1,3}\.\d{1,3})\.(?:(\d{1,3}\.\*)|\*)$/; //Disallow anything bigger than a /16 +exports.maxTime = 9999999999999; +exports.unitsT5 = [UnitTypes.reign, UnitTypes.toxopid, UnitTypes.corvus, UnitTypes.eclipse, UnitTypes.oct, UnitTypes.omura, UnitTypes.navanax, UnitTypes.conquer, UnitTypes.collaris, UnitTypes.disrupt]; +exports.FishEvents = new funcs_1.EventEmitter(); diff --git a/build/scripts/index.js b/build/scripts/index.js index 07c2e51e..02acd68d 100644 --- a/build/scripts/index.js +++ b/build/scripts/index.js @@ -1,341 +1,341 @@ -"use strict"; -/* -Copyright © BalaM314, 2026. All Rights Reserved. -This file contains the main code, which calls other functions and initializes the plugin. -*/ -var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { - if (k2 === undefined) k2 = k; - var desc = Object.getOwnPropertyDescriptor(m, k); - if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { - desc = { enumerable: true, get: function() { return m[k]; } }; - } - Object.defineProperty(o, k2, desc); -}) : (function(o, m, k, k2) { - if (k2 === undefined) k2 = k; - o[k2] = m[k]; -})); -var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { - Object.defineProperty(o, "default", { enumerable: true, value: v }); -}) : function(o, v) { - o["default"] = v; -}); -var __importStar = (this && this.__importStar) || (function () { - var ownKeys = function(o) { - ownKeys = Object.getOwnPropertyNames || function (o) { - var ar = []; - for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k; - return ar; - }; - return ownKeys(o); - }; - return function (mod) { - if (mod && mod.__esModule) return mod; - var result = {}; - if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]); - __setModuleDefault(result, mod); - return result; - }; -})(); -var __values = (this && this.__values) || function(o) { - var s = typeof Symbol === "function" && Symbol.iterator, m = s && o[s], i = 0; - if (m) return m.call(o); - if (o && typeof o.length === "number") return { - next: function () { - if (o && i >= o.length) o = void 0; - return { value: o && o[i++], done: !o }; - } - }; - throw new TypeError(s ? "Object is not iterable." : "Symbol.iterator is not defined."); -}; -Object.defineProperty(exports, "__esModule", { value: true }); -var api = __importStar(require("/api")); -var aggregate_1 = require("/commands/aggregate"); -var config_1 = require("/config"); -var commands_1 = require("/frameworks/commands"); -var menus = __importStar(require("/frameworks/menus")); -var funcs_1 = require("/funcs"); -var globals_1 = require("/globals"); -var maps_1 = require("/maps"); -var packetHandlers_1 = require("/packetHandlers"); -var players_1 = require("/players"); -var timers = __importStar(require("/timers")); -var utils_1 = require("/utils"); -Events.on(EventType.ConnectionEvent, function (e) { - if (Vars.netServer.admins.bannedIPs.contains(e.connection.address)) { - api.getBanned({ - ip: e.connection.address, - }, function (banned) { - if (!banned) { - //If they were previously banned locally, but the API says they aren't banned, then unban them and clear the kick that the outer function already did - Vars.netServer.admins.unbanPlayerIP(e.connection.address); - Vars.netServer.admins.kickedIPs.remove(e.connection.address); - } - }); - } - else if (api.isVpnCached(e.connection.address) && players_1.FishPlayer.shouldWhackFlaggedPlayers()) { - Vars.netServer.admins.blacklistDos(e.connection.address); - e.connection.kick("You have been DOSblacklisted. Please join our discord for help: " + config_1.text.discordURL + "\nYou won't see this message again."); - Log.info("&yAntibot killed connection ".concat(e.connection.address, " due to flagged while under attack")); - } -}); -Events.on(EventType.PlayerConnect, function (e) { - if (players_1.FishPlayer.shouldKickNewPlayers() && e.player.info.timesJoined == 1) { - //do not use the helper function, for maximum performance - e.player.kick(Packets.KickReason.kick, 3600000); - } - players_1.FishPlayer.onPlayerConnect(e.player); -}); -Events.on(EventType.PlayerJoin, function (e) { - players_1.FishPlayer.onPlayerJoin(e.player); -}); -Events.on(EventType.PlayerLeave, function (e) { - players_1.FishPlayer.onPlayerLeave(e.player); -}); -Events.on(EventType.ConnectPacketEvent, function (e) { - if (!players_1.FishPlayer.connectRate.allow(5000, 35)) { - players_1.FishPlayer.triggerAntibot(300000, "Rate of player connections exceeded 35 / 5s", "automatic"); - } - globals_1.ipJoins.increment(e.connection.address); - var info = Vars.netServer.admins.getInfoOptional(e.packet.uuid); - var underAttack = players_1.FishPlayer.antiBotMode(); - var newPlayer = !info || info.timesJoined < 10; - var longModName = e.packet.mods.contains(function (str) { return str.length > 50; }); - var veryLongModName = e.packet.mods.contains(function (str) { return str.length > 100; }); - if ((underAttack && e.packet.mods.size > 2) || - (underAttack && longModName) || - (veryLongModName && (underAttack || newPlayer))) { - Vars.netServer.admins.blacklistDos(e.connection.address); - e.connection.kicked = true; - players_1.FishPlayer.triggerAntibot(60000, (veryLongModName ? "very long mod name" : longModName ? "long mod name" : "it had mods while under attack"), "automatic"); - return; - } - var suspiciousModName = e.packet.mods.contains(function (str) { return str.includes('\x1B'); }); - if (suspiciousModName || e.packet.name.includes('\x1B')) { - Vars.netServer.admins.blacklistDos(e.connection.address); - e.connection.kicked = true; - players_1.FishPlayer.triggerAntibot(5000, "illegal characters in name or mods", "automatic"); - return; - } - if (globals_1.ipJoins.get(e.connection.address) >= ((underAttack || veryLongModName) ? 3 : (newPlayer || longModName) ? 7 : 15)) { - Vars.netServer.admins.blacklistDos(e.connection.address); - e.connection.kicked = true; - players_1.FishPlayer.triggerAntibot(5000, "too many connections", "automatic"); - return; - } - /*if(e.packet.name.includes("discord.gg/GnEdS9TdV6")){ - Vars.netServer.admins.blacklistDos(e.connection.address); - e.connection.kicked = true; - FishPlayer.onBotWhack(); - Log.info(`&yAntibot killed connection ${e.connection.address} due to omni discord link`); - return; - }*/ - if (e.packet.name.includes("1`1@everyone")) { - Vars.netServer.admins.blacklistDos(e.connection.address); - e.connection.kicked = true; - players_1.FishPlayer.triggerAntibot(-1, "known bad name", "automatic"); - return; - } - if (Vars.netServer.admins.isDosBlacklisted(e.connection.address)) { - //threading moment, i think - e.connection.kicked = true; - return; - } - api.getBanned({ - ip: e.connection.address, - uuid: e.packet.uuid - }, function (banned) { - if (banned) { - Log.info("&lrSynced ban of ".concat(e.packet.uuid, "/").concat(e.connection.address, ".")); - e.connection.kick(Packets.KickReason.banned, 1); - Vars.netServer.admins.banPlayerIP(e.connection.address); - Vars.netServer.admins.banPlayerID(e.packet.uuid); - } - else { - Vars.netServer.admins.unbanPlayerIP(e.connection.address); - Vars.netServer.admins.unbanPlayerID(e.packet.uuid); - } - }); - players_1.FishPlayer.onConnectPacket(e.packet); -}); -Events.on(EventType.UnitChangeEvent, function (e) { - players_1.FishPlayer.onUnitChange(e.player, e.unit); -}); -Events.on(EventType.ContentInitEvent, function () { - //Unhide latum and renale - UnitTypes.latum.hidden = false; - UnitTypes.renale.hidden = false; -}); -Events.on(EventType.PlayerChatEvent, function (e) { return (0, utils_1.processChat)(e.player, e.message, true); }); -Events.on(EventType.ServerLoadEvent, function (e) { - Time.mark(); - var clientHandler = Vars.netServer.clientCommands; - var serverHandler = ServerControl.instance.handler; - players_1.FishPlayer.loadAll(); - globals_1.FishEvents.fire("loadData", []); - timers.initializeTimers(); - menus.registerListeners(); - //Cap delta - Time.setDeltaProvider(function () { return Math.min(Core.graphics.getDeltaTime() * 60, 10); }); - // Mute muted players - Vars.netServer.admins.addChatFilter(function (player, message) { return (0, utils_1.processChat)(player, message); }); - // Vars.netServer.admins.addChatFilter((p, message) => FishPlayer.get(p).hasPerm("member") ? message : foolifyChat(message)); - // Action filters - Vars.netServer.admins.addActionFilter(function (action) { - var _a, _b, _c; - var player = action.player; - var fishP = players_1.FishPlayer.get(player); - //prevent stopped players from doing anything other than deposit items. - if (!fishP.hasPerm("play")) { - action.player.sendMessage('[scarlet]\u26A0 [yellow]You are stopped, you cant perfom this action.'); - return false; - } - else { - if (action.type === Administration.ActionType.pickupBlock) { - (0, utils_1.addToTileHistory)({ - pos: "".concat(action.tile.x, ",").concat(action.tile.y), - uuid: action.player.uuid(), - action: "picked up", - type: (_b = (_a = action.tile.block()) === null || _a === void 0 ? void 0 : _a.name) !== null && _b !== void 0 ? _b : "nothing", - }); - } - else if (action.type === Administration.ActionType.control && !((_c = action.unit) === null || _c === void 0 ? void 0 : _c.spawnedByCore) && Date.now() < fishP.blockedFromPossessingUnitsUntil) { - action.player.sendMessage("[scarlet]\u26A0 [yellow]You are blocked from controlling units for ".concat((0, utils_1.formatTimeRelative)(fishP.blockedFromPossessingUnitsUntil, true))); - return false; - } - else if (action.type === Administration.ActionType.commandUnits && Date.now() < fishP.blockedFromCommandingUnitsUntil) { - action.player.sendMessage("[scarlet]\u26A0 [yellow]You are blocked from commanding units for ".concat((0, utils_1.formatTimeRelative)(fishP.blockedFromCommandingUnitsUntil, true))); - return false; - } - else if (action.type === Administration.ActionType.pingLocation && action.pingText && action.pingText.length < Vars.maxPingTextLength) { - var fishP_1 = players_1.FishPlayer.get(action.player); - if (fishP_1.muted) { - action.player.sendMessage("[scarlet]\u26A0 [yellow]You are muted, you cannot send text through location pings."); - return false; - } - else if ((0, utils_1.matchFilter)(action.pingText, "chat", false)) { - //Allow it, but replace - player.pingX = action.pingX; - player.pingY = action.pingY; - player.pingTime = 1; - player.pingText = config_1.text.chatFilterReplacement.messageShort(); - return false; - } - } - return true; - } - }); - (0, aggregate_1.registerAll)(clientHandler, serverHandler); - (0, packetHandlers_1.loadPacketHandlers)(); - //Load plugin data - try { - var path = (0, utils_1.fishCommandsRootDirPath)(); - globals_1.fishPlugin.directory = path.toString(); - Threads.daemon(function () { - try { - globals_1.fishPlugin.version = OS.exec("git", "-C", globals_1.fishPlugin.directory, "rev-parse", "HEAD"); - } - catch (_a) { } - }); - } - catch (err) { - Log.err("Failed to get fish plugin information."); - Log.err(err); - } - Runtime.getRuntime().addShutdownHook(new Thread(function () { - try { - players_1.FishPlayer.uploadAll(); - } - catch (_a) { - Log.err("failed to upload"); - } - try { - globals_1.FishEvents.fire("saveData", []); - } - catch (_b) { - Log.err("failed to save misc data"); - } - try { - players_1.FishPlayer.saveAll(false); - } - catch (_c) { - Log.err("failed to save player data"); - } - Log.info("Saved on exit."); - })); - Vars.netServer.assigner = function (player, players) { - var _a; - if (Vars.state.rules.pvp) { - //find team with minimum amount of players and auto-assign player to that. - var fishP = players_1.FishPlayer.get(player); - var preferredTeam_1 = null; - if (fishP.restoreTeam && (Date.now() - fishP.restoreTeam[1] < funcs_1.Duration.minutes(5)) && fishP.restoreTeam[2] == ((_a = maps_1.PartialMapRun.current) === null || _a === void 0 ? void 0 : _a.startTime)) - preferredTeam_1 = fishP.restoreTeam[0]; - var re = Vars.state.teams.getActive().select(function (data) { return !((Vars.state.rules.waveTeam == data.team && Vars.state.rules.waves) || - !data.hasCore() || - data.team == Team.derelict || - !data.team.rules().protectCores); }).min(floatf(function (data) { - //Only if the team is valid - if (data.team == preferredTeam_1) - return -1; - var count = 0; - players.forEach(function (other) { - if (other.team() == data.team && other != player) { - count++; - } - }); - return count + Mathf.random(-0.1, 0.1); - })); - return re == null ? Vars.state.rules.defaultTeam : re.team; - } - else { - return Vars.state.rules.defaultTeam; - } - }; - Log.info("fish-commands: initialized in @ms (incl previous)", Time.elapsed()); -}); -// Keeps track of any action performed on a tile for use in tilelog. -Events.on(EventType.BlockBuildBeginEvent, utils_1.addToTileHistory); -Events.on(EventType.BuildRotateEvent, utils_1.addToTileHistory); -Events.on(EventType.ConfigEvent, utils_1.addToTileHistory); -Events.on(EventType.PickupEvent, utils_1.addToTileHistory); -Events.on(EventType.PayloadDropEvent, utils_1.addToTileHistory); -Events.on(EventType.UnitDestroyEvent, utils_1.addToTileHistory); -Events.on(EventType.BlockDestroyEvent, utils_1.addToTileHistory); -Events.on(EventType.UnitControlEvent, utils_1.addToTileHistory); -Events.on(EventType.TapEvent, commands_1.handleTapEvent); -Events.on(EventType.GameOverEvent, function (e) { - var e_1, _a; - try { - for (var _b = __values(Object.keys(globals_1.tileHistory)), _c = _b.next(); !_c.done; _c = _b.next()) { - var key = _c.value; - //clear tilelog - globals_1.tileHistory[key] = null; - delete globals_1.tileHistory[key]; - } - } - catch (e_1_1) { e_1 = { error: e_1_1 }; } - finally { - try { - if (_c && !_c.done && (_a = _b.return)) _a.call(_b); - } - finally { if (e_1) throw e_1.error; } - } - if (globals_1.fishState.restartQueued) { - //restart - Call.sendMessage("[accent]---[[[coral]+++[]]---\n[accent]Server restart imminent. [green]We'll be back after 15 seconds.[]\n[accent]---[[[coral]+++[]]---"); - (0, utils_1.serverRestartLoop)(12, true); - Events.on(EventType.WorldLoadBeginEvent, function () { - //Remove save - (0, utils_1.restartNow)(true); - }); - } - players_1.FishPlayer.onGameOver(e.winner); -}); -Events.on(EventType.WorldLoadEvent, function () { return players_1.FishPlayer.onGameBegin(); }); -Events.on(EventType.PlayerChatEvent, function (e) { - players_1.FishPlayer.onPlayerChat(e.player, e.message); -}); -Events.on(EventType.PlayEvent, function () { - globals_1.fishState.startTime = Date.now(); -}); -Log.info("fish-commands: parsing done in @ms", Date.now() - this._startTime); +"use strict"; +/* +Copyright © BalaM314, 2026. All Rights Reserved. +This file contains the main code, which calls other functions and initializes the plugin. +*/ +var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + var desc = Object.getOwnPropertyDescriptor(m, k); + if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { + desc = { enumerable: true, get: function() { return m[k]; } }; + } + Object.defineProperty(o, k2, desc); +}) : (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + o[k2] = m[k]; +})); +var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { + Object.defineProperty(o, "default", { enumerable: true, value: v }); +}) : function(o, v) { + o["default"] = v; +}); +var __importStar = (this && this.__importStar) || (function () { + var ownKeys = function(o) { + ownKeys = Object.getOwnPropertyNames || function (o) { + var ar = []; + for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k; + return ar; + }; + return ownKeys(o); + }; + return function (mod) { + if (mod && mod.__esModule) return mod; + var result = {}; + if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]); + __setModuleDefault(result, mod); + return result; + }; +})(); +var __values = (this && this.__values) || function(o) { + var s = typeof Symbol === "function" && Symbol.iterator, m = s && o[s], i = 0; + if (m) return m.call(o); + if (o && typeof o.length === "number") return { + next: function () { + if (o && i >= o.length) o = void 0; + return { value: o && o[i++], done: !o }; + } + }; + throw new TypeError(s ? "Object is not iterable." : "Symbol.iterator is not defined."); +}; +Object.defineProperty(exports, "__esModule", { value: true }); +var api = __importStar(require("/api")); +var aggregate_1 = require("/commands/aggregate"); +var config_1 = require("/config"); +var commands_1 = require("/frameworks/commands"); +var menus = __importStar(require("/frameworks/menus")); +var funcs_1 = require("/funcs"); +var globals_1 = require("/globals"); +var maps_1 = require("/maps"); +var packetHandlers_1 = require("/packetHandlers"); +var players_1 = require("/players"); +var timers = __importStar(require("/timers")); +var utils_1 = require("/utils"); +Events.on(EventType.ConnectionEvent, function (e) { + if (Vars.netServer.admins.bannedIPs.contains(e.connection.address)) { + api.getBanned({ + ip: e.connection.address, + }, function (banned) { + if (!banned) { + //If they were previously banned locally, but the API says they aren't banned, then unban them and clear the kick that the outer function already did + Vars.netServer.admins.unbanPlayerIP(e.connection.address); + Vars.netServer.admins.kickedIPs.remove(e.connection.address); + } + }); + } + else if (api.isVpnCached(e.connection.address) && players_1.FishPlayer.shouldWhackFlaggedPlayers()) { + Vars.netServer.admins.blacklistDos(e.connection.address); + e.connection.kick("You have been DOSblacklisted. Please join our discord for help: " + config_1.text.discordURL + "\nYou won't see this message again."); + Log.info("&yAntibot killed connection ".concat(e.connection.address, " due to flagged while under attack")); + } +}); +Events.on(EventType.PlayerConnect, function (e) { + if (players_1.FishPlayer.shouldKickNewPlayers() && e.player.info.timesJoined == 1) { + //do not use the helper function, for maximum performance + e.player.kick(Packets.KickReason.kick, 3600000); + } + players_1.FishPlayer.onPlayerConnect(e.player); +}); +Events.on(EventType.PlayerJoin, function (e) { + players_1.FishPlayer.onPlayerJoin(e.player); +}); +Events.on(EventType.PlayerLeave, function (e) { + players_1.FishPlayer.onPlayerLeave(e.player); +}); +Events.on(EventType.ConnectPacketEvent, function (e) { + if (!players_1.FishPlayer.connectRate.allow(5000, 35)) { + players_1.FishPlayer.triggerAntibot(300000, "Rate of player connections exceeded 35 / 5s", "automatic"); + } + globals_1.ipJoins.increment(e.connection.address); + var info = Vars.netServer.admins.getInfoOptional(e.packet.uuid); + var underAttack = players_1.FishPlayer.antiBotMode(); + var newPlayer = !info || info.timesJoined < 10; + var longModName = e.packet.mods.contains(function (str) { return str.length > 50; }); + var veryLongModName = e.packet.mods.contains(function (str) { return str.length > 100; }); + if ((underAttack && e.packet.mods.size > 2) || + (underAttack && longModName) || + (veryLongModName && (underAttack || newPlayer))) { + Vars.netServer.admins.blacklistDos(e.connection.address); + e.connection.kicked = true; + players_1.FishPlayer.triggerAntibot(60000, (veryLongModName ? "very long mod name" : longModName ? "long mod name" : "it had mods while under attack"), "automatic"); + return; + } + var suspiciousModName = e.packet.mods.contains(function (str) { return str.includes('\x1B'); }); + if (suspiciousModName || e.packet.name.includes('\x1B')) { + Vars.netServer.admins.blacklistDos(e.connection.address); + e.connection.kicked = true; + players_1.FishPlayer.triggerAntibot(5000, "illegal characters in name or mods", "automatic"); + return; + } + if (globals_1.ipJoins.get(e.connection.address) >= ((underAttack || veryLongModName) ? 3 : (newPlayer || longModName) ? 7 : 15)) { + Vars.netServer.admins.blacklistDos(e.connection.address); + e.connection.kicked = true; + players_1.FishPlayer.triggerAntibot(5000, "too many connections", "automatic"); + return; + } + /*if(e.packet.name.includes("discord.gg/GnEdS9TdV6")){ + Vars.netServer.admins.blacklistDos(e.connection.address); + e.connection.kicked = true; + FishPlayer.onBotWhack(); + Log.info(`&yAntibot killed connection ${e.connection.address} due to omni discord link`); + return; + }*/ + if (e.packet.name.includes("1`1@everyone")) { + Vars.netServer.admins.blacklistDos(e.connection.address); + e.connection.kicked = true; + players_1.FishPlayer.triggerAntibot(-1, "known bad name", "automatic"); + return; + } + if (Vars.netServer.admins.isDosBlacklisted(e.connection.address)) { + //threading moment, i think + e.connection.kicked = true; + return; + } + api.getBanned({ + ip: e.connection.address, + uuid: e.packet.uuid + }, function (banned) { + if (banned) { + Log.info("&lrSynced ban of ".concat(e.packet.uuid, "/").concat(e.connection.address, ".")); + e.connection.kick(Packets.KickReason.banned, 1); + Vars.netServer.admins.banPlayerIP(e.connection.address); + Vars.netServer.admins.banPlayerID(e.packet.uuid); + } + else { + Vars.netServer.admins.unbanPlayerIP(e.connection.address); + Vars.netServer.admins.unbanPlayerID(e.packet.uuid); + } + }); + players_1.FishPlayer.onConnectPacket(e.packet); +}); +Events.on(EventType.UnitChangeEvent, function (e) { + players_1.FishPlayer.onUnitChange(e.player, e.unit); +}); +Events.on(EventType.ContentInitEvent, function () { + //Unhide latum and renale + UnitTypes.latum.hidden = false; + UnitTypes.renale.hidden = false; +}); +Events.on(EventType.PlayerChatEvent, function (e) { return (0, utils_1.processChat)(e.player, e.message, true); }); +Events.on(EventType.ServerLoadEvent, function (e) { + Time.mark(); + var clientHandler = Vars.netServer.clientCommands; + var serverHandler = ServerControl.instance.handler; + players_1.FishPlayer.loadAll(); + globals_1.FishEvents.fire("loadData", []); + timers.initializeTimers(); + menus.registerListeners(); + //Cap delta + Time.setDeltaProvider(function () { return Math.min(Core.graphics.getDeltaTime() * 60, 10); }); + // Mute muted players + Vars.netServer.admins.addChatFilter(function (player, message) { return (0, utils_1.processChat)(player, message); }); + // Vars.netServer.admins.addChatFilter((p, message) => FishPlayer.get(p).hasPerm("member") ? message : foolifyChat(message)); + // Action filters + Vars.netServer.admins.addActionFilter(function (action) { + var _a, _b, _c; + var player = action.player; + var fishP = players_1.FishPlayer.get(player); + //prevent stopped players from doing anything other than deposit items. + if (!fishP.hasPerm("play")) { + action.player.sendMessage('[scarlet]\u26A0 [yellow]You are stopped, you cant perfom this action.'); + return false; + } + else { + if (action.type === Administration.ActionType.pickupBlock) { + (0, utils_1.addToTileHistory)({ + pos: "".concat(action.tile.x, ",").concat(action.tile.y), + uuid: action.player.uuid(), + action: "picked up", + type: (_b = (_a = action.tile.block()) === null || _a === void 0 ? void 0 : _a.name) !== null && _b !== void 0 ? _b : "nothing", + }); + } + else if (action.type === Administration.ActionType.control && !((_c = action.unit) === null || _c === void 0 ? void 0 : _c.spawnedByCore) && Date.now() < fishP.blockedFromPossessingUnitsUntil) { + action.player.sendMessage("[scarlet]\u26A0 [yellow]You are blocked from controlling units for ".concat((0, utils_1.formatTimeRelative)(fishP.blockedFromPossessingUnitsUntil, true))); + return false; + } + else if (action.type === Administration.ActionType.commandUnits && Date.now() < fishP.blockedFromCommandingUnitsUntil) { + action.player.sendMessage("[scarlet]\u26A0 [yellow]You are blocked from commanding units for ".concat((0, utils_1.formatTimeRelative)(fishP.blockedFromCommandingUnitsUntil, true))); + return false; + } + else if (action.type === Administration.ActionType.pingLocation && action.pingText && action.pingText.length < Vars.maxPingTextLength) { + var fishP_1 = players_1.FishPlayer.get(action.player); + if (fishP_1.muted) { + action.player.sendMessage("[scarlet]\u26A0 [yellow]You are muted, you cannot send text through location pings."); + return false; + } + else if ((0, utils_1.matchFilter)(action.pingText, "chat", false)) { + //Allow it, but replace + player.pingX = action.pingX; + player.pingY = action.pingY; + player.pingTime = 1; + player.pingText = config_1.text.chatFilterReplacement.messageShort(); + return false; + } + } + return true; + } + }); + (0, aggregate_1.registerAll)(clientHandler, serverHandler); + (0, packetHandlers_1.loadPacketHandlers)(); + //Load plugin data + try { + var path = (0, utils_1.fishCommandsRootDirPath)(); + globals_1.fishPlugin.directory = path.toString(); + Threads.daemon(function () { + try { + globals_1.fishPlugin.version = OS.exec("git", "-C", globals_1.fishPlugin.directory, "rev-parse", "HEAD"); + } + catch (_a) { } + }); + } + catch (err) { + Log.err("Failed to get fish plugin information."); + Log.err(err); + } + Runtime.getRuntime().addShutdownHook(new Thread(function () { + try { + players_1.FishPlayer.uploadAll(); + } + catch (_a) { + Log.err("failed to upload"); + } + try { + globals_1.FishEvents.fire("saveData", []); + } + catch (_b) { + Log.err("failed to save misc data"); + } + try { + players_1.FishPlayer.saveAll(false); + } + catch (_c) { + Log.err("failed to save player data"); + } + Log.info("Saved on exit."); + })); + Vars.netServer.assigner = function (player, players) { + var _a; + if (Vars.state.rules.pvp) { + //find team with minimum amount of players and auto-assign player to that. + var fishP = players_1.FishPlayer.get(player); + var preferredTeam_1 = null; + if (fishP.restoreTeam && (Date.now() - fishP.restoreTeam[1] < funcs_1.Duration.minutes(5)) && fishP.restoreTeam[2] == ((_a = maps_1.PartialMapRun.current) === null || _a === void 0 ? void 0 : _a.startTime)) + preferredTeam_1 = fishP.restoreTeam[0]; + var re = Vars.state.teams.getActive().select(function (data) { return !((Vars.state.rules.waveTeam == data.team && Vars.state.rules.waves) || + !data.hasCore() || + data.team == Team.derelict || + !data.team.rules().protectCores); }).min(floatf(function (data) { + //Only if the team is valid + if (data.team == preferredTeam_1) + return -1; + var count = 0; + players.forEach(function (other) { + if (other.team() == data.team && other != player) { + count++; + } + }); + return count + Mathf.random(-0.1, 0.1); + })); + return re == null ? Vars.state.rules.defaultTeam : re.team; + } + else { + return Vars.state.rules.defaultTeam; + } + }; + Log.info("fish-commands: initialized in @ms (incl previous)", Time.elapsed()); +}); +// Keeps track of any action performed on a tile for use in tilelog. +Events.on(EventType.BlockBuildBeginEvent, utils_1.addToTileHistory); +Events.on(EventType.BuildRotateEvent, utils_1.addToTileHistory); +Events.on(EventType.ConfigEvent, utils_1.addToTileHistory); +Events.on(EventType.PickupEvent, utils_1.addToTileHistory); +Events.on(EventType.PayloadDropEvent, utils_1.addToTileHistory); +Events.on(EventType.UnitDestroyEvent, utils_1.addToTileHistory); +Events.on(EventType.BlockDestroyEvent, utils_1.addToTileHistory); +Events.on(EventType.UnitControlEvent, utils_1.addToTileHistory); +Events.on(EventType.TapEvent, commands_1.handleTapEvent); +Events.on(EventType.GameOverEvent, function (e) { + var e_1, _a; + try { + for (var _b = __values(Object.keys(globals_1.tileHistory)), _c = _b.next(); !_c.done; _c = _b.next()) { + var key = _c.value; + //clear tilelog + globals_1.tileHistory[key] = null; + delete globals_1.tileHistory[key]; + } + } + catch (e_1_1) { e_1 = { error: e_1_1 }; } + finally { + try { + if (_c && !_c.done && (_a = _b.return)) _a.call(_b); + } + finally { if (e_1) throw e_1.error; } + } + if (globals_1.fishState.restartQueued) { + //restart + Call.sendMessage("[accent]---[[[coral]+++[]]---\n[accent]Server restart imminent. [green]We'll be back after 15 seconds.[]\n[accent]---[[[coral]+++[]]---"); + (0, utils_1.serverRestartLoop)(12, true); + Events.on(EventType.WorldLoadBeginEvent, function () { + //Remove save + (0, utils_1.restartNow)(true); + }); + } + players_1.FishPlayer.onGameOver(e.winner); +}); +Events.on(EventType.WorldLoadEvent, function () { return players_1.FishPlayer.onGameBegin(); }); +Events.on(EventType.PlayerChatEvent, function (e) { + players_1.FishPlayer.onPlayerChat(e.player, e.message); +}); +Events.on(EventType.PlayEvent, function () { + globals_1.fishState.startTime = Date.now(); +}); +Log.info("fish-commands: parsing done in @ms", Date.now() - this._startTime); diff --git a/build/scripts/maps.js b/build/scripts/maps.js index b9f770de..3cc33473 100644 --- a/build/scripts/maps.js +++ b/build/scripts/maps.js @@ -1,361 +1,361 @@ -"use strict"; -/* -Copyright © BalaM314, 2026. All Rights Reserved. -This file contains the map run tracker and statistics computation. -*/ -var __extends = (this && this.__extends) || (function () { - var extendStatics = function (d, b) { - extendStatics = Object.setPrototypeOf || - ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || - function (d, b) { for (var p in b) if (Object.prototype.hasOwnProperty.call(b, p)) d[p] = b[p]; }; - return extendStatics(d, b); - }; - return function (d, b) { - if (typeof b !== "function" && b !== null) - throw new TypeError("Class extends value " + String(b) + " is not a constructor or null"); - extendStatics(d, b); - function __() { this.constructor = d; } - d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); - }; -})(); -var __esDecorate = (this && this.__esDecorate) || function (ctor, descriptorIn, decorators, contextIn, initializers, extraInitializers) { - function accept(f) { if (f !== void 0 && typeof f !== "function") throw new TypeError("Function expected"); return f; } - var kind = contextIn.kind, key = kind === "getter" ? "get" : kind === "setter" ? "set" : "value"; - var target = !descriptorIn && ctor ? contextIn["static"] ? ctor : ctor.prototype : null; - var descriptor = descriptorIn || (target ? Object.getOwnPropertyDescriptor(target, contextIn.name) : {}); - var _, done = false; - for (var i = decorators.length - 1; i >= 0; i--) { - var context = {}; - for (var p in contextIn) context[p] = p === "access" ? {} : contextIn[p]; - for (var p in contextIn.access) context.access[p] = contextIn.access[p]; - context.addInitializer = function (f) { if (done) throw new TypeError("Cannot add initializers after decoration has completed"); extraInitializers.push(accept(f || null)); }; - var result = (0, decorators[i])(kind === "accessor" ? { get: descriptor.get, set: descriptor.set } : descriptor[key], context); - if (kind === "accessor") { - if (result === void 0) continue; - if (result === null || typeof result !== "object") throw new TypeError("Object expected"); - if (_ = accept(result.get)) descriptor.get = _; - if (_ = accept(result.set)) descriptor.set = _; - if (_ = accept(result.init)) initializers.unshift(_); - } - else if (_ = accept(result)) { - if (kind === "field") initializers.unshift(_); - else descriptor[key] = _; - } - } - if (target) Object.defineProperty(target, contextIn.name, descriptor); - done = true; -}; -var __runInitializers = (this && this.__runInitializers) || function (thisArg, initializers, value) { - var useValue = arguments.length > 2; - for (var i = 0; i < initializers.length; i++) { - value = useValue ? initializers[i].call(thisArg, value) : initializers[i].call(thisArg); - } - return useValue ? value : void 0; -}; -var __read = (this && this.__read) || function (o, n) { - var m = typeof Symbol === "function" && o[Symbol.iterator]; - if (!m) return o; - var i = m.call(o), r, ar = [], e; - try { - while ((n === void 0 || n-- > 0) && !(r = i.next()).done) ar.push(r.value); - } - catch (error) { e = { error: error }; } - finally { - try { - if (r && !r.done && (m = i["return"])) m.call(i); - } - finally { if (e) throw e.error; } - } - return ar; -}; -var _this = this; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.FMap = exports.PartialMapRun = exports.FinishedMapRun = void 0; -var config_1 = require("/config"); -var io_1 = require("/frameworks/io"); -var funcs_1 = require("/funcs"); -var globals_1 = require("/globals"); -var utils_1 = require("/utils"); -var FinishedMapRun = /** @class */ (function (_super) { - __extends(FinishedMapRun, _super); - //this constructor is useless, but rhino crashes with a bizarre error when trying to run the emitted code - //do not remove this useless constructor - function FinishedMapRun(data) { - return _super.call(this, data) || this; - } - FinishedMapRun.prototype.duration = function () { - return this.endTime - this.startTime; - }; - FinishedMapRun.prototype.outcome = function () { - if (config_1.Gamemode.pvp()) { - if (this.winTeam === Team.derelict) { - if (this.duration() > funcs_1.Duration.minutes(20)) - return ["rtv", "late rtv"]; - else - return ["rtv", "early rtv"]; - } - else - return ["win", "win"]; - } - else { - if (this.success) - return ["win", "win"]; - else if (this.winTeam === Team.derelict) { - if (this.duration() > funcs_1.Duration.minutes(3)) - return ["loss", "late rtv"]; - else - return ["rtv", "early rtv"]; - } - else - return ["loss", "loss"]; - } - }; - return FinishedMapRun; -}((0, io_1.dataClass)())); -exports.FinishedMapRun = FinishedMapRun; -var PartialMapRun = /** @class */ (function () { - function PartialMapRun() { - this.startTime = Date.now(); - this.maxPlayerCount = 0; - } - /** In milliseconds */ - PartialMapRun.prototype.duration = function () { - return Date.now() - this.startTime; - }; - PartialMapRun.prototype.update = function () { - this.maxPlayerCount = Math.max(this.maxPlayerCount, Groups.player.size()); - }; - PartialMapRun.prototype.finish = function (_b) { - var winTeam = _b.winTeam; - return new FinishedMapRun({ - winTeam: winTeam, - success: config_1.Gamemode.pvp() ? true : winTeam == Vars.state.rules.defaultTeam, - startTime: this.startTime, - endTime: Date.now(), - maxPlayerCount: this.maxPlayerCount, - wave: Vars.state.wave, - }); - }; - //Used for continuing through a restart - PartialMapRun.prototype.write = function () { - return "".concat(Date.now() - this.startTime, "/").concat(this.maxPlayerCount); - }; - PartialMapRun.read = function (data) { - var _b = __read(data.split("/").map(Number), 2), duration = _b[0], maxPlayerCount = _b[1]; - if (isNaN(duration) || isNaN(maxPlayerCount)) { - Log.err("_FINDTAG_ failed to load map run stats data: ".concat(data)); - } - var out = new _a(); - out.startTime = Date.now() - duration; //move start time forward by time when the server was off - out.maxPlayerCount = maxPlayerCount; - return out; - }; - var _a; - _a = PartialMapRun; - PartialMapRun.key = "fish-partial-map-run"; - PartialMapRun.current = null; - (function () { - globals_1.FishEvents.on("saveData", function () { - if (_a.current) - Core.settings.put(_a.key, _a.current.write()); - }); - globals_1.FishEvents.on("loadData", function () { - var data = Core.settings.getString(_a.key); - if (data) { - _a.current = _a.read(data); - } - else { - //loading a map, but there is no run information, create one - _a.current = new _a(); - } - }); - Events.on(EventType.SaveLoadEvent, function (e) { - var _b; - //create a new run, if there isn't one already - //loadData will have run first if it is a server restart - (_b = _a.current) !== null && _b !== void 0 ? _b : (_a.current = new _a()); - }); - Timer.schedule(function () { - var _b; - (_b = _a.current) === null || _b === void 0 ? void 0 : _b.update(); - }, 0, 5); - Events.on(EventType.GameOverEvent, function (e) { - var _b; - if (_a.current) { - var finishedRun = _a.current.finish({ winTeam: (_b = e.winner) !== null && _b !== void 0 ? _b : Team.derelict }); - var fmap = FMap.getCreate(Vars.state.map); - if (!fmap) - return; - //Highscore message - if (config_1.Gamemode.attack() && finishedRun.success) { - var bestPreviousTime = fmap.stats().shortestWinTime; - var duration = finishedRun.duration(); - Call.sendMessage("[orange]--------\n".concat(finishedRun.success && duration < bestPreviousTime ? - "[green]New highscore! Map completed in [accent]".concat((0, utils_1.formatTimeShort)(duration), "[]") - : "[orange]Map completed in [accent]".concat((0, utils_1.formatTimeShort)(duration), "[]. Current highscore: [green]").concat((0, utils_1.formatTimeShort)(bestPreviousTime), "[]"), "\n[orange]--------")); - } - else if (config_1.Gamemode.survival()) { - var bestPreviousWave = fmap.stats().highestWave; - var wave = finishedRun.wave; - Call.sendMessage("[orange]--------\n".concat(finishedRun.success && wave < bestPreviousWave ? - "[green]New highscore! Reached wave [accent]".concat(wave, "[].") - : "[orange]Reached wave [accent]".concat(wave, "[]. Current highscore: [green]").concat(bestPreviousWave, "[]"), "\n[orange]--------")); - } - fmap.runs.push(finishedRun); - globals_1.FishEvents.fire("saveMaps", []); - } - Core.settings.remove(_a.key); - _a.current = null; - }); - })(); - return PartialMapRun; -}()); -exports.PartialMapRun = PartialMapRun; -var FMap = function () { - var _b; - var _classSuper = (0, io_1.dataClass)(); - var _static_allMaps_decorators; - var _static_allMaps_initializers = []; - var _static_allMaps_extraInitializers = []; - return _b = /** @class */ (function (_super) { - __extends(FMap, _super); - function FMap(data, - //O(n^2)... should be fine? - map) { - if (map === void 0) { map = Vars.maps.customMaps().find(function (m) { return m.file.name() === data.mapFileName; }); } - var _this = _super.call(this, data) || this; - _this.map = map; - return _this; - } - FMap.getCreate = function (map) { - if (this.allMaps == null) - return null; - var mapFileName = map.file.name(); - if (Object.prototype.hasOwnProperty.call(this.maps, mapFileName)) - return this.maps[mapFileName]; - var fmap = new this({ - runs: [], - mapFileName: mapFileName - }, map); - this.maps[mapFileName] = fmap; - this.allMaps.push(fmap); - return fmap; - }; - FMap.prototype.rules = function () { - var _c; - return (_c = this.map) === null || _c === void 0 ? void 0 : _c.rules(); - }; - FMap.prototype.stats = function () { - var _c; - var runs = this.runs.filter(function (r) { return r.maxPlayerCount > 0; }); //Remove all runs with no players on - var allRunCount = runs.length; - var victories = runs.filter(function (r) { return r.outcome()[1] === "win"; }); - var losses = runs.filter(function (r) { return r.outcome()[0] === "loss"; }).length; - var earlyRTVs = runs.filter(function (r) { return r.outcome()[1] === "early rtv"; }).length; - var lateRTVs = runs.filter(function (r) { return r.outcome()[1] === "late rtv"; }).length; - var significantRunCount = allRunCount - earlyRTVs; - var totalLosses = losses + lateRTVs; - var durations = runs.filter(function (r) { return r.outcome()[0] !== "rtv"; }).map(function (r) { return r.duration(); }); - var durationStats = (0, funcs_1.computeStatistics)(durations); - var winDurationStats = (0, funcs_1.computeStatistics)(runs.filter(function (r) { return r.outcome()[0] === "win"; }).map(function (r) { return r.duration(); })); - var teamWins = runs.filter(function (r) { return r.outcome()[1] !== "early rtv"; }).reduce(function (acc, item) { - var _c; - acc[item.winTeam.name] = ((_c = acc[item.winTeam.name]) !== null && _c !== void 0 ? _c : 0) + 1; - return acc; - }, {}); - var teamWinRate = Object.fromEntries(Object.entries(teamWins).map(function (_c) { - var _d = __read(_c, 2), team = _d[0], wins = _d[1]; - return [team, wins / significantRunCount]; - })); - //Remove runs that were on wave 0, due to a silly bug we have thousands of runs with a max wave of 0 - var waveStats = (0, funcs_1.computeStatistics)(runs.filter(function (r) { return r.outcome()[0] !== "rtv" && r.wave !== 0; }).map(function (r) { return r.wave; })); - return { - allRunCount: allRunCount, - significantRunCount: significantRunCount, - victories: victories.length, - losses: losses, - totalLosses: totalLosses, - earlyRTVs: earlyRTVs, - lateRTVs: lateRTVs, - earlyRTVRate: earlyRTVs / allRunCount, - winRate: victories.length / significantRunCount, - lossRate: losses / significantRunCount, - averagePlaytime: durationStats.average, - shortestWinTime: winDurationStats.lowest, - longestTime: durationStats.highest, - shortestTime: durationStats.lowest, - averageHighestPlayerCount: (0, funcs_1.computeStatistics)(runs.map(function (r) { return r.maxPlayerCount; })).average, - teamWins: teamWins, - teamWinRate: teamWinRate, - highestWave: waveStats.highest, - averageWave: waveStats.average, - mostRecentWin: (_c = victories.at(-1)) === null || _c === void 0 ? void 0 : _c.startTime - }; - }; - FMap.prototype.displayStats = function (f) { - var map = this.map; - if (!map) - return null; - var stats = this.stats(); - var rules = this.rules(); - var modeSpecificStats = (0, utils_1.match)(config_1.Gamemode.name(), { - attack: "[#CCFFCC]Total runs: ".concat(stats.allRunCount, " (").concat(stats.victories, " wins, ").concat(stats.totalLosses, " losses, ").concat(stats.earlyRTVs, " RTVs)\n[#CCFFCC]Outcomes: ").concat(f.percent(stats.winRate, 1), " wins, ").concat(f.percent(stats.lossRate, 1), " losses, ").concat(f.percent(stats.earlyRTVRate, 1), " RTVs\n[#CCFFCC]Average playtime: ").concat((0, utils_1.formatTime)(stats.averagePlaytime), "\n[#CCFFCC]Shortest win time: ").concat((0, utils_1.formatTime)(stats.shortestWinTime), "\n[#CCFFCC]Most recent win: ").concat(stats.mostRecentWin ? (0, utils_1.formatTimestamp)(stats.mostRecentWin) : "[red]none[]"), - survival: "[#CCFFCC]Highest wave reached: ".concat(stats.highestWave, "\n[#CCFFCC]Average wave reached: ").concat(stats.averageWave, "\n[#CCFFCC]Total runs: ").concat(stats.allRunCount, " (").concat(stats.earlyRTVs, " RTVs)\n[#CCFFCC]RTV rate: ").concat(f.percent(stats.earlyRTVRate, 1), "\n[#CCFFCC]Average duration: ").concat((0, utils_1.formatTime)(stats.averagePlaytime), "\n[#CCFFCC]Longest duration: ").concat((0, utils_1.formatTime)(stats.longestTime)), - pvp: "[#CCFFCC]Total runs: ".concat(stats.allRunCount, " (").concat(stats.earlyRTVs, " RTVs)\n[#CCFFCC]Team win rates: ").concat(Object.entries(stats.teamWinRate).map(function (_c) { - var _d = __read(_c, 2), team = _d[0], rate = _d[1]; - return "".concat(team, " ").concat(f.percent(rate, 1)); - }).join(", "), "\n[#CCFFCC]RTV rate: ").concat(f.percent(stats.earlyRTVRate, 1), "\n[#CCFFCC]Average match duration: ").concat((0, utils_1.formatTime)(stats.averagePlaytime), "\n[#CCFFCC]Shortest match duration: ").concat((0, utils_1.formatTime)(stats.shortestWinTime)), - hexed: "[#CCFFCC]Total runs: ".concat(stats.allRunCount, " (").concat(stats.earlyRTVs, " RTVs)\n[#CCFFCC]RTV rate: ").concat(f.percent(stats.earlyRTVRate, 1), "\n[#CCFFCC]Average match duration: ").concat((0, utils_1.formatTime)(stats.averagePlaytime), "\n[#CCFFCC]Shortest match duration: ").concat((0, utils_1.formatTime)(stats.shortestWinTime)), - sandbox: "[#CCFFCC]Total plays: ".concat(stats.allRunCount, "\n[#CCFFCC]Average play time: ").concat((0, utils_1.formatTime)(stats.averagePlaytime), "\n[#CCFFCC]Shortest play time: ").concat((0, utils_1.formatTime)(stats.shortestTime)), - }, ""); - return ("[coral]".concat(map.name(), "\n[gray](").concat(map.file.name(), ")\n\n[accent]Map by: [white]").concat(map.author(), "\n[accent]Description: [white]").concat(map.description(), "\n[accent]Size: [white]").concat(map.width, "x").concat(map.height, "\n[accent]Last updated: [white]").concat(new Date(map.file.lastModified()).toLocaleDateString(), "\n[accent]BvB allowed: ").concat(f.boolGood(rules.placeRangeCheck), ", unit item transfer allowed: ").concat(f.boolGood(rules.onlyDepositCore), "\n\n").concat(modeSpecificStats, "\n[#CCFFCC]Longest play time: ").concat((0, utils_1.formatTime)(stats.longestTime), "\n[#CCFFCC]Average player count: ").concat(f.number(stats.averageHighestPlayerCount, 1))); - }; - return FMap; - }(_classSuper)), - (function () { - var _c; - var _metadata = typeof Symbol === "function" && Symbol.metadata ? Object.create((_c = _classSuper[Symbol.metadata]) !== null && _c !== void 0 ? _c : null) : void 0; - _static_allMaps_decorators = [(0, io_1.serialize)("fish-map-data", function () { return ["version", 1, ["array", "u16", ["class", FMap, [ - ["runs", ["array", "u32", ["class", FinishedMapRun, [ - ["startTime", ["number", "i64"]], - ["endTime", ["number", "i64"]], - ["maxPlayerCount", ["number", "u8"]], - ["success", ["boolean"]], - ["winTeam", ["team"]], - ["wave", ["number", "u16"]] - ]]]], - ["mapFileName", ["string"]], - ]]]]; }, function () { return ["array", "u16", ["class", FMap, [ - ["runs", ["array", "u32", ["class", FinishedMapRun, [ - ["startTime", ["number", "i64"]], - ["endTime", ["number", "i64"]], - ["maxPlayerCount", ["number", "u8"]], - ["success", ["boolean"]], - ["winTeam", ["team"]], - ]]]], - ["mapFileName", ["string"]], - ]]]; }, undefined, "saveMaps")]; - __esDecorate(null, null, _static_allMaps_decorators, { kind: "field", name: "allMaps", static: true, private: false, access: { has: function (obj) { return "allMaps" in obj; }, get: function (obj) { return obj.allMaps; }, set: function (obj, value) { obj.allMaps = value; } }, metadata: _metadata }, _static_allMaps_initializers, _static_allMaps_extraInitializers); - if (_metadata) Object.defineProperty(_b, Symbol.metadata, { enumerable: true, configurable: true, writable: true, value: _metadata }); - })(), - _b.allMaps = __runInitializers(_b, _static_allMaps_initializers, null), - _b.maps = (__runInitializers(_b, _static_allMaps_extraInitializers), {}), - (function () { - globals_1.FishEvents.on("dataLoaded", function () { - var _c; - //This event listener runs after the data has been loaded into allMaps - ((_c = _b.allMaps) !== null && _c !== void 0 ? _c : (_b.allMaps = [])).forEach(function (map) { - _b.maps[map.mapFileName] = map; - map.runs.forEach(function (run) { - var _c; - //this should not even happen, I think GameOverEvent is sending winTeam as null sometimes?? - (_c = run.winTeam) !== null && _c !== void 0 ? _c : (run.winTeam = Team.derelict); - }); - }); - //create all the data - Vars.maps.customMaps().each(function (m) { return void _b.getCreate(m); }); - }); - })(), - _b; -}(); -exports.FMap = FMap; +"use strict"; +/* +Copyright © BalaM314, 2026. All Rights Reserved. +This file contains the map run tracker and statistics computation. +*/ +var __extends = (this && this.__extends) || (function () { + var extendStatics = function (d, b) { + extendStatics = Object.setPrototypeOf || + ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || + function (d, b) { for (var p in b) if (Object.prototype.hasOwnProperty.call(b, p)) d[p] = b[p]; }; + return extendStatics(d, b); + }; + return function (d, b) { + if (typeof b !== "function" && b !== null) + throw new TypeError("Class extends value " + String(b) + " is not a constructor or null"); + extendStatics(d, b); + function __() { this.constructor = d; } + d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); + }; +})(); +var __esDecorate = (this && this.__esDecorate) || function (ctor, descriptorIn, decorators, contextIn, initializers, extraInitializers) { + function accept(f) { if (f !== void 0 && typeof f !== "function") throw new TypeError("Function expected"); return f; } + var kind = contextIn.kind, key = kind === "getter" ? "get" : kind === "setter" ? "set" : "value"; + var target = !descriptorIn && ctor ? contextIn["static"] ? ctor : ctor.prototype : null; + var descriptor = descriptorIn || (target ? Object.getOwnPropertyDescriptor(target, contextIn.name) : {}); + var _, done = false; + for (var i = decorators.length - 1; i >= 0; i--) { + var context = {}; + for (var p in contextIn) context[p] = p === "access" ? {} : contextIn[p]; + for (var p in contextIn.access) context.access[p] = contextIn.access[p]; + context.addInitializer = function (f) { if (done) throw new TypeError("Cannot add initializers after decoration has completed"); extraInitializers.push(accept(f || null)); }; + var result = (0, decorators[i])(kind === "accessor" ? { get: descriptor.get, set: descriptor.set } : descriptor[key], context); + if (kind === "accessor") { + if (result === void 0) continue; + if (result === null || typeof result !== "object") throw new TypeError("Object expected"); + if (_ = accept(result.get)) descriptor.get = _; + if (_ = accept(result.set)) descriptor.set = _; + if (_ = accept(result.init)) initializers.unshift(_); + } + else if (_ = accept(result)) { + if (kind === "field") initializers.unshift(_); + else descriptor[key] = _; + } + } + if (target) Object.defineProperty(target, contextIn.name, descriptor); + done = true; +}; +var __runInitializers = (this && this.__runInitializers) || function (thisArg, initializers, value) { + var useValue = arguments.length > 2; + for (var i = 0; i < initializers.length; i++) { + value = useValue ? initializers[i].call(thisArg, value) : initializers[i].call(thisArg); + } + return useValue ? value : void 0; +}; +var __read = (this && this.__read) || function (o, n) { + var m = typeof Symbol === "function" && o[Symbol.iterator]; + if (!m) return o; + var i = m.call(o), r, ar = [], e; + try { + while ((n === void 0 || n-- > 0) && !(r = i.next()).done) ar.push(r.value); + } + catch (error) { e = { error: error }; } + finally { + try { + if (r && !r.done && (m = i["return"])) m.call(i); + } + finally { if (e) throw e.error; } + } + return ar; +}; +var _this = this; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.FMap = exports.PartialMapRun = exports.FinishedMapRun = void 0; +var config_1 = require("/config"); +var io_1 = require("/frameworks/io"); +var funcs_1 = require("/funcs"); +var globals_1 = require("/globals"); +var utils_1 = require("/utils"); +var FinishedMapRun = /** @class */ (function (_super) { + __extends(FinishedMapRun, _super); + //this constructor is useless, but rhino crashes with a bizarre error when trying to run the emitted code + //do not remove this useless constructor + function FinishedMapRun(data) { + return _super.call(this, data) || this; + } + FinishedMapRun.prototype.duration = function () { + return this.endTime - this.startTime; + }; + FinishedMapRun.prototype.outcome = function () { + if (config_1.Gamemode.pvp()) { + if (this.winTeam === Team.derelict) { + if (this.duration() > funcs_1.Duration.minutes(20)) + return ["rtv", "late rtv"]; + else + return ["rtv", "early rtv"]; + } + else + return ["win", "win"]; + } + else { + if (this.success) + return ["win", "win"]; + else if (this.winTeam === Team.derelict) { + if (this.duration() > funcs_1.Duration.minutes(3)) + return ["loss", "late rtv"]; + else + return ["rtv", "early rtv"]; + } + else + return ["loss", "loss"]; + } + }; + return FinishedMapRun; +}((0, io_1.dataClass)())); +exports.FinishedMapRun = FinishedMapRun; +var PartialMapRun = /** @class */ (function () { + function PartialMapRun() { + this.startTime = Date.now(); + this.maxPlayerCount = 0; + } + /** In milliseconds */ + PartialMapRun.prototype.duration = function () { + return Date.now() - this.startTime; + }; + PartialMapRun.prototype.update = function () { + this.maxPlayerCount = Math.max(this.maxPlayerCount, Groups.player.size()); + }; + PartialMapRun.prototype.finish = function (_b) { + var winTeam = _b.winTeam; + return new FinishedMapRun({ + winTeam: winTeam, + success: config_1.Gamemode.pvp() ? true : winTeam == Vars.state.rules.defaultTeam, + startTime: this.startTime, + endTime: Date.now(), + maxPlayerCount: this.maxPlayerCount, + wave: Vars.state.wave, + }); + }; + //Used for continuing through a restart + PartialMapRun.prototype.write = function () { + return "".concat(Date.now() - this.startTime, "/").concat(this.maxPlayerCount); + }; + PartialMapRun.read = function (data) { + var _b = __read(data.split("/").map(Number), 2), duration = _b[0], maxPlayerCount = _b[1]; + if (isNaN(duration) || isNaN(maxPlayerCount)) { + Log.err("_FINDTAG_ failed to load map run stats data: ".concat(data)); + } + var out = new _a(); + out.startTime = Date.now() - duration; //move start time forward by time when the server was off + out.maxPlayerCount = maxPlayerCount; + return out; + }; + var _a; + _a = PartialMapRun; + PartialMapRun.key = "fish-partial-map-run"; + PartialMapRun.current = null; + (function () { + globals_1.FishEvents.on("saveData", function () { + if (_a.current) + Core.settings.put(_a.key, _a.current.write()); + }); + globals_1.FishEvents.on("loadData", function () { + var data = Core.settings.getString(_a.key); + if (data) { + _a.current = _a.read(data); + } + else { + //loading a map, but there is no run information, create one + _a.current = new _a(); + } + }); + Events.on(EventType.SaveLoadEvent, function (e) { + var _b; + //create a new run, if there isn't one already + //loadData will have run first if it is a server restart + (_b = _a.current) !== null && _b !== void 0 ? _b : (_a.current = new _a()); + }); + Timer.schedule(function () { + var _b; + (_b = _a.current) === null || _b === void 0 ? void 0 : _b.update(); + }, 0, 5); + Events.on(EventType.GameOverEvent, function (e) { + var _b; + if (_a.current) { + var finishedRun = _a.current.finish({ winTeam: (_b = e.winner) !== null && _b !== void 0 ? _b : Team.derelict }); + var fmap = FMap.getCreate(Vars.state.map); + if (!fmap) + return; + //Highscore message + if (config_1.Gamemode.attack() && finishedRun.success) { + var bestPreviousTime = fmap.stats().shortestWinTime; + var duration = finishedRun.duration(); + Call.sendMessage("[orange]--------\n".concat(finishedRun.success && duration < bestPreviousTime ? + "[green]New highscore! Map completed in [accent]".concat((0, utils_1.formatTimeShort)(duration), "[]") + : "[orange]Map completed in [accent]".concat((0, utils_1.formatTimeShort)(duration), "[]. Current highscore: [green]").concat((0, utils_1.formatTimeShort)(bestPreviousTime), "[]"), "\n[orange]--------")); + } + else if (config_1.Gamemode.survival()) { + var bestPreviousWave = fmap.stats().highestWave; + var wave = finishedRun.wave; + Call.sendMessage("[orange]--------\n".concat(finishedRun.success && wave < bestPreviousWave ? + "[green]New highscore! Reached wave [accent]".concat(wave, "[].") + : "[orange]Reached wave [accent]".concat(wave, "[]. Current highscore: [green]").concat(bestPreviousWave, "[]"), "\n[orange]--------")); + } + fmap.runs.push(finishedRun); + globals_1.FishEvents.fire("saveMaps", []); + } + Core.settings.remove(_a.key); + _a.current = null; + }); + })(); + return PartialMapRun; +}()); +exports.PartialMapRun = PartialMapRun; +var FMap = function () { + var _b; + var _classSuper = (0, io_1.dataClass)(); + var _static_allMaps_decorators; + var _static_allMaps_initializers = []; + var _static_allMaps_extraInitializers = []; + return _b = /** @class */ (function (_super) { + __extends(FMap, _super); + function FMap(data, + //O(n^2)... should be fine? + map) { + if (map === void 0) { map = Vars.maps.customMaps().find(function (m) { return m.file.name() === data.mapFileName; }); } + var _this = _super.call(this, data) || this; + _this.map = map; + return _this; + } + FMap.getCreate = function (map) { + if (this.allMaps == null) + return null; + var mapFileName = map.file.name(); + if (Object.prototype.hasOwnProperty.call(this.maps, mapFileName)) + return this.maps[mapFileName]; + var fmap = new this({ + runs: [], + mapFileName: mapFileName + }, map); + this.maps[mapFileName] = fmap; + this.allMaps.push(fmap); + return fmap; + }; + FMap.prototype.rules = function () { + var _c; + return (_c = this.map) === null || _c === void 0 ? void 0 : _c.rules(); + }; + FMap.prototype.stats = function () { + var _c; + var runs = this.runs.filter(function (r) { return r.maxPlayerCount > 0; }); //Remove all runs with no players on + var allRunCount = runs.length; + var victories = runs.filter(function (r) { return r.outcome()[1] === "win"; }); + var losses = runs.filter(function (r) { return r.outcome()[0] === "loss"; }).length; + var earlyRTVs = runs.filter(function (r) { return r.outcome()[1] === "early rtv"; }).length; + var lateRTVs = runs.filter(function (r) { return r.outcome()[1] === "late rtv"; }).length; + var significantRunCount = allRunCount - earlyRTVs; + var totalLosses = losses + lateRTVs; + var durations = runs.filter(function (r) { return r.outcome()[0] !== "rtv"; }).map(function (r) { return r.duration(); }); + var durationStats = (0, funcs_1.computeStatistics)(durations); + var winDurationStats = (0, funcs_1.computeStatistics)(runs.filter(function (r) { return r.outcome()[0] === "win"; }).map(function (r) { return r.duration(); })); + var teamWins = runs.filter(function (r) { return r.outcome()[1] !== "early rtv"; }).reduce(function (acc, item) { + var _c; + acc[item.winTeam.name] = ((_c = acc[item.winTeam.name]) !== null && _c !== void 0 ? _c : 0) + 1; + return acc; + }, {}); + var teamWinRate = Object.fromEntries(Object.entries(teamWins).map(function (_c) { + var _d = __read(_c, 2), team = _d[0], wins = _d[1]; + return [team, wins / significantRunCount]; + })); + //Remove runs that were on wave 0, due to a silly bug we have thousands of runs with a max wave of 0 + var waveStats = (0, funcs_1.computeStatistics)(runs.filter(function (r) { return r.outcome()[0] !== "rtv" && r.wave !== 0; }).map(function (r) { return r.wave; })); + return { + allRunCount: allRunCount, + significantRunCount: significantRunCount, + victories: victories.length, + losses: losses, + totalLosses: totalLosses, + earlyRTVs: earlyRTVs, + lateRTVs: lateRTVs, + earlyRTVRate: earlyRTVs / allRunCount, + winRate: victories.length / significantRunCount, + lossRate: losses / significantRunCount, + averagePlaytime: durationStats.average, + shortestWinTime: winDurationStats.lowest, + longestTime: durationStats.highest, + shortestTime: durationStats.lowest, + averageHighestPlayerCount: (0, funcs_1.computeStatistics)(runs.map(function (r) { return r.maxPlayerCount; })).average, + teamWins: teamWins, + teamWinRate: teamWinRate, + highestWave: waveStats.highest, + averageWave: waveStats.average, + mostRecentWin: (_c = victories.at(-1)) === null || _c === void 0 ? void 0 : _c.startTime + }; + }; + FMap.prototype.displayStats = function (f) { + var map = this.map; + if (!map) + return null; + var stats = this.stats(); + var rules = this.rules(); + var modeSpecificStats = (0, utils_1.match)(config_1.Gamemode.name(), { + attack: "[#CCFFCC]Total runs: ".concat(stats.allRunCount, " (").concat(stats.victories, " wins, ").concat(stats.totalLosses, " losses, ").concat(stats.earlyRTVs, " RTVs)\n[#CCFFCC]Outcomes: ").concat(f.percent(stats.winRate, 1), " wins, ").concat(f.percent(stats.lossRate, 1), " losses, ").concat(f.percent(stats.earlyRTVRate, 1), " RTVs\n[#CCFFCC]Average playtime: ").concat((0, utils_1.formatTime)(stats.averagePlaytime), "\n[#CCFFCC]Shortest win time: ").concat((0, utils_1.formatTime)(stats.shortestWinTime), "\n[#CCFFCC]Most recent win: ").concat(stats.mostRecentWin ? (0, utils_1.formatTimestamp)(stats.mostRecentWin) : "[red]none[]"), + survival: "[#CCFFCC]Highest wave reached: ".concat(stats.highestWave, "\n[#CCFFCC]Average wave reached: ").concat(stats.averageWave, "\n[#CCFFCC]Total runs: ").concat(stats.allRunCount, " (").concat(stats.earlyRTVs, " RTVs)\n[#CCFFCC]RTV rate: ").concat(f.percent(stats.earlyRTVRate, 1), "\n[#CCFFCC]Average duration: ").concat((0, utils_1.formatTime)(stats.averagePlaytime), "\n[#CCFFCC]Longest duration: ").concat((0, utils_1.formatTime)(stats.longestTime)), + pvp: "[#CCFFCC]Total runs: ".concat(stats.allRunCount, " (").concat(stats.earlyRTVs, " RTVs)\n[#CCFFCC]Team win rates: ").concat(Object.entries(stats.teamWinRate).map(function (_c) { + var _d = __read(_c, 2), team = _d[0], rate = _d[1]; + return "".concat(team, " ").concat(f.percent(rate, 1)); + }).join(", "), "\n[#CCFFCC]RTV rate: ").concat(f.percent(stats.earlyRTVRate, 1), "\n[#CCFFCC]Average match duration: ").concat((0, utils_1.formatTime)(stats.averagePlaytime), "\n[#CCFFCC]Shortest match duration: ").concat((0, utils_1.formatTime)(stats.shortestWinTime)), + hexed: "[#CCFFCC]Total runs: ".concat(stats.allRunCount, " (").concat(stats.earlyRTVs, " RTVs)\n[#CCFFCC]RTV rate: ").concat(f.percent(stats.earlyRTVRate, 1), "\n[#CCFFCC]Average match duration: ").concat((0, utils_1.formatTime)(stats.averagePlaytime), "\n[#CCFFCC]Shortest match duration: ").concat((0, utils_1.formatTime)(stats.shortestWinTime)), + sandbox: "[#CCFFCC]Total plays: ".concat(stats.allRunCount, "\n[#CCFFCC]Average play time: ").concat((0, utils_1.formatTime)(stats.averagePlaytime), "\n[#CCFFCC]Shortest play time: ").concat((0, utils_1.formatTime)(stats.shortestTime)), + }, ""); + return ("[coral]".concat(map.name(), "\n[gray](").concat(map.file.name(), ")\n\n[accent]Map by: [white]").concat(map.author(), "\n[accent]Description: [white]").concat(map.description(), "\n[accent]Size: [white]").concat(map.width, "x").concat(map.height, "\n[accent]Last updated: [white]").concat(new Date(map.file.lastModified()).toLocaleDateString(), "\n[accent]BvB allowed: ").concat(f.boolGood(rules.placeRangeCheck), ", unit item transfer allowed: ").concat(f.boolGood(rules.onlyDepositCore), "\n\n").concat(modeSpecificStats, "\n[#CCFFCC]Longest play time: ").concat((0, utils_1.formatTime)(stats.longestTime), "\n[#CCFFCC]Average player count: ").concat(f.number(stats.averageHighestPlayerCount, 1))); + }; + return FMap; + }(_classSuper)), + (function () { + var _c; + var _metadata = typeof Symbol === "function" && Symbol.metadata ? Object.create((_c = _classSuper[Symbol.metadata]) !== null && _c !== void 0 ? _c : null) : void 0; + _static_allMaps_decorators = [(0, io_1.serialize)("fish-map-data", function () { return ["version", 1, ["array", "u16", ["class", FMap, [ + ["runs", ["array", "u32", ["class", FinishedMapRun, [ + ["startTime", ["number", "i64"]], + ["endTime", ["number", "i64"]], + ["maxPlayerCount", ["number", "u8"]], + ["success", ["boolean"]], + ["winTeam", ["team"]], + ["wave", ["number", "u16"]] + ]]]], + ["mapFileName", ["string"]], + ]]]]; }, function () { return ["array", "u16", ["class", FMap, [ + ["runs", ["array", "u32", ["class", FinishedMapRun, [ + ["startTime", ["number", "i64"]], + ["endTime", ["number", "i64"]], + ["maxPlayerCount", ["number", "u8"]], + ["success", ["boolean"]], + ["winTeam", ["team"]], + ]]]], + ["mapFileName", ["string"]], + ]]]; }, undefined, "saveMaps")]; + __esDecorate(null, null, _static_allMaps_decorators, { kind: "field", name: "allMaps", static: true, private: false, access: { has: function (obj) { return "allMaps" in obj; }, get: function (obj) { return obj.allMaps; }, set: function (obj, value) { obj.allMaps = value; } }, metadata: _metadata }, _static_allMaps_initializers, _static_allMaps_extraInitializers); + if (_metadata) Object.defineProperty(_b, Symbol.metadata, { enumerable: true, configurable: true, writable: true, value: _metadata }); + })(), + _b.allMaps = __runInitializers(_b, _static_allMaps_initializers, null), + _b.maps = (__runInitializers(_b, _static_allMaps_extraInitializers), {}), + (function () { + globals_1.FishEvents.on("dataLoaded", function () { + var _c; + //This event listener runs after the data has been loaded into allMaps + ((_c = _b.allMaps) !== null && _c !== void 0 ? _c : (_b.allMaps = [])).forEach(function (map) { + _b.maps[map.mapFileName] = map; + map.runs.forEach(function (run) { + var _c; + //this should not even happen, I think GameOverEvent is sending winTeam as null sometimes?? + (_c = run.winTeam) !== null && _c !== void 0 ? _c : (run.winTeam = Team.derelict); + }); + }); + //create all the data + Vars.maps.customMaps().each(function (m) { return void _b.getCreate(m); }); + }); + })(), + _b; +}(); +exports.FMap = FMap; diff --git a/build/scripts/metrics.js b/build/scripts/metrics.js index 5c77f587..21a755cb 100644 --- a/build/scripts/metrics.js +++ b/build/scripts/metrics.js @@ -1,76 +1,76 @@ -"use strict"; -/* -Copyright © BalaM314, 2026. All Rights Reserved. -This file contains the player count tracking. -*/ -Object.defineProperty(exports, "__esModule", { value: true }); -exports.Metrics = void 0; -var Metrics = /** @class */ (function () { - function Metrics() { - } - Metrics.weekNumber = function (date) { - if (date === void 0) { date = Date.now(); } - return Math.floor((date - this.startDate) / this.millisPerWeek); - }; - Metrics.readingNumber = function (date) { - if (date === void 0) { date = Date.now(); } - return Math.floor(((date - this.startDate) % this.millisPerWeek) / this.millisBetweenReadings); - }; - Metrics.newWeek = function () { - return Array(2520).fill(this.noData); - }; - Metrics.currentWeek = function () { - var _b; - var _c, _d; - return (_b = (_c = this.weeks)[_d = this.weekNumber()]) !== null && _b !== void 0 ? _b : (_c[_d] = this.newWeek()); - }; - Metrics.update = function () { - Time.mark(); - var playerCount = Groups.player.size(); - this.currentWeek()[this.readingNumber()] = - Math.max(playerCount, this.currentWeek()[this.readingNumber()]); - Log.debug("metrics update @", Time.elapsed()); - }; - Metrics.exportRange = function (startDate, endDate) { - var _this = this; - if (startDate === void 0) { startDate = this.startDate; } - if (endDate === void 0) { endDate = Date.now(); } - if (typeof startDate !== "number") - throw new Error('startDate should be a number'); - var startWeek = this.weekNumber(startDate); - var endWeek = this.weekNumber(endDate); - return this.weeks.slice(startWeek, endWeek + 1).map(function (week, weekNumber) { - return week.filter(function (v) { return v >= 0; }).map(function (v, i) { return [ - v, - _this.startDate + - weekNumber * _this.millisPerWeek + - i * _this.millisBetweenReadings - ]; }); - }).flat(); - }; - var _a; - _a = Metrics; - /** 4 May 2025 */ - Metrics.startDate = new Date(2025, 4, 4).getTime(); - Metrics.millisPerWeek = 604800000; - Metrics.millisBetweenReadings = 240000; - Metrics.noData = -1; - /** - * Weeks are numbered starting at the week of 4 May 2025. - * A value is taken every 4 minutes, for a total of 15 readings per hour. - */ - // @serialize("player-count-data", () => ["version", 0, - // ["array", "u16", ["array", 2520, ["number", "i8"]]] - // ], undefined, weeks => { - // for(let i = 0; i <= Metrics.weekNumber(); i ++){ - // weeks[i] ??= Metrics.newWeek(); - // } - // return weeks; - // }) - Metrics.weeks = Array(_a.weekNumber() + 1).fill(0).map(function () { return _a.newWeek(); }); - (function () { - Timer.schedule(function () { return _a.update(); }, 15, 60); - })(); - return Metrics; -}()); -exports.Metrics = Metrics; +"use strict"; +/* +Copyright © BalaM314, 2026. All Rights Reserved. +This file contains the player count tracking. +*/ +Object.defineProperty(exports, "__esModule", { value: true }); +exports.Metrics = void 0; +var Metrics = /** @class */ (function () { + function Metrics() { + } + Metrics.weekNumber = function (date) { + if (date === void 0) { date = Date.now(); } + return Math.floor((date - this.startDate) / this.millisPerWeek); + }; + Metrics.readingNumber = function (date) { + if (date === void 0) { date = Date.now(); } + return Math.floor(((date - this.startDate) % this.millisPerWeek) / this.millisBetweenReadings); + }; + Metrics.newWeek = function () { + return Array(2520).fill(this.noData); + }; + Metrics.currentWeek = function () { + var _b; + var _c, _d; + return (_b = (_c = this.weeks)[_d = this.weekNumber()]) !== null && _b !== void 0 ? _b : (_c[_d] = this.newWeek()); + }; + Metrics.update = function () { + Time.mark(); + var playerCount = Groups.player.size(); + this.currentWeek()[this.readingNumber()] = + Math.max(playerCount, this.currentWeek()[this.readingNumber()]); + Log.debug("metrics update @", Time.elapsed()); + }; + Metrics.exportRange = function (startDate, endDate) { + var _this = this; + if (startDate === void 0) { startDate = this.startDate; } + if (endDate === void 0) { endDate = Date.now(); } + if (typeof startDate !== "number") + throw new Error('startDate should be a number'); + var startWeek = this.weekNumber(startDate); + var endWeek = this.weekNumber(endDate); + return this.weeks.slice(startWeek, endWeek + 1).map(function (week, weekNumber) { + return week.filter(function (v) { return v >= 0; }).map(function (v, i) { return [ + v, + _this.startDate + + weekNumber * _this.millisPerWeek + + i * _this.millisBetweenReadings + ]; }); + }).flat(); + }; + var _a; + _a = Metrics; + /** 4 May 2025 */ + Metrics.startDate = new Date(2025, 4, 4).getTime(); + Metrics.millisPerWeek = 604800000; + Metrics.millisBetweenReadings = 240000; + Metrics.noData = -1; + /** + * Weeks are numbered starting at the week of 4 May 2025. + * A value is taken every 4 minutes, for a total of 15 readings per hour. + */ + // @serialize("player-count-data", () => ["version", 0, + // ["array", "u16", ["array", 2520, ["number", "i8"]]] + // ], undefined, weeks => { + // for(let i = 0; i <= Metrics.weekNumber(); i ++){ + // weeks[i] ??= Metrics.newWeek(); + // } + // return weeks; + // }) + Metrics.weeks = Array(_a.weekNumber() + 1).fill(0).map(function () { return _a.newWeek(); }); + (function () { + Timer.schedule(function () { return _a.update(); }, 15, 60); + })(); + return Metrics; +}()); +exports.Metrics = Metrics; diff --git a/build/scripts/mindustryTypes.js b/build/scripts/mindustryTypes.js index 4d9bf344..5c879b56 100644 --- a/build/scripts/mindustryTypes.js +++ b/build/scripts/mindustryTypes.js @@ -1,12 +1,12 @@ -"use strict"; -/* -Copyright © BalaM314, 2026. All Rights Reserved. -This file contains TypeScript type definitions for Mindustry's code. -Mindustry is written in Java, which has strong types. -Mindustry supports loading Javascript, which does not have types. -Javascript will have access to Mindustry's functions, which have types. -We are writing Typescript, which does have types. We are able to call Mindustry's functions, but because those are written in Java we cannot directly use those types. -This file contains some of those type definitions, ported over from the Java definitions. -*/ -//this is fine -Object.defineProperty(exports, "__esModule", { value: true }); +"use strict"; +/* +Copyright © BalaM314, 2026. All Rights Reserved. +This file contains TypeScript type definitions for Mindustry's code. +Mindustry is written in Java, which has strong types. +Mindustry supports loading Javascript, which does not have types. +Javascript will have access to Mindustry's functions, which have types. +We are writing Typescript, which does have types. We are able to call Mindustry's functions, but because those are written in Java we cannot directly use those types. +This file contains some of those type definitions, ported over from the Java definitions. +*/ +//this is fine +Object.defineProperty(exports, "__esModule", { value: true }); diff --git a/build/scripts/packetHandlers.js b/build/scripts/packetHandlers.js index c8c49be4..0de90069 100644 --- a/build/scripts/packetHandlers.js +++ b/build/scripts/packetHandlers.js @@ -1,281 +1,281 @@ -"use strict"; -/* -Copyright © BalaM314, 2026. All Rights Reserved. -This file contains the visual effects system. -Original contributor: @author TheEt1234 -Fixes: @author BalaM314 -Fixes: @author Dart25 -Fixes: @author Jurorno9 -*/ -Object.defineProperty(exports, "__esModule", { value: true }); -exports.commands = void 0; -exports.loadPacketHandlers = loadPacketHandlers; -exports.bulkInfoMsg = bulkInfoMsg; -var commands_1 = require("/frameworks/commands"); -var players_1 = require("/players"); -//some much needed restrictions -/** point in which effects will refuse to render */ -var MIN_EFFECT_TPS = 20; -/** maximum duration for user-created labels (seconds) */ -var MAX_LABEL_TIME = 20; -//info tracker -var lastLabel = ''; -var lastAccessedBulkLabel = null; -var lastAccessedLabel = null; -var lastAccessedBulkLine = null; -var lastAccessedLine = null; -var bulkLimit = 1000; -var noPermissionText = "[red]You don't have permission to use this packet."; -var invalidContentText = '[red]Invalid label content.'; -var tooLongText = '[red]Bulk content length exceeded, please use fewer effects.'; -var bulkSeparator = '|'; -var procError = '[red]An error occured while processing your request.'; -var invalidReq = '[red]Invalid request. Please consult the documentation.'; -var lowTPSError = '[red]Low server TPS, skipping request.'; -var tmpLinePacket = new EffectCallPacket2(); -var tmpLabelPacket = new LabelReliableCallPacket(); -function loadPacketHandlers() { - //initialize line packet - tmpLinePacket.effect = Fx.pointBeam; - tmpLinePacket.rotation = 0.0; - tmpLinePacket.color = Tmp.c1; - tmpLinePacket.data = Tmp.v1; - //labels - //fmt: "content,duration,x,y" - Vars.netServer.addPacketHandler('label', function (player, content) { - var p = players_1.FishPlayer.get(player); - try { - if (Core.graphics.getFramesPerSecond() < MIN_EFFECT_TPS) { - p.sendMessage(lowTPSError, 1000); - return; - } - if (!p.hasPerm("visualEffects")) { - p.sendMessage(noPermissionText, 1000); - return; - } - lastAccessedLabel = p; - handleLabel(player, content, true); - } - catch (_a) { - p.sendMessage(procError, 1000); - } - }); - Vars.netServer.addPacketHandler('bulkLabel', function (player, content) { - var p = players_1.FishPlayer.get(player); - try { - if (Core.graphics.getFramesPerSecond() < MIN_EFFECT_TPS) { - p.sendMessage(lowTPSError, 1000); - return; - } - if (!p.hasPerm('bulkVisualEffects')) { - p.sendMessage(noPermissionText, 1000); - return; - } - lastAccessedBulkLabel = p; - //get individual labels - var labels = []; - var inQuotes = false; - var startIdx = 0; - for (var i = 0; i < content.length; i++) { - switch (content[i]) { - case '"': - if (i > 0 && content[i - 1] == '\\') - break; - inQuotes = !inQuotes; - break; - //separate - case bulkSeparator: - if (inQuotes) - break; - labels.push(content.substring(startIdx, i)); - startIdx = i + 1; - break; - default: - break; - } - } - //last label - if (startIdx < content.length) { - labels.push(content.substring(startIdx, content.length - 1)); - } - if (labels.length > bulkLimit) { - p.sendMessage(tooLongText, 1000); - return; - } - //display labels - // eslint-disable-next-line @typescript-eslint/prefer-for-of - for (var i = 0; i < labels.length; i++) { - var label = labels[i]; - if (label.trim().length <= 0) - continue; - if (!handleLabel(player, label, false)) - return; - } - } - catch (_a) { - p.sendMessage(procError, 1000); - } - }); - //lines - Vars.netServer.addPacketHandler('lineEffect', function (player, content) { - var p = players_1.FishPlayer.get(player); - try { - if (Core.graphics.getFramesPerSecond() < MIN_EFFECT_TPS) { - p.sendMessage(lowTPSError, 1000); - return; - } - if (!p.hasPerm("visualEffects")) { - p.sendMessage(noPermissionText, 1000); - return; - } - if (!handleLine(content, player)) - return; - lastAccessedLine = p; - } - catch (_a) { - p.sendMessage(procError, 1000); - } - }); - //this is the silas effect but it's way too real - Vars.netServer.addPacketHandler('bulkLineEffect', function (player, content) { - var p = players_1.FishPlayer.get(player); - if (Core.graphics.getFramesPerSecond() < MIN_EFFECT_TPS) { - p.sendMessage(lowTPSError, 1000); - return; - } - if (!p.hasPerm('bulkVisualEffects')) { - p.sendMessage(noPermissionText, 1000); - return; - } - try { - var lines = content.split(bulkSeparator); - if (lines.length > bulkLimit) { - p.sendMessage(tooLongText, 1000); - return; - } - // eslint-disable-next-line @typescript-eslint/prefer-for-of - for (var i = 0; i < lines.length; i++) { - var line = lines[i]; - if (line.trim().length <= 0) - continue; - if (!handleLine(line, player)) - return; - } - lastAccessedBulkLine = p; - } - catch (_a) { - p.sendMessage(procError, 1000); - } - }); -} -//commands -exports.commands = (0, commands_1.commandList)({ - pklast: { - args: [], - description: 'Tells you who last accessed the packet handlers.', - perm: commands_1.Perm.none, - handler: function (_a) { - var output = _a.output; - var outputLines = []; - if (lastAccessedLabel && lastLabel) { - outputLines.push("".concat(lastAccessedLabel.name, "[white] created label \"").concat(lastLabel, "\".")); - } - if (lastAccessedBulkLabel) { - outputLines.push("".concat(lastAccessedBulkLabel.name, "[white] last used the bulk label effect.")); - } - if (lastAccessedLine) { - outputLines.push("".concat(lastAccessedLine.name, "[white] last used the line effect.")); - } - if (lastAccessedBulkLine) { - outputLines.push("".concat(lastAccessedBulkLine.name, "[white] last used the bulk line effect.")); - } - output(outputLines.length > 0 ? outputLines.join('\n') : 'No packet handlers have been accessed yet.'); - } - }, - pkdocs: { - description: 'Packet handler documentation.', - args: [], - perm: commands_1.Perm.none, - handler: function (_a) { - var sender = _a.sender, output = _a.output; - output("\t\t\t\t[blue]FISH[white] Packet Handler Docs\n[white]Usage:[accent]\n\t- Run the javascript function \"Call.serverPacketReliable()\" to send these. (!js in foos)\n\t- You need to multiply world coordinates by Vars.tilesize (8) for things to work properly. This is a relic from the v3 days where every tile was 8 pixels.\n\n[white]Packet types[accent]:\n\t- Line effect: \"lineEffect\", \"x0,y0,x1,y1,hexColor\" (for example \"20.7,19.3,50.4,28.9,#FF0000\")\n\t- Bulk line effect: \"bulkLineEffect\", equivalent to multiple lineEffect packets, with every line separated by a '|' symbol.\n\t- Label effect: \"label\", \"content,duration,x,y\" (for example \"\"Hi!\",10,20,28\")\n\t- Bulk label effect: \"bulkLabel\", equivalent to multiple label packets, with every label separated by a '|' symbol.\n\n[white]Limitations[accent]:\n\t- You ".concat((sender.hasPerm('bulkVisualEffects') ? ("[green]have been granted[accent]") : ("[red]do not have[accent]")), " access to bulk effects.\n\t- Effects will no longer be drawn at ").concat(MIN_EFFECT_TPS, " for server preformance.\n\t- Labels cannot last longer than ").concat(MAX_LABEL_TIME, " seconds.\n\t- There is a set ratelimit for sending packets, be careful ...\n\n[white]Starter Example[accent]:\n\n\tTo place a label saying \"hello\" at (0,0);\n\tFoos users: [lightgray]!js Call.serverPacketReliable(\"label\", [\"\\\"hello\\\"\", 10, 0, 0].join(\",\"))[accent]\n\tnewConsole users: [lightgrey]Call.serverPacketReliable(\"label\", [\"hello\", 10, 0, 10].join(\",\"))[accent]\n\n[white]Comments and Credits[accent]:\n\t- 'These packet handlers and everything related to them were made by [green]frog[accent].\n\t- 'The code style when submitted was beyond drunk... but it worked... barely' -BalaM314\n\t- \"worst error handling i have ever seen, why kick the player???\" -ASimpleBeginner'\n\t- Most of the code was rewritten in 2024 by [#6e00fb]D[#9e15de]a[#cd29c2]r[#fd3ea5]t[accent].'\n\t- Small tweaks by [#00cf]s[#00bf]w[#009f]a[#007f]m[#005f]p[accent]")); - } - } -}); -//#region utils -function findEndQuote(content, startPos) { - if (content[startPos] != '"') { - //not a start quote?? - return -1; - } - for (var i = startPos + 1; i < content.length; i++) { - if (content[i] == '"' && (i < 1 || content[i - 1] != '\\')) { - return i; - } - } - return -1; -} -function handleLabel(player, content, isSingle) { - var endPos = findEndQuote(content, 0); - if (endPos == -1) { - //invalid content - player.sendMessage(invalidContentText); - return false; - } - //label, clean up \"s - var message = content.substring(1, endPos).replace('\\"', '"'); - var parts = content.substring(endPos + 2).split(','); - if (parts.length != 3) { //dur,x,y - player.sendMessage(invalidReq); - return false; - } - if (isSingle && Strings.stripColors(message).length > 150) { - player.sendMessage('Label too large. Maximum is 150 characters, not including color tags.'); - } - if (isSingle) { - lastLabel = message; - } - var duration = Number(parts[0]); - var x = Number(parts[1]), y = Number(parts[2]); - if (Number.isNaN(duration) || duration > MAX_LABEL_TIME || Number.isNaN(x) || Number.isNaN(y)) { - player.sendMessage(invalidReq); - return false; - } - /*Call.labelReliable( - message, //message - Number(parts[0]), //duration - Number(parts[1]), //x - Number(parts[2]) //y - );*/ - tmpLabelPacket.message = message; - tmpLabelPacket.duration = duration; - tmpLabelPacket.worldx = x; - tmpLabelPacket.worldy = y; - Vars.net.send(tmpLabelPacket, false); - return true; -} -function handleLine(content, player) { - var parts = content.split(','); - if (parts.length != 5) { //x0,y0,x1,y1,color - player.sendMessage(invalidReq); - return false; - } - Tmp.v1.set(Number(parts[2]), Number(parts[3])); //x1,y1 - Color.valueOf(Tmp.c1, parts[4]); //color - /*Call.effect( - Fx.pointBeam, - Number(parts[0]), Number(parts[1]), //x,y - 0, Tmp.c1, //color - Tmp.v1 //x1,y1 - );*/ - tmpLinePacket.x = Number(parts[0]); - tmpLinePacket.y = Number(parts[1]); - Vars.net.send(tmpLinePacket, false); - return true; -} -function bulkInfoMsg(messages, conn) { - for (var i = messages.length - 1; i >= 0; i--) { - Call.infoMessage(conn, messages[i]); - } -} -//#endregion +"use strict"; +/* +Copyright © BalaM314, 2026. All Rights Reserved. +This file contains the visual effects system. +Original contributor: @author TheEt1234 +Fixes: @author BalaM314 +Fixes: @author Dart25 +Fixes: @author Jurorno9 +*/ +Object.defineProperty(exports, "__esModule", { value: true }); +exports.commands = void 0; +exports.loadPacketHandlers = loadPacketHandlers; +exports.bulkInfoMsg = bulkInfoMsg; +var commands_1 = require("/frameworks/commands"); +var players_1 = require("/players"); +//some much needed restrictions +/** point in which effects will refuse to render */ +var MIN_EFFECT_TPS = 20; +/** maximum duration for user-created labels (seconds) */ +var MAX_LABEL_TIME = 20; +//info tracker +var lastLabel = ''; +var lastAccessedBulkLabel = null; +var lastAccessedLabel = null; +var lastAccessedBulkLine = null; +var lastAccessedLine = null; +var bulkLimit = 1000; +var noPermissionText = "[red]You don't have permission to use this packet."; +var invalidContentText = '[red]Invalid label content.'; +var tooLongText = '[red]Bulk content length exceeded, please use fewer effects.'; +var bulkSeparator = '|'; +var procError = '[red]An error occured while processing your request.'; +var invalidReq = '[red]Invalid request. Please consult the documentation.'; +var lowTPSError = '[red]Low server TPS, skipping request.'; +var tmpLinePacket = new EffectCallPacket2(); +var tmpLabelPacket = new LabelReliableCallPacket(); +function loadPacketHandlers() { + //initialize line packet + tmpLinePacket.effect = Fx.pointBeam; + tmpLinePacket.rotation = 0.0; + tmpLinePacket.color = Tmp.c1; + tmpLinePacket.data = Tmp.v1; + //labels + //fmt: "content,duration,x,y" + Vars.netServer.addPacketHandler('label', function (player, content) { + var p = players_1.FishPlayer.get(player); + try { + if (Core.graphics.getFramesPerSecond() < MIN_EFFECT_TPS) { + p.sendMessage(lowTPSError, 1000); + return; + } + if (!p.hasPerm("visualEffects")) { + p.sendMessage(noPermissionText, 1000); + return; + } + lastAccessedLabel = p; + handleLabel(player, content, true); + } + catch (_a) { + p.sendMessage(procError, 1000); + } + }); + Vars.netServer.addPacketHandler('bulkLabel', function (player, content) { + var p = players_1.FishPlayer.get(player); + try { + if (Core.graphics.getFramesPerSecond() < MIN_EFFECT_TPS) { + p.sendMessage(lowTPSError, 1000); + return; + } + if (!p.hasPerm('bulkVisualEffects')) { + p.sendMessage(noPermissionText, 1000); + return; + } + lastAccessedBulkLabel = p; + //get individual labels + var labels = []; + var inQuotes = false; + var startIdx = 0; + for (var i = 0; i < content.length; i++) { + switch (content[i]) { + case '"': + if (i > 0 && content[i - 1] == '\\') + break; + inQuotes = !inQuotes; + break; + //separate + case bulkSeparator: + if (inQuotes) + break; + labels.push(content.substring(startIdx, i)); + startIdx = i + 1; + break; + default: + break; + } + } + //last label + if (startIdx < content.length) { + labels.push(content.substring(startIdx, content.length - 1)); + } + if (labels.length > bulkLimit) { + p.sendMessage(tooLongText, 1000); + return; + } + //display labels + // eslint-disable-next-line @typescript-eslint/prefer-for-of + for (var i = 0; i < labels.length; i++) { + var label = labels[i]; + if (label.trim().length <= 0) + continue; + if (!handleLabel(player, label, false)) + return; + } + } + catch (_a) { + p.sendMessage(procError, 1000); + } + }); + //lines + Vars.netServer.addPacketHandler('lineEffect', function (player, content) { + var p = players_1.FishPlayer.get(player); + try { + if (Core.graphics.getFramesPerSecond() < MIN_EFFECT_TPS) { + p.sendMessage(lowTPSError, 1000); + return; + } + if (!p.hasPerm("visualEffects")) { + p.sendMessage(noPermissionText, 1000); + return; + } + if (!handleLine(content, player)) + return; + lastAccessedLine = p; + } + catch (_a) { + p.sendMessage(procError, 1000); + } + }); + //this is the silas effect but it's way too real + Vars.netServer.addPacketHandler('bulkLineEffect', function (player, content) { + var p = players_1.FishPlayer.get(player); + if (Core.graphics.getFramesPerSecond() < MIN_EFFECT_TPS) { + p.sendMessage(lowTPSError, 1000); + return; + } + if (!p.hasPerm('bulkVisualEffects')) { + p.sendMessage(noPermissionText, 1000); + return; + } + try { + var lines = content.split(bulkSeparator); + if (lines.length > bulkLimit) { + p.sendMessage(tooLongText, 1000); + return; + } + // eslint-disable-next-line @typescript-eslint/prefer-for-of + for (var i = 0; i < lines.length; i++) { + var line = lines[i]; + if (line.trim().length <= 0) + continue; + if (!handleLine(line, player)) + return; + } + lastAccessedBulkLine = p; + } + catch (_a) { + p.sendMessage(procError, 1000); + } + }); +} +//commands +exports.commands = (0, commands_1.commandList)({ + pklast: { + args: [], + description: 'Tells you who last accessed the packet handlers.', + perm: commands_1.Perm.none, + handler: function (_a) { + var output = _a.output; + var outputLines = []; + if (lastAccessedLabel && lastLabel) { + outputLines.push("".concat(lastAccessedLabel.name, "[white] created label \"").concat(lastLabel, "\".")); + } + if (lastAccessedBulkLabel) { + outputLines.push("".concat(lastAccessedBulkLabel.name, "[white] last used the bulk label effect.")); + } + if (lastAccessedLine) { + outputLines.push("".concat(lastAccessedLine.name, "[white] last used the line effect.")); + } + if (lastAccessedBulkLine) { + outputLines.push("".concat(lastAccessedBulkLine.name, "[white] last used the bulk line effect.")); + } + output(outputLines.length > 0 ? outputLines.join('\n') : 'No packet handlers have been accessed yet.'); + } + }, + pkdocs: { + description: 'Packet handler documentation.', + args: [], + perm: commands_1.Perm.none, + handler: function (_a) { + var sender = _a.sender, output = _a.output; + output("\t\t\t\t[blue]FISH[white] Packet Handler Docs\n[white]Usage:[accent]\n\t- Run the javascript function \"Call.serverPacketReliable()\" to send these. (!js in foos)\n\t- You need to multiply world coordinates by Vars.tilesize (8) for things to work properly. This is a relic from the v3 days where every tile was 8 pixels.\n\n[white]Packet types[accent]:\n\t- Line effect: \"lineEffect\", \"x0,y0,x1,y1,hexColor\" (for example \"20.7,19.3,50.4,28.9,#FF0000\")\n\t- Bulk line effect: \"bulkLineEffect\", equivalent to multiple lineEffect packets, with every line separated by a '|' symbol.\n\t- Label effect: \"label\", \"content,duration,x,y\" (for example \"\"Hi!\",10,20,28\")\n\t- Bulk label effect: \"bulkLabel\", equivalent to multiple label packets, with every label separated by a '|' symbol.\n\n[white]Limitations[accent]:\n\t- You ".concat((sender.hasPerm('bulkVisualEffects') ? ("[green]have been granted[accent]") : ("[red]do not have[accent]")), " access to bulk effects.\n\t- Effects will no longer be drawn at ").concat(MIN_EFFECT_TPS, " for server preformance.\n\t- Labels cannot last longer than ").concat(MAX_LABEL_TIME, " seconds.\n\t- There is a set ratelimit for sending packets, be careful ...\n\n[white]Starter Example[accent]:\n\n\tTo place a label saying \"hello\" at (0,0);\n\tFoos users: [lightgray]!js Call.serverPacketReliable(\"label\", [\"\\\"hello\\\"\", 10, 0, 0].join(\",\"))[accent]\n\tnewConsole users: [lightgrey]Call.serverPacketReliable(\"label\", [\"hello\", 10, 0, 10].join(\",\"))[accent]\n\n[white]Comments and Credits[accent]:\n\t- 'These packet handlers and everything related to them were made by [green]frog[accent].\n\t- 'The code style when submitted was beyond drunk... but it worked... barely' -BalaM314\n\t- \"worst error handling i have ever seen, why kick the player???\" -ASimpleBeginner'\n\t- Most of the code was rewritten in 2024 by [#6e00fb]D[#9e15de]a[#cd29c2]r[#fd3ea5]t[accent].'\n\t- Small tweaks by [#00cf]s[#00bf]w[#009f]a[#007f]m[#005f]p[accent]")); + } + } +}); +//#region utils +function findEndQuote(content, startPos) { + if (content[startPos] != '"') { + //not a start quote?? + return -1; + } + for (var i = startPos + 1; i < content.length; i++) { + if (content[i] == '"' && (i < 1 || content[i - 1] != '\\')) { + return i; + } + } + return -1; +} +function handleLabel(player, content, isSingle) { + var endPos = findEndQuote(content, 0); + if (endPos == -1) { + //invalid content + player.sendMessage(invalidContentText); + return false; + } + //label, clean up \"s + var message = content.substring(1, endPos).replace('\\"', '"'); + var parts = content.substring(endPos + 2).split(','); + if (parts.length != 3) { //dur,x,y + player.sendMessage(invalidReq); + return false; + } + if (isSingle && Strings.stripColors(message).length > 150) { + player.sendMessage('Label too large. Maximum is 150 characters, not including color tags.'); + } + if (isSingle) { + lastLabel = message; + } + var duration = Number(parts[0]); + var x = Number(parts[1]), y = Number(parts[2]); + if (Number.isNaN(duration) || duration > MAX_LABEL_TIME || Number.isNaN(x) || Number.isNaN(y)) { + player.sendMessage(invalidReq); + return false; + } + /*Call.labelReliable( + message, //message + Number(parts[0]), //duration + Number(parts[1]), //x + Number(parts[2]) //y + );*/ + tmpLabelPacket.message = message; + tmpLabelPacket.duration = duration; + tmpLabelPacket.worldx = x; + tmpLabelPacket.worldy = y; + Vars.net.send(tmpLabelPacket, false); + return true; +} +function handleLine(content, player) { + var parts = content.split(','); + if (parts.length != 5) { //x0,y0,x1,y1,color + player.sendMessage(invalidReq); + return false; + } + Tmp.v1.set(Number(parts[2]), Number(parts[3])); //x1,y1 + Color.valueOf(Tmp.c1, parts[4]); //color + /*Call.effect( + Fx.pointBeam, + Number(parts[0]), Number(parts[1]), //x,y + 0, Tmp.c1, //color + Tmp.v1 //x1,y1 + );*/ + tmpLinePacket.x = Number(parts[0]); + tmpLinePacket.y = Number(parts[1]); + Vars.net.send(tmpLinePacket, false); + return true; +} +function bulkInfoMsg(messages, conn) { + for (var i = messages.length - 1; i >= 0; i--) { + Call.infoMessage(conn, messages[i]); + } +} +//#endregion diff --git a/build/scripts/players.js b/build/scripts/players.js index 91313b74..73c005ff 100644 --- a/build/scripts/players.js +++ b/build/scripts/players.js @@ -1,1994 +1,1994 @@ -"use strict"; -/* -Copyright © BalaM314, 2026. All Rights Reserved. -This file contains the FishPlayer class, and many player-related functions. -*/ -var __makeTemplateObject = (this && this.__makeTemplateObject) || function (cooked, raw) { - if (Object.defineProperty) { Object.defineProperty(cooked, "raw", { value: raw }); } else { cooked.raw = raw; } - return cooked; -}; -var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { - if (k2 === undefined) k2 = k; - var desc = Object.getOwnPropertyDescriptor(m, k); - if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { - desc = { enumerable: true, get: function() { return m[k]; } }; - } - Object.defineProperty(o, k2, desc); -}) : (function(o, m, k, k2) { - if (k2 === undefined) k2 = k; - o[k2] = m[k]; -})); -var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { - Object.defineProperty(o, "default", { enumerable: true, value: v }); -}) : function(o, v) { - o["default"] = v; -}); -var __importStar = (this && this.__importStar) || (function () { - var ownKeys = function(o) { - ownKeys = Object.getOwnPropertyNames || function (o) { - var ar = []; - for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k; - return ar; - }; - return ownKeys(o); - }; - return function (mod) { - if (mod && mod.__esModule) return mod; - var result = {}; - if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]); - __setModuleDefault(result, mod); - return result; - }; -})(); -var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { - function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } - return new (P || (P = Promise))(function (resolve, reject) { - function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } - function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } - function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } - step((generator = generator.apply(thisArg, _arguments || [])).next()); - }); -}; -var __generator = (this && this.__generator) || function (thisArg, body) { - var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g = Object.create((typeof Iterator === "function" ? Iterator : Object).prototype); - return g.next = verb(0), g["throw"] = verb(1), g["return"] = verb(2), typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g; - function verb(n) { return function (v) { return step([n, v]); }; } - function step(op) { - if (f) throw new TypeError("Generator is already executing."); - while (g && (g = 0, op[0] && (_ = 0)), _) try { - if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t; - if (y = 0, t) op = [op[0] & 2, t.value]; - switch (op[0]) { - case 0: case 1: t = op; break; - case 4: _.label++; return { value: op[1], done: false }; - case 5: _.label++; y = op[1]; op = [0]; continue; - case 7: op = _.ops.pop(); _.trys.pop(); continue; - default: - if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; } - if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; } - if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; } - if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; } - if (t[2]) _.ops.pop(); - _.trys.pop(); continue; - } - op = body.call(thisArg, _); - } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; } - if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true }; - } -}; -var __values = (this && this.__values) || function(o) { - var s = typeof Symbol === "function" && Symbol.iterator, m = s && o[s], i = 0; - if (m) return m.call(o); - if (o && typeof o.length === "number") return { - next: function () { - if (o && i >= o.length) o = void 0; - return { value: o && o[i++], done: !o }; - } - }; - throw new TypeError(s ? "Object is not iterable." : "Symbol.iterator is not defined."); -}; -var __read = (this && this.__read) || function (o, n) { - var m = typeof Symbol === "function" && o[Symbol.iterator]; - if (!m) return o; - var i = m.call(o), r, ar = [], e; - try { - while ((n === void 0 || n-- > 0) && !(r = i.next()).done) ar.push(r.value); - } - catch (error) { e = { error: error }; } - finally { - try { - if (r && !r.done && (m = i["return"])) m.call(i); - } - finally { if (e) throw e.error; } - } - return ar; -}; -var __spreadArray = (this && this.__spreadArray) || function (to, from, pack) { - if (pack || arguments.length === 2) for (var i = 0, l = from.length, ar; i < l; i++) { - if (ar || !(i in from)) { - if (!ar) ar = Array.prototype.slice.call(from, 0, i); - ar[i] = from[i]; - } - } - return to.concat(ar || Array.prototype.slice.call(from)); -}; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.FishPlayer = void 0; -var api = __importStar(require("/api")); -var config_1 = require("/config"); -var commands_1 = require("/frameworks/commands"); -var menus_1 = require("/frameworks/menus"); -var funcs_1 = require("/funcs"); -var globals = __importStar(require("/globals")); -var globals_1 = require("/globals"); -var maps_1 = require("/maps"); -var ranks_1 = require("/ranks"); -var utils_1 = require("/utils"); -var FishPlayer = /** @class */ (function () { - //#endregion - function FishPlayer(uuid, data, player) { - //#endregion - //#region Transient properties - //Commands framework - /** Front-to-back queue of menus to show. */ - this.activeMenus = []; - /** Mapping from command to usage data. */ - this.usageData = {}; - this.tapInfo = { - commandName: null, - lastArgs: {}, - mode: "once", - }; - //Misc - this.player = null; - /** Used for the /trail command. */ - this.trail = null; - this.cleanedName = "Unnamed player [ERROR}"; - this.prefixedName = "Unnamed player [ERROR}"; - /** Used to freeze players when votekicking. */ - this.frozen = false; - /** Used to avoid spamming players with ads by the tip message system */ - this.lastShownAd = globals.maxTime; - /** Used to avoid spamming players with ads by the tip message system */ - this.showAdNext = false; - /** Transient statistics, used by the automatic griefer detection. */ - this.tstats = { - //remember to clear this in updateSavedInfoFromPlayer! - blocksBroken: 0, - blockInteractionsThisMap: 0, - lastMapStartTime: 0, - lastMapPlayedTime: 0, - wavesSurvived: 0, - }; - /** Whether the player has manually marked themselves as AFK. */ - this.manualAfk = false; - //Used for AFK detection. - this.lastMousePosition = [0, 0]; - this.lastUnitPosition = [0, 0]; - this.lastActive = Date.now(); - /** Set this to false to disable automatic name updates. Used for the rename console command. */ - this.shouldUpdateName = true; - /** Used by the sendMessage() ratelimit system. */ - this.lastRatelimitedMessage = -1; - /** Keeps track of whether a player has changed team this match, for win rate calculation. */ - this.changedTeam = false; - /** Whether the player's IP was detected as a VPN. */ - this.ipDetectedVpn = false; - /** - * If a player's IP is detected as a VPN on their first join, - * they are autoflagged and cannot build or talk in chat. - */ - this.autoflagged = false; - /** Timestamp until which this player will not be allowed to control units. */ - this.blockedFromPossessingUnitsUntil = 0; - /** Timestamp until which this player will not be allowed to control units. */ - this.blockedFromCommandingUnitsUntil = 0; - // Used by the data syncing framework. - this.infoUpdated = false; - this.dataSynced = false; - this.restoreTeam = null; - this.name = "Unnamed player [ERROR}"; - this.muted = false; - this.unmarkTime = -1; - this.rank = ranks_1.Rank.player; - this.flags = new Set(); - /** Used to color chat messages for the member command */ - this.highlight = null; - /** Used to color the player's name for the member command */ - this.rainbow = null; - /** List of all moderation actions that have been performed on this player. */ - this.history = []; - /** - * The USID for this player. - * USID stands for Unique Server IDentifier. It is like a UUID, but unique to each server (by IP and port). - * It cannot be viewed by admins and it cannot be obtained by other servers. - */ - this.usid = null; - /** If chat strictness is set to "strict", the player will not be allowed to swear. */ - this.chatStrictness = "chat"; - /** -1 represents unknown */ - this.lastJoined = -1; - /** -1 represents unknown */ - this.firstJoined = -1; - /** -1 represents unknown */ - this.globalLastJoined = -1; - /** -1 represents unknown */ - this.globalFirstJoined = -1; - this.stats = { - blocksBroken: 0, - blocksPlaced: 0, - timeInGame: 0, - chatMessagesSent: 0, - gamesFinished: 0, - gamesWon: 0, - }; - this.globalStats = this.stats; - /** Used for the /vanish command. */ - this.showRankPrefix = true; - this.achievements = new Bits(); - this.uuid = uuid; - this.player = player; - this.updateData(data); - } - //#region getplayer - //Contains methods used to get FishPlayer instances. - FishPlayer.createFromPlayer = function (player) { - return new this(player.uuid(), {}, player); - }; - FishPlayer.createFromInfo = function (playerInfo) { - var _a; - return new this(playerInfo.id, { - uuid: playerInfo.id, - name: playerInfo.lastName, - usid: (_a = playerInfo.adminUsid) !== null && _a !== void 0 ? _a : null - }, null); - }; - FishPlayer.getFromInfo = function (playerInfo) { - var _a; - var _b, _c; - return (_a = (_b = FishPlayer.cachedPlayers)[_c = playerInfo.id]) !== null && _a !== void 0 ? _a : (_b[_c] = FishPlayer.createFromInfo(playerInfo)); - }; - FishPlayer.get = function (player) { - var _a; - var _b, _c; - return (_a = (_b = FishPlayer.cachedPlayers)[_c = player.uuid()]) !== null && _a !== void 0 ? _a : (_b[_c] = FishPlayer.createFromPlayer(player)); - }; - FishPlayer.resolve = function (player) { - var _a; - var _b, _c; - if (player instanceof FishPlayer) - return player; - else - return (_a = (_b = FishPlayer.cachedPlayers)[_c = player.uuid()]) !== null && _a !== void 0 ? _a : (_b[_c] = FishPlayer.createFromPlayer(player)); - }; - FishPlayer.getById = function (id) { - var _a; - return (_a = this.cachedPlayers[id]) !== null && _a !== void 0 ? _a : null; - }; - /** Returns the FishPlayer representing the first online player matching a given name. */ - FishPlayer.getByName = function (name) { - if (name == "") - return null; - var realPlayer = Groups.player.find(function (p) { - return p.name === name || - p.name.includes(name) || - p.name.toLowerCase().includes(name.toLowerCase()) || - Strings.stripColors(p.name).toLowerCase() === name.toLowerCase() || - Strings.stripColors(p.name).toLowerCase().includes(name.toLowerCase()) || - false; - }); - return realPlayer ? this.get(realPlayer) : null; - }; - ; - /** Returns the FishPlayers representing all online players matching a given name. */ - FishPlayer.getAllByName = function (name, strict) { - if (strict === void 0) { strict = true; } - if (name == "") - return []; - var output = []; - Groups.player.each(function (p) { - var fishP = FishPlayer.get(p); - if (fishP.connected() && fishP.cleanedName.includes(name) || (!strict && fishP.cleanedName.toLowerCase().includes(name))) - output.push(fishP); - }); - return output; - }; - FishPlayer.getOneMindustryPlayerByName = function (str) { - var e_1, _a; - if (str == "") - return "none"; - var players = (0, funcs_1.setToArray)(Groups.player); - var matchingPlayers; - var filters = [ - function (p) { return p.name === str; }, - // p => Strings.stripColors(p.name) === str, - function (p) { return Strings.stripColors(p.name).toLowerCase() === str.toLowerCase(); }, - // p => p.name.includes(str), - function (p) { return p.name.toLowerCase().includes(str.toLowerCase()); }, - function (p) { return Strings.stripColors(p.name).includes(str); }, - function (p) { return Strings.stripColors(p.name).toLowerCase().includes(str.toLowerCase()); }, - ]; - try { - for (var filters_1 = __values(filters), filters_1_1 = filters_1.next(); !filters_1_1.done; filters_1_1 = filters_1.next()) { - var filter = filters_1_1.value; - matchingPlayers = players.filter(filter); - if (matchingPlayers.length == 1) - return matchingPlayers[0]; - else if (matchingPlayers.length > 1) - return "multiple"; - } - } - catch (e_1_1) { e_1 = { error: e_1_1 }; } - finally { - try { - if (filters_1_1 && !filters_1_1.done && (_a = filters_1.return)) _a.call(filters_1); - } - finally { if (e_1) throw e_1.error; } - } - return "none"; - }; - //This method exists only because there is no easy way to turn an entitygroup into an array - FishPlayer.getAllOnline = function () { - var players = []; - Groups.player.each(function (p) { - var fishP = FishPlayer.get(p); - if (fishP.connected()) - players.push(fishP); - }); - return players; - }; - /** Returns all cached FishPlayers with names matching the search string. */ - FishPlayer.getAllOfflineByName = function (name) { - var e_2, _a; - var matching = []; - try { - for (var _b = __values(Object.entries(this.cachedPlayers)), _c = _b.next(); !_c.done; _c = _b.next()) { - var _d = __read(_c.value, 2), uuid = _d[0], player = _d[1]; - if (player.cleanedName.toLowerCase().includes(name)) - matching.push(player); - } - } - catch (e_2_1) { e_2 = { error: e_2_1 }; } - finally { - try { - if (_c && !_c.done && (_a = _b.return)) _a.call(_b); - } - finally { if (e_2) throw e_2.error; } - } - return matching; - }; - FishPlayer.onConnectPacket = function (_a) { - var _this = this; - var uuid = _a.uuid, name = _a.name; - var entry = this.cachedPlayers[uuid]; - if (entry) { - entry.infoUpdated = false; - entry.dataSynced = false; - entry.name = name; - } - api.getFishPlayerData(uuid).then(function (data) { - if (!data) - return; //nothing to sync - var fishP; - if (!(uuid in _this.cachedPlayers)) { - fishP = new FishPlayer(uuid, data, null); - fishP.originalName = name; - fishP.dataSynced = true; - _this.cachedPlayers[uuid] = fishP; - } - else { - fishP = _this.cachedPlayers[uuid]; - fishP.dataSynced = true; - fishP.updateData(data); - if (fishP.infoUpdated) { - //Player has already connected - //Run it again - if (fishP.player) - fishP.updateSavedInfoFromPlayer(fishP.player, true); - } - else { - //Player has not connected yet, nothing further needed - } - } - if (fishP.connected()) { - fishP.checkUsid(); - fishP.updateMemberExclusiveState(); - fishP.updateName(); - fishP.updateAdminStatus(); - fishP.updateAutoflaggedStatus(); - fishP.checkAutoRanks(); - fishP.sendWelcomeMessage(); - } - }, function () { - var fishP = _this.cachedPlayers[uuid]; - fishP.updateAdminStatus(); - fishP.updateAutoflaggedStatus(); - fishP.sendWelcomeMessage(); - if (fishP === null || fishP === void 0 ? void 0 : fishP.player) - fishP.player.sendMessage(config_1.text.dataFetchFailed); - else - _this.dataFetchFailedUuids.add(uuid); - }); - }; - /** Must be called at player join, before updateName(). */ - FishPlayer.prototype.updateSavedInfoFromPlayer = function (player, repeated) { - if (repeated === void 0) { repeated = false; } - this.player = player; - if (repeated) { - this.name = this.originalName; - } - else { - this.originalName = this.name = player.name; - } - if (this.firstJoined < 1) - this.firstJoined = Date.now(); - //Do not update USID here - this.manualAfk = false; - this.cleanedName = Strings.stripColors(player.name); - this.lastJoined = Date.now(); - this.lastMousePosition = [0, 0]; - this.lastActive = Date.now(); - if (this.highlight === "[white]") - this.highlight = null; - this.shouldUpdateName = true; - this.changedTeam = false; - this.ipDetectedVpn = false; - this.tstats.blocksBroken = 0; - if (this.tstats.lastMapPlayedTime != FishPlayer.lastMapStartTime) { - this.tstats.blockInteractionsThisMap = 0; - this.tstats.lastMapPlayedTime = FishPlayer.lastMapStartTime; - } - this.infoUpdated = true; - }; - FishPlayer.prototype.updateData = function (data) { - var _a; - if (data.name != undefined) - this.name = data.name; - if (data.muted != undefined) - this.muted = data.muted; - if (data.unmarkTime != undefined) - this.unmarkTime = data.unmarkTime; - if (data.lastJoined != undefined) - this.lastJoined = data.lastJoined; - if (data.firstJoined != undefined) - this.firstJoined = data.firstJoined; - if (data.globalLastJoined != undefined) - this.globalLastJoined = data.globalLastJoined; - if (data.globalFirstJoined != undefined) - this.globalFirstJoined = data.globalFirstJoined; - if (data.highlight != undefined) - this.highlight = data.highlight; - if (data.history != undefined) - this.history = data.history; - if (data.rainbow != undefined) - this.rainbow = data.rainbow; - if (data.usid != undefined) - this.usid = data.usid; - if (data.chatStrictness != undefined) - this.chatStrictness = data.chatStrictness; - if (data.stats != undefined) - this.stats = data.stats; - if (data.globalStats != undefined) - this.globalStats = data.globalStats; - if (data.showRankPrefix != undefined) - this.showRankPrefix = data.showRankPrefix; - if (data.rank != undefined) - this.rank = (_a = ranks_1.Rank.getByName(data.rank)) !== null && _a !== void 0 ? _a : ranks_1.Rank.player; - if (data.flags != undefined) - this.flags = new Set(data.flags.map(ranks_1.RoleFlag.getByName).filter(Boolean)); - if (data.achievements != undefined) - this.achievements = JsonIO.read(Bits, "{bits:".concat(data.achievements, "}")); - }; - FishPlayer.prototype.getData = function () { - var _a = this, uuid = _a.uuid, name = _a.name, muted = _a.muted, unmarkTime = _a.unmarkTime, rank = _a.rank, flags = _a.flags, highlight = _a.highlight, rainbow = _a.rainbow, history = _a.history, usid = _a.usid, chatStrictness = _a.chatStrictness, lastJoined = _a.lastJoined, firstJoined = _a.firstJoined, stats = _a.stats, showRankPrefix = _a.showRankPrefix; - return { - uuid: uuid, - name: name, - muted: muted, - unmarkTime: unmarkTime, - highlight: highlight, - rainbow: rainbow, - history: history, - usid: usid, - chatStrictness: chatStrictness, - lastJoined: lastJoined, - firstJoined: firstJoined, - stats: stats, - showRankPrefix: showRankPrefix, - rank: rank.name, - flags: __spreadArray([], __read(flags.values()), false).map(function (f) { return f.name; }), - achievements: JsonIO.write(Reflect.get(this.achievements, "bits")) - }; - }; - /** Warning: the "update" callback is run twice. */ - FishPlayer.prototype.updateSynced = function (update, beforeFetch, afterFetch) { - return __awaiter(this, void 0, void 0, function () { - var data; - return __generator(this, function (_a) { - switch (_a.label) { - case 0: - update(this); - beforeFetch === null || beforeFetch === void 0 ? void 0 : beforeFetch(this); - return [4 /*yield*/, api.getFishPlayerData(this.uuid)]; - case 1: - data = _a.sent(); - if (data) - this.updateData(data); - update(this); - //of course, this is a race condition - //but it's unlikely to happen - //could be fixed by transmitting the update operation to the server as a mongo update command - afterFetch === null || afterFetch === void 0 ? void 0 : afterFetch(this); - return [4 /*yield*/, api.setFishPlayerData(this.getData(), 1, false)]; - case 2: - _a.sent(); - return [2 /*return*/]; - } - }); - }); - }; - //#endregion - //#region actively synced data updates - FishPlayer.prototype.stop = function (by, duration, message, notify) { - var _this = this; - if (notify === void 0) { notify = true; } - if (duration > 60000) - this.setPunishedIP(config_1.stopAntiEvadeTime); - this.showRankPrefix = true; - var unmarkTime = Date.now() + duration; - if (unmarkTime > globals.maxTime) - unmarkTime = globals.maxTime; - return this.updateSynced(function () { - _this.unmarkTime = unmarkTime; - _this.updateName(); - }, function () { - _this.setUnmarkTimer(duration); - if (_this.connected() && notify) { - _this.stopUnit(); - _this.sendMessage(message - ? "[scarlet]Oopsy Whoopsie! You've been stopped, and marked as a griefer for reason: [white]".concat(message, "[]") - : "[scarlet]Oopsy Whoopsie! You've been stopped, and marked as a griefer."); - if (duration < funcs_1.Duration.hours(1)) { - //less than one hour - _this.sendMessage("[yellow]Your mark will expire in ".concat((0, utils_1.formatTime)(duration), ".")); - } - } - }, function () { return _this.addHistoryEntry({ - action: 'stopped', - by: by instanceof FishPlayer ? by.name : by, - time: Date.now(), - }); }); - }; - FishPlayer.prototype.free = function (by) { - var _this = this; - by !== null && by !== void 0 ? by : (by = "console"); - this.autoflagged = false; //Might as well set autoflagged to false - FishPlayer.removePunishedIP(this.ip()); - FishPlayer.removePunishedUUID(this.uuid); - return this.updateSynced(function () { - _this.unmarkTime = -1; - }, function () { - if (_this.connected()) { - _this.sendMessage('[yellow]Looks like someone had mercy on you.'); - _this.updateName(); - _this.forceRespawn(); - } - }, function () { return _this.addHistoryEntry({ - action: 'freed', - by: by instanceof FishPlayer ? by.name : by, - time: Date.now(), - }); }); - }; - FishPlayer.prototype.setRank = function (rank) { - return __awaiter(this, void 0, void 0, function () { - var _this = this; - return __generator(this, function (_a) { - switch (_a.label) { - case 0: - if (typeof rank === "string" || !rank) { - rank; - (0, funcs_1.crash)("Type error in FishPlayer.setFlag(): rank is invalid"); - } - if (rank == ranks_1.Rank.pi && !config_1.Mode.localDebug) - throw new TypeError("Cannot find function setRank in object [object Object]."); - return [4 /*yield*/, this.updateSynced(function () { - _this.rank = rank; - _this.updateName(); - _this.updateAdminStatus(); - }, function () { return FishPlayer.saveAll(); })]; - case 1: - _a.sent(); - return [2 /*return*/]; - } - }); - }); - }; - FishPlayer.prototype.setFlag = function (flag_, value) { - return __awaiter(this, void 0, void 0, function () { - var flag; - var _this = this; - return __generator(this, function (_a) { - switch (_a.label) { - case 0: - flag = typeof flag_ == "string" ? - (ranks_1.RoleFlag.getByName(flag_)) - : flag_; - // eslint-disable-next-line @typescript-eslint/no-base-to-string - if (!flag) - (0, funcs_1.crash)("Type error in FishPlayer.setFlag(): flag ".concat(String(flag_), " is invalid")); - return [4 /*yield*/, this.updateSynced(function () { - if (value) { - _this.flags.add(flag); - } - else { - _this.flags.delete(flag); - } - _this.updateMemberExclusiveState(); - _this.updateName(); - })]; - case 1: - _a.sent(); - return [2 /*return*/]; - } - }); - }); - }; - FishPlayer.prototype.mute = function (by) { - var _this = this; - if (this.muted) - return; - this.showRankPrefix = true; - return this.updateSynced(function () { - _this.muted = true; - _this.updateName(); - }, function () { - _this.sendMessage("[yellow]Hey! You have been muted. You cannot send messages to other players. You can still send messages to staff members."); - _this.setPunishedIP(config_1.stopAntiEvadeTime); - }, function () { return _this.addHistoryEntry({ - action: 'muted', - by: by instanceof FishPlayer ? by.name : by, - time: Date.now(), - }); }); - }; - FishPlayer.prototype.unmute = function (by) { - var _this = this; - if (!this.muted) - return; - FishPlayer.removePunishedIP(this.ip()); - FishPlayer.removePunishedUUID(this.uuid); - return this.updateSynced(function () { - _this.muted = false; - _this.updateName(); - }, function () { - _this.sendMessage("[green]You have been unmuted."); - }, function () { return _this.addHistoryEntry({ - action: 'muted', - by: by instanceof FishPlayer ? by.name : by, - time: Date.now(), - }); }); - }; - //#endregion - //#region eventhandling - //Contains methods that handle an event and must be called by other code (usually through Events.on). - /** Must be run on PlayerConnectEvent. */ - FishPlayer.onPlayerConnect = function (player) { - var _a; - var _b, _c; - var fishPlayer = (_a = (_b = this.cachedPlayers)[_c = player.uuid()]) !== null && _a !== void 0 ? _a : (_b[_c] = this.createFromPlayer(player)); - var previousJoin = fishPlayer.lastJoined; - fishPlayer.updateSavedInfoFromPlayer(player); - if (fishPlayer.validate()) { - if (!fishPlayer.hasPerm("bypassNameCheck")) { - var message = (0, utils_1.isImpersonator)(fishPlayer.name, fishPlayer.ranksAtLeast("admin")); - if (message !== false) { - fishPlayer.sendMessage("[scarlet]\u26A0[] [gold]Oh no! Our systems think you are a [scarlet]SUSSY IMPERSONATOR[]!\n[gold]Reason: ".concat(message, "\n[gold]Change your name to remove the tag.")); - } - else if ((0, utils_1.cleanText)(player.name, true).includes("hacker")) { - fishPlayer.sendMessage("[scarlet]\u26A0 Don't be a script kiddie!"); - globals_1.FishEvents.fire("scriptKiddie", [fishPlayer]); - } - } - fishPlayer.updateAdminStatus(); - fishPlayer.checkVPNAndJoins(); - fishPlayer.updateName(); - //I think this is a better spot for this - if (fishPlayer.firstJoin()) - void menus_1.Menu.menu("Rules for [#0000ff] >|||> FISH [white] servers [white]", config_1.rules.join("\n\n[white]") + "\nYou can view these rules again by running [cyan]/rules[].", ["[green]I understand and agree to these terms"], fishPlayer); - } - }; - /** Must be run on PlayerJoinEvent. */ - FishPlayer.onPlayerJoin = function (player) { - var _this = this; - var _a; - var _b, _c; - var fishPlayer = (_a = (_b = this.cachedPlayers)[_c = player.uuid()]) !== null && _a !== void 0 ? _a : (_b[_c] = (function () { - Log.err("onPlayerJoin: no fish player was created? ".concat(player.uuid())); - return _this.createFromPlayer(player); - })()); - //Don't activate heuristics until they've joined - //a lot of time can pass between connect and join - //also the player might connect but fail to join for a lot of reasons, - //or connect, fail to join, then connect again and join successfully - //which would cause heuristics to activate twice - fishPlayer.activateHeuristics(); - }; - FishPlayer.updateAFKCheck = function () { - //TODO better AFK check - this.forEachPlayer(function (fishP, mp) { - if (fishP.lastMousePosition[0] != mp.mouseX || fishP.lastMousePosition[1] != mp.mouseY) { - fishP.lastActive = Date.now(); - } - fishP.lastMousePosition = [mp.mouseX, mp.mouseY]; - if (fishP.lastUnitPosition[0] != mp.x || fishP.lastUnitPosition[1] != mp.y) { - fishP.lastActive = Date.now(); - } - fishP.lastUnitPosition = [mp.x, mp.y]; - fishP.updateName(); - }); - }; - /** Must be run on PlayerLeaveEvent. */ - FishPlayer.onPlayerLeave = function (player) { - var _a; - var fishP = this.cachedPlayers[player.uuid()]; - if (!fishP) - return; - if (Vars.netServer.currentlyKicking && - Reflect.get(Vars.netServer.currentlyKicking, "target") == player) { - //Anti votekick evasion - var votes_1 = Reflect.get(Vars.netServer.currentlyKicking, "votes"); - if ((function () { - if (fishP.hasPerm("bypassVotekick")) - return false; - if (fishP.hasPerm("bypassVoteFreeze")) - return votes_1 >= Vars.netServer.votesRequired(); - if (fishP.info().timesJoined > 50) - return votes_1 >= 2; - return votes_1 >= 1; - })()) { - var kickDuration = NetServer.kickDuration; - //Pass the votekick - Call.sendMessage("[orange]Vote passed.[scarlet] ".concat(player.name, "[orange] will be banned from the server for ").concat(kickDuration / 60, " minutes.")); - player.kick(Packets.KickReason.vote, kickDuration * 1000); //it is stored in seconds but needs to be converted to millis - Reflect.get(Vars.netServer.currentlyKicking, "task").cancel(); - Vars.netServer.currentlyKicking = null; - } - } - //Clear temporary states such as menu and taphandler - fishP.activeMenus = []; - fishP.tapInfo.commandName = null; - fishP.updateStats(function (stats) { return stats.timeInGame += (Date.now() - fishP.lastJoined); }); //Time between joining and leaving - fishP.lastJoined = Date.now(); - this.recentLeaves.unshift(fishP); - if (this.recentLeaves.length > 10) - this.recentLeaves.pop(); - void api.setFishPlayerData(fishP.getData(), 1, true); - var currentRun = (_a = maps_1.PartialMapRun.current) === null || _a === void 0 ? void 0 : _a.startTime; - if (currentRun) - Core.app.post(function () { - //Wait for the /spectate command's handler to fix their team before saving it - fishP.restoreTeam = [fishP.player.team(), Date.now(), currentRun]; - }); - }; - FishPlayer.validateVotekickSession = function () { - var _a; - if (!Vars.netServer.currentlyKicking) - return; - var target = this.get(Reflect.get(Vars.netServer.currentlyKicking, "target")); - var voted = Reflect.get(Vars.netServer.currentlyKicking, "voted"); - if (voted.size == 2) { - //Try to find the UUID of the initiator - var uuid_1 = null; - voted.entries().toArray().each(function (e) { - if (globals_1.uuidPattern.test(e.key)) - uuid_1 = e.key; - }); - if (uuid_1) { - var initiator = this.getById(uuid_1); - if (initiator === null || initiator === void 0 ? void 0 : initiator.stelled()) { - if (initiator.hasPerm("bypassVotekick")) { - if (target !== this.easterEggVotekickTarget) { - this.easterEggVotekickTarget = target; - var msg = (_a = (new Error()).stack) === null || _a === void 0 ? void 0 : _a.split("\n").slice(0, 4).join("\n"); - Call.sendMessage("[scarlet]Server[lightgray] has voted on kicking[orange] ".concat(initiator.prefixedName, "[lightgray].[accent] (\u221E/").concat(Vars.netServer.votesRequired(), ")\n\t[scarlet]Error: failed to kick player ").concat(initiator.name, "\n\t").concat(msg, "\n\t[scarlet]Error: failed to cancel votekick\n\t").concat(msg)); - } - return; - } - Call.sendMessage("[scarlet]Server[lightgray] has voted on kicking[orange] ".concat(initiator.prefixedName, "[lightgray].[accent] (\u221E/").concat(Vars.netServer.votesRequired(), ")\n[scarlet]Vote passed.")); - initiator.kick("You are not allowed to votekick other players while marked.", 2); - Reflect.get(Vars.netServer.currentlyKicking, "task").cancel(); - Vars.netServer.currentlyKicking = null; - return; - } - else if ((initiator === null || initiator === void 0 ? void 0 : initiator.hasPerm("immediatelyVotekickNewPlayers")) && target.isSuspicious("high") && !target.hasPerm("bypassVotekick")) { - Call.sendMessage("[scarlet]Server[lightgray] has voted on kicking[orange] ".concat(target.prefixedName, "[lightgray].[accent] (").concat(Vars.netServer.votesRequired(), "/").concat(Vars.netServer.votesRequired(), ")\n[scarlet]Vote passed.")); - target.kick(Packets.KickReason.vote, funcs_1.Duration.minutes(30)); - Reflect.get(Vars.netServer.currentlyKicking, "task").cancel(); - Vars.netServer.currentlyKicking = null; - return; - } - else if (target.isSuspicious("high") && !target.hasPerm("bypassVotekick") && !target.ranksAtLeast("trusted")) { - //Increase votes by 1, from 1 to 2 - Reflect.set(Vars.netServer.currentlyKicking, "votes", Packages.java.lang.Integer(2)); - voted.put("__server__", 1); - Call.sendMessage("[scarlet]Server[lightgray] has voted on kicking[orange] ".concat(target.prefixedName, "[lightgray].[accent] (2/").concat(Vars.netServer.votesRequired(), ")\n[lightgray]Type[orange] /vote [] to agree.")); - return; - } - } - } - if (target.hasPerm("bypassVotekick")) { - Call.sendMessage("[scarlet]Server[lightgray] has voted on kicking[orange] ".concat(target.prefixedName, "[lightgray].[accent] (-\u221E/").concat(Vars.netServer.votesRequired(), ")\n[scarlet]Vote cancelled.")); - Reflect.get(Vars.netServer.currentlyKicking, "task").cancel(); - Vars.netServer.currentlyKicking = null; - } - else if (target.ranksAtLeast("trusted") && Groups.player.size() > 4 && voted.get("__server__") == 0) { - //decrease votes by two, goes from 1 to negative 1 - Reflect.set(Vars.netServer.currentlyKicking, "votes", Packages.java.lang.Integer(-1)); - voted.put("__server__", -2); - Call.sendMessage("[scarlet]Server[lightgray] has voted on kicking[orange] ".concat(target.prefixedName, "[lightgray].[accent] (-1/").concat(Vars.netServer.votesRequired(), ")\n[lightgray]Type[orange] /vote [] to agree.")); - } - }; - FishPlayer.onPlayerChat = function (player, message) { - var fishP = this.get(player); - if (message.trim().toLowerCase().startsWith("/vote y") || message.startsWith("/votekick ")) { - this.checkVotekickAction(fishP, message); - } - fishP.lastActive = Date.now(); - fishP.updateStats(function (stats) { return stats.chatMessagesSent++; }); - }; - FishPlayer.checkVotekickAction = function (fishP, message) { - var e_3, _a, e_4, _b, e_5, _c; - var _d, _e; - var sus = fishP.suspicionLevel(); - var timeSinceJoin = Date.now() - fishP.lastJoined; - var target; - if (message.startsWith("/votekick")) { - var id = (_d = message.split(" ")[1]) === null || _d === void 0 ? void 0 : _d.split("#")[1]; - target = Groups.player.getByID(Number(id)); - if (!target) - return; //invalid votekick command, harmless - } - else { //TODO these "harmless" actions could be indications of a malfunctioning vkbot and should be logged if they repeat a lot (eg more than 5 times per minute) - if (!Vars.netServer.currentlyKicking) - return; //nobody to votekick, harmless - target = Reflect.get(Vars.netServer.currentlyKicking, "target"); - } - var targetSusLevel = FishPlayer.get(target).suspicionLevel(); - //Evaluate if this action should be blocked - if (sus <= 1) - return; - var reason = undefined; - if (!this.votekickActionRate.allow(108000, 8)) - reason = "Exceeded 8 votekick actions in the last 2 minutes"; - else if (sus == 3 && this.lastVKActions.find(function (a) { return Date.now() - a.time < 10000 && a.playerSusLevel == 3; }) && timeSinceJoin < 6000) - reason = "Performed votekick within 6 seconds of joining and there was a recent suspicious vote"; - else if (sus == 3 && timeSinceJoin < 80000 && this.lastVKActions.find(function (a) { return a.player == fishP; }) && targetSusLevel <= 1) - reason = "Two votekick actions within 80 seconds of joining and the target is not suspicious"; - else if (sus >= 2 && this.lastVKActions.filter(function (a) { return a.playerSusLevel == 3 && Date.now() - a.time < 33000; }).length >= 3) - reason = "More than 3 recent votekick actions by suspicious players"; - else if (sus >= 2 && this.lastVKActions.filter(function (a) { return a.playerSusLevel >= 2; }).length >= 6 && this.lastVKActions.filter(function (a) { return a.player == fishP; }).length >= 3) - reason = "More than 6 slightly suspicious votekick actions within the past 20 minutes and this player has already performed 3 of them"; - if (reason != undefined) { - //Should we ban everyone? - var suspiciousActions = this.lastVKActions.filter(function (action) { - return (action.playerSusLevel == 3 || (action.targetSusLevel <= 2 && action.playerSusLevel >= 2) || action.player == fishP) && Date.now() - action.time < 78000; - }); - if (suspiciousActions.length >= 3) { - //Ban everyone - var playersToBan = suspiciousActions.map(function (a) { return a.player; }).reduce(function (map, p) { - var _a; - map.set(p, ((_a = map.get(p)) !== null && _a !== void 0 ? _a : 0) + 1); - return map; - }, new Map()); - //Only ban players that appeared in the list twice or are high suslevel - var admins = Vars.netServer.admins; - try { - for (var playersToBan_1 = __values(playersToBan), playersToBan_1_1 = playersToBan_1.next(); !playersToBan_1_1.done; playersToBan_1_1 = playersToBan_1.next()) { - var _f = __read(playersToBan_1_1.value, 2), p = _f[0], times = _f[1]; - if (p.suspicionLevel() == 3 || p.suspicionLevel() == 2 && times > 1) { - admins.banPlayerID(p.uuid); - admins.banPlayerIP(p.ip()); - api.ban({ ip: p.ip(), uuid: p.uuid }); - (0, utils_1.logHTrip)(p, "votekick abuse", (p == fishP ? "Player banned automatically" : "Player banned automatically based on previous activity") + - ". Trigger reason: ".concat(reason)); - } - } - } - catch (e_3_1) { e_3 = { error: e_3_1 }; } - finally { - try { - if (playersToBan_1_1 && !playersToBan_1_1.done && (_a = playersToBan_1.return)) _a.call(playersToBan_1); - } - finally { if (e_3) throw e_3.error; } - } - (0, utils_1.updateBans)(function (player) { return "[scarlet]Player [yellow]".concat(player.name, "[scarlet] has been whacked automatically for suspected votekick abuse."); }); - //Pardon most of the votekick targets (the ones that weren't voted on by a non-sus player) - var candidatePardons = new Set(FishPlayer.lastVKActions.map(function (a) { return a.target; })); - try { - for (var _g = __values(FishPlayer.lastVKActions), _h = _g.next(); !_h.done; _h = _g.next()) { - var action = _h.value; - if (action.playerSusLevel <= 1) - candidatePardons.delete(action.target); - } - } - catch (e_4_1) { e_4 = { error: e_4_1 }; } - finally { - try { - if (_h && !_h.done && (_b = _g.return)) _b.call(_g); - } - finally { if (e_4) throw e_4.error; } - } - var playersToPardon = __spreadArray([], __read(candidatePardons), false).map(FishPlayer.get); - try { - //Don't pardon players with suslevel 3 - for (var playersToPardon_1 = __values(playersToPardon), playersToPardon_1_1 = playersToPardon_1.next(); !playersToPardon_1_1.done; playersToPardon_1_1 = playersToPardon_1.next()) { - var p = playersToPardon_1_1.value; - if (!p.isSuspicious("high")) { - p.info().lastKicked = 0; - admins.kickedIPs.remove(p.ip()); - Log.info("Pardoned player @ (@/@)", p.name, p.uuid, p.ip()); - (0, utils_1.logAction)("pardoned", "automod", p, "kicked by suspected votekick bot"); - } - } - } - catch (e_5_1) { e_5 = { error: e_5_1 }; } - finally { - try { - if (playersToPardon_1_1 && !playersToPardon_1_1.done && (_c = playersToPardon_1.return)) _c.call(playersToPardon_1); - } - finally { if (e_5) throw e_5.error; } - } - } - else { - //Just kick the player - (0, utils_1.logHTrip)(fishP, "votekick abuse", "sus=".concat(sus)); - fishP.kick("You have been kicked [accent]automatically[] due to suspicious behavior. Please wait [accent]35[] seconds before rejoining.", 30000); - Call.sendMessage("[scarlet]Player [yellow]".concat(fishP.prefixedName, "[scarlet] was kicked due to suspected votekick abuse.")); - //If this message is going to start a votekick, cancel it - if (message.startsWith("/votekick") && Vars.netServer.currentlyKicking == null) - Core.app.post(function () { - Call.sendMessage("[scarlet]Server[lightgray] has voted on kicking[orange] ".concat(target.name, "[lightgray].[accent] (-\u221E/").concat(Vars.netServer.votesRequired(), ")\n\t\t[scarlet]Vote cancelled due to suspected abuse. [accent]If this is in error, please report it to staff.")); - if (Vars.netServer.currentlyKicking) - Reflect.get(Vars.netServer.currentlyKicking, "task").cancel(); - Vars.netServer.currentlyKicking = null; - }); - //If there is an ongoing votekick and the initiator is suspicious, cancel that - else if (((_e = FishPlayer.lastVKActions.slice().reverse().find(function (a) { return a.type == "start"; })) === null || _e === void 0 ? void 0 : _e.playerSusLevel) == 3) { - Call.sendMessage("[scarlet]Server[lightgray] has voted on kicking[orange] ".concat(target.name, "[lightgray].[accent] (-\u221E/").concat(Vars.netServer.votesRequired(), ")\n\t\t[scarlet]Vote cancelled due to suspected abuse. [accent]If this is in error, please report it to staff.")); - if (Vars.netServer.currentlyKicking) - Reflect.get(Vars.netServer.currentlyKicking, "task").cancel(); - Vars.netServer.currentlyKicking = null; - } - //Otherwise, revoke the vote - else - Core.app.post(function () { - if (Vars.netServer.currentlyKicking) { - var votes = Reflect.get(Vars.netServer.currentlyKicking, "votes") - 1; - Reflect.set(Vars.netServer.currentlyKicking, "votes", votes); - var voted = Reflect.get(Vars.netServer.currentlyKicking, "voted"); - voted.put(fishP.uuid, 0); - voted.put(fishP.ip(), 0); - Call.sendMessage("[scarlet]Vote cancelled due to suspected abuse. [accent]If this is in error, please report it to staff."); - } - }); - } - } - //Update state to catch future actions - this.lastVKActions.push({ - player: fishP, - playerSusLevel: sus, - target: target, - targetSusLevel: targetSusLevel, - time: Date.now(), - type: message.startsWith("/votekick") ? "start" : "vote y", - reason: message.startsWith("/votekick") ? message.split(" ").slice(2).join(" ") : undefined - }); - this.lastVKActions = this.lastVKActions.filter(function (a) { return Date.now() - a.time < funcs_1.Duration.minutes(10); }); - }; - FishPlayer.onPlayerCommand = function (player, command, unjoinedRawArgs) { - if (command == "msg" && unjoinedRawArgs[1] == "Please do not use that logic, as it is attem83 logic and is bad to use. For more information please read www.mindustry.dev/attem") - return; //Attemwarfare message, not sent by the player - player.lastActive = Date.now(); - }; - FishPlayer.onGameOver = function (winningTeam) { - var _this = this; - globals_1.FishEvents.fire("gameOver", [winningTeam]); - this.forEachPlayer(function (fishPlayer) { - //Clear temporary states such as menu and taphandler - fishPlayer.activeMenus = []; - fishPlayer.tapInfo.commandName = null; - //Update stats - if (!_this.ignoreGameOver && fishPlayer.team() != Team.derelict && winningTeam != Team.derelict) { - fishPlayer.updateStats(function (stats) { return stats.gamesFinished++; }); - if (fishPlayer.changedTeam) { - fishPlayer.sendMessage("Refusing to update stats due to a team change."); - } - else { - if (fishPlayer.team() == winningTeam) - fishPlayer.updateStats(function (stats) { return stats.gamesWon++; }); - } - } - fishPlayer.changedTeam = false; - fishPlayer.tstats.wavesSurvived = 0; - fishPlayer.tstats.blockInteractionsThisMap = 0; - }); - }; - FishPlayer.ignoreGameover = function (callback) { - this.ignoreGameOver = true; - callback(); - this.ignoreGameOver = false; - }; - FishPlayer.onGameBegin = function () { - var startTime = Date.now(); - FishPlayer.lastMapStartTime = startTime; - //wait 7 seconds for players to join - Timer.schedule(function () { return FishPlayer.forEachPlayer(function (p) { return p.tstats.lastMapStartTime = startTime; }); }, 7); - }; - /** Must be run on UnitChangeEvent. */ - FishPlayer.onUnitChange = function (player, unit) { - if (unit === null || unit === void 0 ? void 0 : unit.spawnedByCore) - this.onRespawn(player); - }; - FishPlayer.onRespawn = function (player) { - var fishP = this.get(player); - if (fishP.stelled()) - fishP.stopUnit(); - }; - FishPlayer.forEachPlayer = function (func) { - var _this = this; - Groups.player.each(function (player) { - if (player == null) { - Log.err(".FINDTAG. Groups.player.each() returned a null player???"); - return; - } - var fishP = _this.get(player); - func(fishP, player); - }); - }; - FishPlayer.mapPlayers = function (func) { - var _this = this; - var out = []; - Groups.player.each(function (player) { - if (player == null) { - Log.err(".FINDTAG. Groups.player.each() returned a null player???"); - return; - } - out.push(func(_this.get(player))); - }); - return out; - }; - FishPlayer.prototype.updateMemberExclusiveState = function () { - if (!this.hasPerm("member")) { - this.highlight = null; - this.rainbow = null; - } - }; - /** Updates the mindustry player's name, using the prefixes of the current rank and role flags. */ - FishPlayer.prototype.updateName = function () { - var e_6, _a; - var _b; - if (!this.connected() || !this.shouldUpdateName) - return; //No player, no need to update - var name = (_b = this.originalName) !== null && _b !== void 0 ? _b : this.name; - if (this.marked()) - this.showRankPrefix = true; - var prefix = ''; - if (!this.hasPerm("bypassNameCheck") && (0, utils_1.isImpersonator)(name, this.ranksAtLeast("admin"))) - prefix += "[scarlet]SUSSY IMPOSTOR[]"; - if (this.marked()) - prefix += config_1.prefixes.marked; - else if (this.autoflagged) - prefix += config_1.prefixes.flagged; - if (this.muted) - prefix += config_1.prefixes.muted; - if (this.afk()) - prefix += "[orange]\uE876 AFK \uE876 | [white]"; - if (this.showRankPrefix) { - try { - for (var _c = __values(this.flags), _d = _c.next(); !_d.done; _d = _c.next()) { - var flag = _d.value; - prefix += flag.prefix; - } - } - catch (e_6_1) { e_6 = { error: e_6_1 }; } - finally { - try { - if (_d && !_d.done && (_a = _c.return)) _a.call(_c); - } - finally { if (e_6) throw e_6.error; } - } - prefix += this.rank.prefix; - } - if (prefix.length > 0 && !prefix.endsWith(" ")) - prefix += " "; - var replacedName; - if ((0, utils_1.cleanText)(name, true).includes("hacker")) { - //"Don't be a script kiddie" - //-LiveOverflow, 2015 - if (/h.*a.*c.*k.*[3e].*r/i.test(name)) { //try to only replace the part that contains "hacker" if it can be found with a simple regex - replacedName = name.replace(/h.*a.*c.*k.*[3e].*r/gi, "[brown]script kiddie[]"); - } - else { - replacedName = "[brown]script kiddie"; - } - } - else if (this.name.endsWith("[") && !this.name.endsWith("[[")) { - replacedName = name + "["; - } - else - replacedName = name; - this.player.name = this.prefixedName = prefix + replacedName; - }; - FishPlayer.prototype.updateAdminStatus = function () { - if (!this.connected()) - return; - if (this.hasPerm("admin")) { - Vars.netServer.admins.adminPlayer(this.uuid, this.player.usid()); - this.player.admin = true; - } - else { - Vars.netServer.admins.unAdminPlayer(this.uuid); - this.player.admin = false; - } - }; - FishPlayer.prototype.updateAutoflaggedStatus = function () { - if (this.ranksAtLeast("active")) { - this.autoflagged = false; - } - }; - FishPlayer.prototype.checkAntiEvasion = function () { - var e_7, _a; - var _b, _c; - FishPlayer.updatePunishedIPs(); - try { - for (var _d = __values(FishPlayer.punishedIPs), _e = _d.next(); !_e.done; _e = _d.next()) { - var _f = __read(_e.value, 2), ip = _f[0], uuid = _f[1]; - if (ip == this.ip() && uuid != this.uuid && !this.ranksAtLeast("mod")) { - api.sendModerationMessage("Automatically banned player `".concat(this.cleanedName, "` (`").concat(this.uuid, "`/`").concat(this.ip(), "`) for suspected punishment evasion.\nPreviously used UUID `").concat(uuid, "`(").concat((_b = Vars.netServer.admins.getInfoOptional(uuid)) === null || _b === void 0 ? void 0 : _b.plainLastName(), "), currently using UUID `").concat(this.uuid, "` from the same IP address.")); - Log.warn("&yAutomatically banned player &b".concat(this.cleanedName, "&y (&b").concat(this.uuid, "&y/&b").concat(this.ip(), "&y) for suspected punishment evasion.\n&yPreviously used UUID &b").concat(uuid, "&y(&b").concat((_c = Vars.netServer.admins.getInfoOptional(uuid)) === null || _c === void 0 ? void 0 : _c.plainLastName(), "&y), currently using UUID &b").concat(this.uuid, "&y from the same IP address.")); - FishPlayer.messageStaff("[yellow]Automatically banned player [cyan]".concat(this.cleanedName, "[] for suspected punishment evasion.")); - Vars.netServer.admins.banPlayerIP(ip); - api.ban({ ip: ip, uuid: uuid }); - this.kick(Packets.KickReason.banned); - return false; - } - } - } - catch (e_7_1) { e_7 = { error: e_7_1 }; } - finally { - try { - if (_e && !_e.done && (_a = _d.return)) _a.call(_d); - } - finally { if (e_7) throw e_7.error; } - } - return true; - }; - FishPlayer.updatePunishedIPs = function () { - for (var i = 0; i < this.punishedIPs.length; i++) { - if (this.punishedIPs[i][2] < Date.now()) { - this.punishedIPs.splice(i, 1); - } - } - }; - FishPlayer.prototype.checkVPNAndJoins = function () { - var _this = this; - var ip = this.ip(); - var info = this.info(); - api.isVpn(ip, function (isVpn) { - if (isVpn) { - Log.warn("IP ".concat(ip, " was flagged as VPN. Flag rate: ").concat(FishPlayer.stats.numIpsFlagged, "/").concat(FishPlayer.stats.numIpsChecked, " (").concat(100 * FishPlayer.stats.numIpsFlagged / FishPlayer.stats.numIpsChecked, "%)")); - _this.ipDetectedVpn = true; - if (!FishPlayer.autoflagRate.allow(30000, 5)) { - FishPlayer.triggerAntibot(funcs_1.Duration.minutes(3), "rate of flagged IPs exceeded 5 / 30s", "automatic"); - return; - } - if ((info.timesJoined <= 1 || (FishPlayer.autoflagRate.occurences > 3 && info.timesJoined <= 10)) //is this smart? - && !_this.ranksAtLeast("active") - && FishPlayer.punishedIPs.length > 0) { - _this.autoflagged = true; - _this.stopUnit(); - _this.updateName(); - if (FishPlayer.shouldWhackFlaggedPlayers()) { - FishPlayer.whackFlaggedPlayers(); //calls whack all flagged players - } - else { - (0, utils_1.logAction)("autoflagged", "AntiVPN", _this); - api.sendStaffMessage("Autoflagged player ".concat(_this.name, "[cyan] for suspected vpn!"), "AntiVPN", true); - FishPlayer.messageStaff("[yellow]WARNING:[scarlet] player [cyan]\"".concat(_this.name, "[cyan]\"[yellow] is new (").concat(info.timesJoined - 1, " joins) and using a vpn. They have been automatically stopped and muted. Unless there is an ongoing griefer raid, they are most likely innocent. Free them with /free.")); - Log.warn("Player ".concat(_this.name, " (").concat(_this.uuid, ") was autoflagged.")); - void menus_1.Menu.buttons(_this, "[gold]Welcome to Fish Community!", "[gold]Hi there! You have been automatically [scarlet]stopped and muted[] because we've found something to be [pink]a bit sus[]. You can still talk to staff and request to be freed. ".concat(config_1.FColor.discord(templateObject_1 || (templateObject_1 = __makeTemplateObject(["Join our Discord"], ["Join our Discord"]))), " to request a staff member come online if none are on."), [[ - { data: "Close", text: "Close" }, - { data: "Discord", text: config_1.FColor.discord("Discord") }, - ]]).then(function (option) { - if (option == "Discord") { - Call.openURI(_this.con, config_1.text.discordURL); - } - }); - _this.sendMessage("[gold]Welcome to Fish Community!\n[gold]Hi there! You have been automatically [scarlet]stopped and muted[] because we've found something to be [pink]a bit sus[]. You can still talk to staff and request to be freed. ".concat(config_1.FColor.discord(templateObject_2 || (templateObject_2 = __makeTemplateObject(["Join our Discord"], ["Join our Discord"]))), " to request a staff member come online if none are on.")); - } - } - else if (info.timesJoined < 5) { - FishPlayer.messageStaff("[yellow]WARNING:[scarlet] player [cyan]\"".concat(_this.name, "[cyan]\"[yellow] is new (").concat(info.timesJoined - 1, " joins) and using a vpn.")); - } - } - else { - if (info.timesJoined == 1) { - FishPlayer.messageTrusted("[yellow]Player \"".concat(_this.cleanedName, "\" is on first join.")); - } - } - if (info.timesJoined == 1) { - var message = "&lrNew player joined: &c".concat(_this.cleanedName, "&lr (&c").concat(_this.uuid, "&lr/&c").concat(ip, "&lr)"); - //Add BEL, this causes an audible noise - if (globals.fishState.joinBell) - message += '\x07'; - Log.info(message); - } - }, function (err) { - Log.err("Error while checking for VPN status of ip ".concat(ip, "!")); - Log.err(err); - }); - }; - FishPlayer.prototype.validate = function () { - return this.checkName() && this.checkUsid() && this.checkAntiEvasion(); - }; - /** Checks if this player's name is allowed. */ - FishPlayer.prototype.checkName = function () { - if ((0, utils_1.matchFilter)(this.name, "name")) { - this.kick("[scarlet]\"".concat(this.name, "[scarlet]\" is not an allowed name because it contains a banned word.\n\nIf you are unable to change it, please download Mindustry from Steam or itch.io."), 1); - } - else if (Strings.stripColors(this.name.replace(/[\u3164]/g, "")).trim().length == 0) { - this.kick("[scarlet]\"".concat((0, funcs_1.escapeStringColorsClient)(this.name), "[scarlet]\" is not an allowed name because it is empty. Please change it."), 1); - } - else { - return true; - } - return false; - }; - /** Checks if this player's USID is correct. */ - FishPlayer.prototype.checkUsid = function () { - var storedUSID = this.usid; - var usidMissing = storedUSID == null || !storedUSID; - var receivedUSID = this.player.usid(); - if (this.hasPerm("usidCheck")) { - if (usidMissing) { - if (this.hasPerm("mod")) { - //Staff missing USID, don't let them in - Log.err("&rUSID missing for privileged player &c\"".concat(this.cleanedName, "\"&r: no stored usid, cannot authenticate.\nRun &lgsetusid ").concat(this.uuid, " ").concat(receivedUSID, "&fr if you have verified this connection attempt.")); - this.kick("Authorization failure! Please ask a staff member with Console Access to approve this connection.", 1); - FishPlayer.lastAuthKicked = this; - return false; - } - else { - Log.info("Acquired USID for player &c\"".concat(this.cleanedName, "\"&fr: &c\"").concat(receivedUSID, "\"&fr")); - } - } - else { - if (receivedUSID != storedUSID) { - Log.err("&rUSID mismatch for player &c\"".concat(this.cleanedName, "\"&r: stored usid is &c").concat(storedUSID, "&r, but they tried to connect with usid &c").concat(receivedUSID, "&r\nRun &lgsetusid ").concat(this.uuid, " ").concat(receivedUSID, "&fr if you have verified this connection attempt.")); - this.kick("Authorization failure!", 1); - FishPlayer.lastAuthKicked = this; - return false; - } - } - } - else { - if (!usidMissing && receivedUSID != storedUSID) { - Log.err("&rUSID mismatch for player &c\"".concat(this.cleanedName, "\"&r: stored usid is &c").concat(storedUSID, "&r, but they tried to connect with usid &c").concat(receivedUSID, "&r")); - } - } - this.usid = receivedUSID; - return true; - }; - FishPlayer.prototype.displayTrail = function () { - if (this.trail) - Call.effect(Fx[this.trail.type], this.player.x, this.player.y, 0, this.trail.color); - }; - FishPlayer.prototype.sendWelcomeMessage = function () { - var _this = this; - var appealLine = "To appeal, ".concat(config_1.FColor.discord(templateObject_3 || (templateObject_3 = __makeTemplateObject(["join our discord"], ["join our discord"]))), " with ").concat(config_1.FColor.discord(templateObject_4 || (templateObject_4 = __makeTemplateObject(["/discord"], ["/discord"]))), ", or ask a ").concat(ranks_1.Rank.mod.color, "staff member[] in-game."); - if (FishPlayer.dataFetchFailedUuids.has(this.uuid)) { - this.sendMessage(config_1.text.dataFetchFailed); - FishPlayer.dataFetchFailedUuids.delete(this.uuid); - } - if (this.marked()) - this.sendMessage("[gold]Hello there! You are currently [scarlet]marked as a griefer[]. You cannot do anything in-game while marked.\n".concat(appealLine, "\nYour mark will expire automatically ").concat(this.unmarkTime == globals.maxTime ? "in [red]never[]" : "[green]".concat((0, utils_1.formatTimeRelative)(this.unmarkTime), "[]"), ".\nWe apologize for the inconvenience.")); - else if (this.muted) - this.sendMessage("[gold]Hello there! You are currently [red]muted[]. You can still play normally, but cannot send chat messages to other non-staff players while muted.\n".concat(appealLine, "\nWe apologize for the inconvenience.")); - else if (this.autoflagged) - this.sendMessage("[gold]Hello there! You are currently [red]flagged as suspicious[]. You cannot do anything in-game.\n".concat(appealLine, "\nWe apologize for the inconvenience.")); - else if (!this.showRankPrefix) - this.sendMessage("[gold]Hello there! Your rank prefix is currently hidden. You can show it again by running [white]/vanish[]."); - else { - this.sendMessage(config_1.text.welcomeMessage()); - //show tips - var showAd = false; - if (Date.now() - this.lastShownAd > funcs_1.Duration.days(1)) { - this.lastShownAd = Date.now(); - this.showAdNext = true; - } - else if (this.lastShownAd == globals.maxTime) { - //this is the first time they joined, show ad the next time they join - this.showAdNext = true; - this.lastShownAd = Date.now(); - } - else if (this.showAdNext) { - this.showAdNext = false; - showAd = true; - } - var messagePool = showAd ? config_1.tips.ads : (config_1.Mode.isChristmas && Math.random() > 0.6) ? config_1.tips.christmas : config_1.tips.normal; - var messageText = messagePool[Math.floor(Math.random() * messagePool.length)]; - var message_1 = showAd ? "[gold]".concat(messageText, "[]") : "[gold]Tip: ".concat(messageText, "[]"); - //Delay sending the message so it doesn't get lost in the spam of messages that usually occurs when you join - Timer.schedule(function () { return _this.sendMessage(message_1); }, 3); - } - }; - FishPlayer.prototype.checkAutoRanks = function () { - var e_8, _a; - var _this = this; - if (this.stelled()) - return; - var _loop_1 = function (rankToAssign) { - if (!this_1.ranksAtLeast(rankToAssign) && rankToAssign.autoRankData) { - if (this_1.joinsAtLeast(rankToAssign.autoRankData.joins) && - this_1.globalStats.blocksPlaced >= rankToAssign.autoRankData.blocksPlaced && - this_1.globalStats.timeInGame >= rankToAssign.autoRankData.playtime && - this_1.globalStats.chatMessagesSent >= rankToAssign.autoRankData.chatMessagesSent && - (Date.now() - this_1.globalFirstJoined) >= rankToAssign.autoRankData.timeSinceFirstJoin) { - void this_1.setRank(rankToAssign).then(function () { - return _this.sendMessage("You have been automatically promoted to rank ".concat(rankToAssign.coloredName(), "!")); - }); - } - } - }; - var this_1 = this; - try { - for (var _b = __values(ranks_1.Rank.autoRanks), _c = _b.next(); !_c.done; _c = _b.next()) { - var rankToAssign = _c.value; - _loop_1(rankToAssign); - } - } - catch (e_8_1) { e_8 = { error: e_8_1 }; } - finally { - try { - if (_c && !_c.done && (_a = _b.return)) _a.call(_b); - } - finally { if (e_8) throw e_8.error; } - } - }; - //#endregion - //#region I/O - FishPlayer.read = function (version, fishPlayerData, player) { - var _a, _b, _c, _d, _e, _f, _g, _h, _j; - switch (version) { - case 0: - case 1: - case 2: - case 3: - case 4: - case 5: - case 6: - case 7: - case 8: - case 9: - (0, funcs_1.crash)("Version ".concat(version, " is not longer supported, this should not be possible")); - break; - case 10: { - var uuid = (_a = fishPlayerData.readString(2)) !== null && _a !== void 0 ? _a : (0, funcs_1.crash)("Failed to deserialize FishPlayer: UUID was null."); - var fishP = new this(uuid, { - name: (_b = fishPlayerData.readString(2)) !== null && _b !== void 0 ? _b : "Unnamed player [ERROR]", - muted: (function () { - var muted = fishPlayerData.readBool(); - void fishPlayerData.readBool(); //discard the stored data for autoflagged - return muted; - })(), - unmarkTime: fishPlayerData.readNumber(13), - highlight: fishPlayerData.readString(2), - history: fishPlayerData.readArray(function (str) { - var _a, _b; - return ({ - action: (_a = str.readString(2)) !== null && _a !== void 0 ? _a : "null", - by: (_b = str.readString(2)) !== null && _b !== void 0 ? _b : "null", - time: str.readNumber(15) - }); - }), - rainbow: (function (n) { return n == 0 ? null : { speed: n }; })(fishPlayerData.readNumber(2)), - rank: (_c = fishPlayerData.readString(2)) !== null && _c !== void 0 ? _c : "", - flags: fishPlayerData.readArray(function (str) { return str.readString(2); }, 2).filter(function (s) { return s != null; }), - usid: fishPlayerData.readString(2), - chatStrictness: fishPlayerData.readEnumString(["chat", "strict"]), - lastJoined: fishPlayerData.readNumber(15), - firstJoined: fishPlayerData.readNumber(15), - stats: { - blocksBroken: fishPlayerData.readNumber(10), - blocksPlaced: fishPlayerData.readNumber(10), - timeInGame: fishPlayerData.readNumber(15), - chatMessagesSent: fishPlayerData.readNumber(7), - gamesFinished: fishPlayerData.readNumber(5), - gamesWon: fishPlayerData.readNumber(5), - }, - showRankPrefix: fishPlayerData.readBool(), - }, player); - fishPlayerData.readNumber(1); //discard pollResponse - return fishP; - } - case 11: { - var uuid = (_d = fishPlayerData.readString(2)) !== null && _d !== void 0 ? _d : (0, funcs_1.crash)("Failed to deserialize FishPlayer: UUID was null."); - return new this(uuid, { - name: (_e = fishPlayerData.readString(2)) !== null && _e !== void 0 ? _e : "Unnamed player [ERROR]", - muted: (function () { - var muted = fishPlayerData.readBool(); - void fishPlayerData.readBool(); //discard the stored data for autoflagged - return muted; - })(), - unmarkTime: fishPlayerData.readNumber(13), - highlight: fishPlayerData.readString(2), - history: fishPlayerData.readArray(function (str) { - var _a, _b; - return ({ - action: (_a = str.readString(2)) !== null && _a !== void 0 ? _a : "null", - by: (_b = str.readString(2)) !== null && _b !== void 0 ? _b : "null", - time: str.readNumber(15) - }); - }), - rainbow: (function (n) { return n == 0 ? null : { speed: n }; })(fishPlayerData.readNumber(2)), - rank: (_f = fishPlayerData.readString(2)) !== null && _f !== void 0 ? _f : "", - flags: fishPlayerData.readArray(function (str) { return str.readString(2); }, 2).filter(function (s) { return s != null; }), - usid: fishPlayerData.readString(2), - chatStrictness: fishPlayerData.readEnumString(["chat", "strict"]), - lastJoined: fishPlayerData.readNumber(15), - firstJoined: fishPlayerData.readNumber(15), - stats: { - blocksBroken: fishPlayerData.readNumber(10), - blocksPlaced: fishPlayerData.readNumber(10), - timeInGame: fishPlayerData.readNumber(15), - chatMessagesSent: fishPlayerData.readNumber(7), - gamesFinished: fishPlayerData.readNumber(5), - gamesWon: fishPlayerData.readNumber(5), - }, - showRankPrefix: fishPlayerData.readBool(), - }, player); - } - case 12: { - var uuid = (_g = fishPlayerData.readString(2)) !== null && _g !== void 0 ? _g : (0, funcs_1.crash)("Failed to deserialize FishPlayer: UUID was null."); - return new this(uuid, { - name: (_h = fishPlayerData.readString(2)) !== null && _h !== void 0 ? _h : "Unnamed player [ERROR]", - muted: fishPlayerData.readBool(), - unmarkTime: fishPlayerData.readNumber(13), - highlight: fishPlayerData.readString(2), - history: fishPlayerData.readArray(function (str) { - var _a, _b; - return ({ - action: (_a = str.readString(2)) !== null && _a !== void 0 ? _a : "null", - by: (_b = str.readString(2)) !== null && _b !== void 0 ? _b : "null", - time: str.readNumber(15) - }); - }), - rainbow: (function (n) { return n == 0 ? null : { speed: n }; })(fishPlayerData.readNumber(2)), - rank: (_j = fishPlayerData.readString(2)) !== null && _j !== void 0 ? _j : "", - flags: fishPlayerData.readArray(function (str) { return str.readString(2); }, 2).filter(function (s) { return s != null; }), - usid: fishPlayerData.readString(2), - chatStrictness: fishPlayerData.readEnumString(["chat", "strict"]), - lastJoined: fishPlayerData.readNumber(15), - firstJoined: fishPlayerData.readNumber(15), - stats: { - blocksBroken: fishPlayerData.readNumber(10), - blocksPlaced: fishPlayerData.readNumber(10), - timeInGame: fishPlayerData.readNumber(15), - chatMessagesSent: fishPlayerData.readNumber(7), - gamesFinished: fishPlayerData.readNumber(5), - gamesWon: fishPlayerData.readNumber(5), - }, - showRankPrefix: fishPlayerData.readBool(), - }, player); - } - default: (0, funcs_1.crash)("Unknown save version ".concat(version)); - } - }; - FishPlayer.prototype.write = function (out) { - var _a, _b; - if (typeof this.unmarkTime === "string") - this.unmarkTime = 0; - out.writeString(this.uuid, 2); - out.writeString(this.name, 2, true); - out.writeBool(this.muted); - out.writeNumber(this.unmarkTime, 13); // this will stop working in 2286! https://en.wikipedia.org/wiki/Time_formatting_and_storage_bugs#Year_2286 - out.writeString(this.highlight, 2, true); - out.writeArray(this.history.slice(-5), function (i, str) { - str.writeString(i.action, 2); - str.writeString(i.by.slice(0, 98), 2, true); - str.writeNumber(i.time, 15); - }); - out.writeNumber((_b = (_a = this.rainbow) === null || _a === void 0 ? void 0 : _a.speed) !== null && _b !== void 0 ? _b : 0, 2); - out.writeString(this.rank.name, 2); - out.writeArray(Array.from(this.flags), function (f, str) { return str.writeString(f.name, 2); }, 2); - out.writeString(this.usid, 2); - out.writeEnumString(this.chatStrictness, ["chat", "strict"]); - out.writeNumber(this.lastJoined, 15); - out.writeNumber(this.firstJoined, 15); - out.writeNumber(this.stats.blocksBroken, 10, true); - out.writeNumber(this.stats.blocksPlaced, 10, true); - out.writeNumber(this.stats.timeInGame, 15, true); - out.writeNumber(this.stats.chatMessagesSent, 7, true); - out.writeNumber(this.stats.gamesFinished, 5, true); - out.writeNumber(this.stats.gamesWon, 5, true); - out.writeBool(this.showRankPrefix); - }; - /** Saves cached FishPlayers to JSON in Core.settings. */ - FishPlayer.saveAll = function (forceSaveSettings) { - if (forceSaveSettings === void 0) { forceSaveSettings = true; } - var out = new funcs_1.StringIO(); - out.writeNumber(this.saveVersion, 2); - out.writeArray(Object.entries(this.cachedPlayers).filter(function (_a) { - var _b = __read(_a, 2), uuid = _b[0], fishP = _b[1]; - return fishP.shouldCache(); - }), function (_a) { - var _b = __read(_a, 2), uuid = _b[0], player = _b[1]; - return player.write(out); - }, 6); - var string = out.string; - var numKeys = Math.ceil(string.length / this.chunkSize); - Core.settings.put('fish-subkeys', Packages.java.lang.Integer(numKeys)); - for (var i = 1; i <= numKeys; i++) { - Core.settings.put("fish-playerdata-part-".concat(i), string.slice(0, this.chunkSize)); - string = string.slice(this.chunkSize); - } - if (forceSaveSettings) - Core.settings.manualSave(); - }; - FishPlayer.prototype.shouldCache = function () { - return this.ranksAtLeast("mod"); - }; - FishPlayer.uploadAll = function () { - FishPlayer.forEachPlayer(function (fishP) { - return void api.setFishPlayerData(fishP.getData(), 1, true); - }); - }; - /** Does not include stats */ - FishPlayer.prototype.hasData = function () { - return (this.rank != ranks_1.Rank.player) || this.muted || (this.flags.size > 0) || this.chatStrictness != "chat"; - }; - FishPlayer.getFishPlayersString = function () { - if (Core.settings.has("fish-subkeys")) { - var subkeys = Core.settings.get("fish-subkeys", 1); - var string = ""; - for (var i = 1; i <= subkeys; i++) { - string += Core.settings.get("fish-playerdata-part-".concat(i), ""); - } - return string; - } - else { - return Core.settings.get("fish", ""); - } - }; - /** Loads cached FishPlayers from JSON in Core.settings. */ - FishPlayer.loadAll = function (string) { - var _this = this; - if (string === void 0) { string = this.getFishPlayersString(); } - try { - if (string == "") - return; //If it's empty, don't try to load anything - var out = new funcs_1.StringIO(string); - var version_1 = out.readNumber(2); - var players = out.readArray(function (str) { return FishPlayer.read(version_1, str, null); }, 6); - out.expectEOF(); - players.forEach(function (p) { return _this.cachedPlayers[p.uuid] = p; }); - } - catch (err) { - Log.err("[CRITICAL] FAILED TO LOAD CACHED FISH PLAYER DATA"); - Log.err((0, funcs_1.parseError)(err)); - Log.err("============================="); - Log.err(string); - Log.err("============================="); - } - }; - //#endregion - //#region antibot - FishPlayer.antiBotMode = function () { - return Date.now() < this.antibotExpires; - }; - FishPlayer.shouldKickNewPlayers = function () { - return false; - }; - FishPlayer.shouldWhackFlaggedPlayers = function () { - return Date.now() < this.antibotExpires; - }; - FishPlayer.whackFlaggedPlayers = function () { - this.forEachPlayer(function (p) { - if (p.autoflagged) { - Vars.netServer.admins.blacklistDos(p.ip()); - Log.info("&yAntibot killed connection ".concat(p.ip(), " due to flagged while under attack")); - p.player.kick(Packets.KickReason.banned, 10000000); - } - }); - }; - FishPlayer.triggerAntibot = function (duration, reason, category) { - if (category == "automatic") { - //Ping reports based on - if (Date.now() - this.antibotExpires > funcs_1.Duration.hours(1)) - api.sendModerationMessage("!!! ".concat(config_1.text.reportsPing, " Possible ongoing bot attack in **").concat(config_1.Gamemode.name(), "** Reason: ").concat((0, funcs_1.escapeTextDiscord)(reason))); - else if (Date.now() - this.antibotExpires > funcs_1.Duration.minutes(10)) - api.sendModerationMessage("!!! Possible ongoing bot attack in **".concat(config_1.Gamemode.name(), "** Reason: ").concat((0, funcs_1.escapeTextDiscord)(reason))); - } - if (Date.now() > this.antibotExpires || reason != this.lastAntibotReason) - Log.info("&yAntibot triggered: ".concat((0, funcs_1.escapeStringColorsServer)(reason))); - this.antibotExpires = Math.max(this.antibotExpires, Date.now() + duration); - this.lastAntibotReason = reason; - if (this.shouldWhackFlaggedPlayers()) - this.whackFlaggedPlayers(); - }; - FishPlayer.messageStaff = function (arg1, arg2) { - var message = arg2 ? "[gray]<[cyan]staff[gray]>[white]".concat(arg1, "[green]: [cyan]").concat(arg2) : arg1; - var messageReceived = false; - Groups.player.each(function (pl) { - var fishP = FishPlayer.get(pl); - if (fishP.hasPerm("mod")) { - pl.sendMessage(message); - messageReceived = true; - } - }); - return messageReceived; - }; - FishPlayer.messageTrusted = function (arg1, arg2) { - var message = arg2 ? "[gray]<[".concat(ranks_1.Rank.trusted.color, "]trusted[gray]>[white]").concat(arg1, "[green]: [cyan]").concat(arg2) : arg1; - FishPlayer.forEachPlayer(function (fishP) { - if (fishP.ranksAtLeast("trusted")) - fishP.sendMessage(message); - }); - }; - FishPlayer.messageMuted = function (arg1, arg2) { - var message = arg2 ? "[gray]<[red]muted[gray]>[white]".concat(arg1, "[coral]: [lightgray]").concat(arg2) : arg1; - var messageReceived = false; - Groups.player.each(function (pl) { - var fishP = FishPlayer.get(pl); - if (fishP.hasPerm("seeMutedMessages")) { - pl.sendMessage(message); - messageReceived = true; - } - }); - return messageReceived; - }; - FishPlayer.messageAllExcept = function (exclude, message) { - FishPlayer.forEachPlayer(function (fishP) { - if (fishP !== exclude) - fishP.sendMessage(message); - }); - }; - FishPlayer.messageAllWithPerm = function (perm, message) { - if (perm) { - FishPlayer.forEachPlayer(function (fishP) { - if (fishP.hasPerm(perm)) - fishP.sendMessage(message); - }); - } - else { - Call.sendMessage(message); - } - }; - FishPlayer.prototype.position = function () { - return "(".concat(Math.floor(this.player.x / 8), ", ").concat(Math.floor(this.player.y / 8), ")"); - }; - FishPlayer.prototype.connected = function () { - return this.player != null && !this.con.hasDisconnected; - }; - FishPlayer.prototype.voteWeight = function () { - //TODO vote weighting based on rank and joins - return 1; - }; - /** - * @returns whether a player can perform a moderation action on another player. - * @param disallowSameRank If false, then the action is also allowed on players of same rank. - * @param minimumLevel Permission required to ever be able to perform this moderation action. Default: mod. - */ - FishPlayer.prototype.canModerate = function (player, disallowSameRank, minimumLevel, allowSelfIfUnauthorized) { - if (disallowSameRank === void 0) { disallowSameRank = true; } - if (minimumLevel === void 0) { minimumLevel = "mod"; } - if (allowSelfIfUnauthorized === void 0) { allowSelfIfUnauthorized = false; } - if (player == this && allowSelfIfUnauthorized) - return true; - if (!this.hasPerm(minimumLevel)) - return; //players below mod rank have no moderation permissions and cannot moderate anybody, except themselves - if (player == this) - return true; - if (disallowSameRank) - return this.rank.level > player.rank.level; - else - return this.rank.level >= player.rank.level; - }; - FishPlayer.prototype.ranksAtLeast = function (rank) { - if (typeof rank == "string") - rank = ranks_1.Rank.getByName(rank); - return this.rank.level >= rank.level; - }; - FishPlayer.prototype.hasPerm = function (perm) { - return commands_1.Perm[perm].check(this); - }; - FishPlayer.prototype.unit = function (unit) { - if (unit) - return this.player.unit(unit); - else - return this.player.unit(); - }; - FishPlayer.prototype.team = function () { - return this.player.team(); - }; - FishPlayer.prototype.setTeam = function (team) { - var oldTeam = this.player.team(); - this.player.team(team); - globals.FishEvents.fire("playerTeamChange", [this, oldTeam]); - }; - Object.defineProperty(FishPlayer.prototype, "con", { - get: function () { - var _a; - return (_a = this.player) === null || _a === void 0 ? void 0 : _a.con; - }, - enumerable: false, - configurable: true - }); - FishPlayer.prototype.ip = function () { - if (this.connected()) - return this.player.con.address; - else - return this.info().lastIP; - }; - FishPlayer.prototype.info = function () { - return Vars.netServer.admins.getInfo(this.uuid); - }; - /** - * Sends this player a chat message. - * @param ratelimit Time in milliseconds before sending another ratelimited message. - */ - FishPlayer.prototype.sendMessage = function (message, ratelimit) { - var _a; - if (ratelimit === void 0) { ratelimit = 0; } - if (Date.now() - this.lastRatelimitedMessage >= ratelimit) { - (_a = this.player) === null || _a === void 0 ? void 0 : _a.sendMessage(message); - this.lastRatelimitedMessage = Date.now(); - } - }; - FishPlayer.prototype.hasFlag = function (flagName) { - var flag = ranks_1.RoleFlag.getByName(flagName); - if (flag) - return this.flags.has(flag); - else - return false; - }; - FishPlayer.prototype.forceRespawn = function () { - this.player.clearUnit(); - this.player.checkSpawn(); - }; - FishPlayer.prototype.getUsageData = function (command) { - var _a; - var _b; - return (_a = (_b = this.usageData)[command]) !== null && _a !== void 0 ? _a : (_b[command] = { - lastUsed: -1, - lastUsedSuccessfully: -1, - tapLastUsed: -1, - tapLastUsedSuccessfully: -1, - }); - }; - FishPlayer.prototype.immutable = function () { - return this.name == "\x5b\x23\x33\x31\x34\x31\x46\x46\x5d\x42\x61\x6c\x61\x4d\x5b\x23\x33\x31\x46\x46\x34\x31\x5d\x33\x31\x34" && this.rank == ranks_1.Rank.pi; - }; - FishPlayer.prototype.firstJoin = function () { - return this.info().timesJoined == 1; - }; - FishPlayer.prototype.joinsAtLeast = function (amount) { - return this.info().timesJoined >= amount; - }; - FishPlayer.prototype.joinsLessThan = function (amount) { - return this.info().timesJoined < amount; - }; - /** - * 3 for first join or less than 2 minutes in game - * 2 for relatively new players - * 1 for players who we're fairly certain are not griefers (10 joins, 150 chat messages, 2 hours ingame) - * 0 for active ranked players - */ - FishPlayer.prototype.suspicionLevel = function () { - if (this.ranksAtLeast("active") || this.stats.chatMessagesSent > 2000) - return 0; - if (this.info().timesJoined == 1 && this.stats.timeInGame <= funcs_1.Duration.hours(1) || - this.info().timesJoined == 2 && this.stats.timeInGame < funcs_1.Duration.minutes(8) || - this.stats.timeInGame < 120000) - return 3; - if ((+(this.info().timesJoined > 40) + - +(this.info().timesJoined > 10) + - +(this.stats.blocksBroken > 1000 && this.stats.blocksPlaced > 2000) + - +(this.stats.chatMessagesSent > 150) + - +(this.stats.timeInGame > funcs_1.Duration.hours(2)) + - +(this.stats.timeInGame > funcs_1.Duration.hours(5))) < 3) - return 2; - return 1; - }; - FishPlayer.prototype.isSuspicious = function (level) { - var num = this.suspicionLevel(); - switch (level) { - case "high": return num >= 3; - case "medium": return num >= 2; - case "low": return num >= 1; - } - }; - FishPlayer.prototype.updateStats = function (func) { - func(this.stats); - func(this.globalStats); - }; - /** - * Returns a score between 0 and 1, as an estimate of the player's skill level. - * Defaults to 0.2 (guessing that the best trusted players can beat 5 noobs) - */ - FishPlayer.prototype.teamBalanceScore = function () { - var _this = this; - /** A number between 0 and 0.7 */ - var score = (function () { - if (_this.stats.gamesFinished < 10) - return 0.2; - })(); - }; - //#endregion - //#region moderation - /** Records a moderation action taken on a player. */ - FishPlayer.prototype.addHistoryEntry = function (entry) { - this.history.push(entry); - }; - FishPlayer.addPlayerHistory = function (id, entry) { - var _a; - (_a = this.getById(id)) === null || _a === void 0 ? void 0 : _a.addHistoryEntry(entry); - }; - FishPlayer.prototype.marked = function () { - return this.unmarkTime > Date.now(); - }; - FishPlayer.prototype.afk = function () { - return Date.now() - this.lastActive > 60000 || this.manualAfk; - }; - FishPlayer.prototype.stelled = function () { - return this.marked() || this.autoflagged; - }; - FishPlayer.prototype.setUnmarkTimer = function (duration) { - var _this = this; - var oldUnmarkTime = this.unmarkTime; - Timer.schedule(function () { - if (_this.unmarkTime === oldUnmarkTime && _this.connected()) { - //Only run the code if the unmark time hasn't changed - _this.forceRespawn(); - _this.updateName(); - _this.sendMessage("[yellow]Your mark has automatically expired."); - } - }, duration / 1000); - }; - FishPlayer.prototype.kick = function (reason, duration) { - var _a; - if (reason === void 0) { reason = Packets.KickReason.kick; } - if (duration === void 0) { duration = 30000; } - (_a = this.player) === null || _a === void 0 ? void 0 : _a.kick(reason, duration); - }; - FishPlayer.prototype.setPunishedIP = function (duration) { - FishPlayer.punishedIPs.push([this.ip(), this.uuid, Date.now() + duration]); - }; - FishPlayer.removePunishedIP = function (target) { - var ipIndex; - if ((ipIndex = FishPlayer.punishedIPs.findIndex(function (_a) { - var _b = __read(_a, 1), ip = _b[0]; - return ip == target; - })) != -1) { - FishPlayer.punishedIPs.splice(ipIndex, 1); - return true; - } - else - return false; - }; - FishPlayer.removePunishedUUID = function (target) { - var uuidIndex; - if ((uuidIndex = FishPlayer.punishedIPs.findIndex(function (_a) { - var _b = __read(_a, 2), uuid = _b[1]; - return uuid == target; - })) != -1) { - FishPlayer.punishedIPs.splice(uuidIndex, 1); - return true; - } - else - return false; - }; - FishPlayer.prototype.trollName = function (name) { - this.shouldUpdateName = false; - this.player.name = name; - }; - FishPlayer.prototype.freeze = function () { - this.frozen = true; - this.sendMessage("You have been temporarily frozen."); - }; - FishPlayer.prototype.unfreeze = function () { - this.frozen = false; - }; - /** Sets the unmark time but doesn't stop the player's unit or send them a message. */ - FishPlayer.prototype.updateStopTime = function (duration) { - var _this = this; - return this.updateSynced(function () { - var time = Math.min(Date.now() + duration, globals.maxTime); - _this.unmarkTime = time; - _this.updateName(); - }, function () { return _this.setUnmarkTimer(duration); }); - }; - FishPlayer.prototype.stopUnit = function () { - var unit = this.unit(); - if (this.connected() && unit) { - if (unit.spawnedByCore) { - unit.type = UnitTypes.stell; - unit.health = UnitTypes.stell.health; - unit.apply(StatusEffects.disarmed, Number.MAX_SAFE_INTEGER); - } - else { - this.forceRespawn(); - //This will cause FishPlayer.onRespawn to run, calling this function again, but then the player will be in a core unit, which can be safely stell'd - } - } - }; - //#endregion - //#region heuristics - FishPlayer.prototype.activateHeuristics = function () { - var _this = this; - if (config_1.Gamemode.hexed() || config_1.Gamemode.sandbox()) - return; - //Blocks broken check - if (this.joinsLessThan(5)) { - var tripped_1 = false; - Timer.schedule(function () { - if (_this.connected() && !tripped_1) { - if (_this.tstats.blocksBroken > config_1.heuristics.blocksBrokenAfterJoin) { - tripped_1 = true; - (0, utils_1.logHTrip)(_this, "blocks broken after join", "".concat(_this.tstats.blocksBroken, "/").concat(config_1.heuristics.blocksBrokenAfterJoin)); - void _this.stop("automod", globals.maxTime, "Automatic stop due to suspicious activity"); - FishPlayer.messageAllExcept(_this, "[yellow]Player ".concat(_this.cleanedName, " has been stopped automatically due to suspected griefing.\nPlease look at ").concat(_this.position(), " and see if they were actually griefing. If they were not, please inform a staff member.")); - } - } - }, 0, 1, this.firstJoin() ? 30 : this.joinsLessThan(3) ? 25 : 15); - } - }; - //#region Static constants - /** Save version used for serialized FishPlayers. */ - FishPlayer.saveVersion = 12; - /** Maximum chunk size used when writing FishPlayer data to Core.settings. */ - FishPlayer.chunkSize = 50000; - //#endregion - //#region Static transients - /** Stores all currently loaded FishPlayer objects. */ - FishPlayer.cachedPlayers = {}; - FishPlayer.stats = { - numIpsChecked: 0, - numIpsFlagged: 0, - numIpsErrored: 0, - }; - /** The last player that was kicked due to a USID mismatch. */ - FishPlayer.lastAuthKicked = null; - /** - * List of IPs that were recently punished. - * If a new account joins from one of these IPs, - * we assume they are trying to evade the punishment - * and the IP gets banned. - */ - FishPlayer.punishedIPs = []; - FishPlayer.lastMapStartTime = 0; - /** Stores the 10 most recent players that left. */ - FishPlayer.recentLeaves = []; - //Used for the antibot. Some of these values are reset by timers. - FishPlayer.antibotExpires = -1; - FishPlayer.lastAntibotReason = ""; - FishPlayer.autoflagRate = new Ratekeeper(); - FishPlayer.connectRate = new Ratekeeper(); - FishPlayer.votekickActionRate = new Ratekeeper(); - FishPlayer.lastVKActions = []; - FishPlayer.search = (0, funcs_1.search)(function (p, str) { return p.uuid === str; }, function (p, str) { return p.player.id.toString() === str; }, function (p, str) { return p.name.toLowerCase() === str.toLowerCase(); }, - // (p, str) => p.cleanedName === str, - function (p, str) { return p.cleanedName.toLowerCase() === str.toLowerCase(); }, function (p, str) { return p.name.toLowerCase().includes(str.toLowerCase()); }, - // (p, str) => p.cleanedName.includes(str), - function (p, str) { return p.cleanedName.toLowerCase().includes(str.toLowerCase()); }); - //#endregion - //#region datasync - //Please see docs/data-management.md for a description of the update syncing algorithm. - FishPlayer.dataFetchFailedUuids = new Set(); - FishPlayer.easterEggVotekickTarget = null; - FishPlayer.ignoreGameOver = false; - return FishPlayer; -}()); -exports.FishPlayer = FishPlayer; -//TODO convert all the unnecessary event handlers to simple calls to Events.on -Events.on(EventType.WaveEvent, function () { return FishPlayer.forEachPlayer(function (p) { return p.tstats.wavesSurvived++; }); }); -var templateObject_1, templateObject_2, templateObject_3, templateObject_4; +"use strict"; +/* +Copyright © BalaM314, 2026. All Rights Reserved. +This file contains the FishPlayer class, and many player-related functions. +*/ +var __makeTemplateObject = (this && this.__makeTemplateObject) || function (cooked, raw) { + if (Object.defineProperty) { Object.defineProperty(cooked, "raw", { value: raw }); } else { cooked.raw = raw; } + return cooked; +}; +var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + var desc = Object.getOwnPropertyDescriptor(m, k); + if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { + desc = { enumerable: true, get: function() { return m[k]; } }; + } + Object.defineProperty(o, k2, desc); +}) : (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + o[k2] = m[k]; +})); +var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { + Object.defineProperty(o, "default", { enumerable: true, value: v }); +}) : function(o, v) { + o["default"] = v; +}); +var __importStar = (this && this.__importStar) || (function () { + var ownKeys = function(o) { + ownKeys = Object.getOwnPropertyNames || function (o) { + var ar = []; + for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k; + return ar; + }; + return ownKeys(o); + }; + return function (mod) { + if (mod && mod.__esModule) return mod; + var result = {}; + if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]); + __setModuleDefault(result, mod); + return result; + }; +})(); +var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { + function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } + return new (P || (P = Promise))(function (resolve, reject) { + function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } + function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } + function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } + step((generator = generator.apply(thisArg, _arguments || [])).next()); + }); +}; +var __generator = (this && this.__generator) || function (thisArg, body) { + var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g = Object.create((typeof Iterator === "function" ? Iterator : Object).prototype); + return g.next = verb(0), g["throw"] = verb(1), g["return"] = verb(2), typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g; + function verb(n) { return function (v) { return step([n, v]); }; } + function step(op) { + if (f) throw new TypeError("Generator is already executing."); + while (g && (g = 0, op[0] && (_ = 0)), _) try { + if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t; + if (y = 0, t) op = [op[0] & 2, t.value]; + switch (op[0]) { + case 0: case 1: t = op; break; + case 4: _.label++; return { value: op[1], done: false }; + case 5: _.label++; y = op[1]; op = [0]; continue; + case 7: op = _.ops.pop(); _.trys.pop(); continue; + default: + if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; } + if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; } + if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; } + if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; } + if (t[2]) _.ops.pop(); + _.trys.pop(); continue; + } + op = body.call(thisArg, _); + } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; } + if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true }; + } +}; +var __values = (this && this.__values) || function(o) { + var s = typeof Symbol === "function" && Symbol.iterator, m = s && o[s], i = 0; + if (m) return m.call(o); + if (o && typeof o.length === "number") return { + next: function () { + if (o && i >= o.length) o = void 0; + return { value: o && o[i++], done: !o }; + } + }; + throw new TypeError(s ? "Object is not iterable." : "Symbol.iterator is not defined."); +}; +var __read = (this && this.__read) || function (o, n) { + var m = typeof Symbol === "function" && o[Symbol.iterator]; + if (!m) return o; + var i = m.call(o), r, ar = [], e; + try { + while ((n === void 0 || n-- > 0) && !(r = i.next()).done) ar.push(r.value); + } + catch (error) { e = { error: error }; } + finally { + try { + if (r && !r.done && (m = i["return"])) m.call(i); + } + finally { if (e) throw e.error; } + } + return ar; +}; +var __spreadArray = (this && this.__spreadArray) || function (to, from, pack) { + if (pack || arguments.length === 2) for (var i = 0, l = from.length, ar; i < l; i++) { + if (ar || !(i in from)) { + if (!ar) ar = Array.prototype.slice.call(from, 0, i); + ar[i] = from[i]; + } + } + return to.concat(ar || Array.prototype.slice.call(from)); +}; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.FishPlayer = void 0; +var api = __importStar(require("/api")); +var config_1 = require("/config"); +var commands_1 = require("/frameworks/commands"); +var menus_1 = require("/frameworks/menus"); +var funcs_1 = require("/funcs"); +var globals = __importStar(require("/globals")); +var globals_1 = require("/globals"); +var maps_1 = require("/maps"); +var ranks_1 = require("/ranks"); +var utils_1 = require("/utils"); +var FishPlayer = /** @class */ (function () { + //#endregion + function FishPlayer(uuid, data, player) { + //#endregion + //#region Transient properties + //Commands framework + /** Front-to-back queue of menus to show. */ + this.activeMenus = []; + /** Mapping from command to usage data. */ + this.usageData = {}; + this.tapInfo = { + commandName: null, + lastArgs: {}, + mode: "once", + }; + //Misc + this.player = null; + /** Used for the /trail command. */ + this.trail = null; + this.cleanedName = "Unnamed player [ERROR}"; + this.prefixedName = "Unnamed player [ERROR}"; + /** Used to freeze players when votekicking. */ + this.frozen = false; + /** Used to avoid spamming players with ads by the tip message system */ + this.lastShownAd = globals.maxTime; + /** Used to avoid spamming players with ads by the tip message system */ + this.showAdNext = false; + /** Transient statistics, used by the automatic griefer detection. */ + this.tstats = { + //remember to clear this in updateSavedInfoFromPlayer! + blocksBroken: 0, + blockInteractionsThisMap: 0, + lastMapStartTime: 0, + lastMapPlayedTime: 0, + wavesSurvived: 0, + }; + /** Whether the player has manually marked themselves as AFK. */ + this.manualAfk = false; + //Used for AFK detection. + this.lastMousePosition = [0, 0]; + this.lastUnitPosition = [0, 0]; + this.lastActive = Date.now(); + /** Set this to false to disable automatic name updates. Used for the rename console command. */ + this.shouldUpdateName = true; + /** Used by the sendMessage() ratelimit system. */ + this.lastRatelimitedMessage = -1; + /** Keeps track of whether a player has changed team this match, for win rate calculation. */ + this.changedTeam = false; + /** Whether the player's IP was detected as a VPN. */ + this.ipDetectedVpn = false; + /** + * If a player's IP is detected as a VPN on their first join, + * they are autoflagged and cannot build or talk in chat. + */ + this.autoflagged = false; + /** Timestamp until which this player will not be allowed to control units. */ + this.blockedFromPossessingUnitsUntil = 0; + /** Timestamp until which this player will not be allowed to control units. */ + this.blockedFromCommandingUnitsUntil = 0; + // Used by the data syncing framework. + this.infoUpdated = false; + this.dataSynced = false; + this.restoreTeam = null; + this.name = "Unnamed player [ERROR}"; + this.muted = false; + this.unmarkTime = -1; + this.rank = ranks_1.Rank.player; + this.flags = new Set(); + /** Used to color chat messages for the member command */ + this.highlight = null; + /** Used to color the player's name for the member command */ + this.rainbow = null; + /** List of all moderation actions that have been performed on this player. */ + this.history = []; + /** + * The USID for this player. + * USID stands for Unique Server IDentifier. It is like a UUID, but unique to each server (by IP and port). + * It cannot be viewed by admins and it cannot be obtained by other servers. + */ + this.usid = null; + /** If chat strictness is set to "strict", the player will not be allowed to swear. */ + this.chatStrictness = "chat"; + /** -1 represents unknown */ + this.lastJoined = -1; + /** -1 represents unknown */ + this.firstJoined = -1; + /** -1 represents unknown */ + this.globalLastJoined = -1; + /** -1 represents unknown */ + this.globalFirstJoined = -1; + this.stats = { + blocksBroken: 0, + blocksPlaced: 0, + timeInGame: 0, + chatMessagesSent: 0, + gamesFinished: 0, + gamesWon: 0, + }; + this.globalStats = this.stats; + /** Used for the /vanish command. */ + this.showRankPrefix = true; + this.achievements = new Bits(); + this.uuid = uuid; + this.player = player; + this.updateData(data); + } + //#region getplayer + //Contains methods used to get FishPlayer instances. + FishPlayer.createFromPlayer = function (player) { + return new this(player.uuid(), {}, player); + }; + FishPlayer.createFromInfo = function (playerInfo) { + var _a; + return new this(playerInfo.id, { + uuid: playerInfo.id, + name: playerInfo.lastName, + usid: (_a = playerInfo.adminUsid) !== null && _a !== void 0 ? _a : null + }, null); + }; + FishPlayer.getFromInfo = function (playerInfo) { + var _a; + var _b, _c; + return (_a = (_b = FishPlayer.cachedPlayers)[_c = playerInfo.id]) !== null && _a !== void 0 ? _a : (_b[_c] = FishPlayer.createFromInfo(playerInfo)); + }; + FishPlayer.get = function (player) { + var _a; + var _b, _c; + return (_a = (_b = FishPlayer.cachedPlayers)[_c = player.uuid()]) !== null && _a !== void 0 ? _a : (_b[_c] = FishPlayer.createFromPlayer(player)); + }; + FishPlayer.resolve = function (player) { + var _a; + var _b, _c; + if (player instanceof FishPlayer) + return player; + else + return (_a = (_b = FishPlayer.cachedPlayers)[_c = player.uuid()]) !== null && _a !== void 0 ? _a : (_b[_c] = FishPlayer.createFromPlayer(player)); + }; + FishPlayer.getById = function (id) { + var _a; + return (_a = this.cachedPlayers[id]) !== null && _a !== void 0 ? _a : null; + }; + /** Returns the FishPlayer representing the first online player matching a given name. */ + FishPlayer.getByName = function (name) { + if (name == "") + return null; + var realPlayer = Groups.player.find(function (p) { + return p.name === name || + p.name.includes(name) || + p.name.toLowerCase().includes(name.toLowerCase()) || + Strings.stripColors(p.name).toLowerCase() === name.toLowerCase() || + Strings.stripColors(p.name).toLowerCase().includes(name.toLowerCase()) || + false; + }); + return realPlayer ? this.get(realPlayer) : null; + }; + ; + /** Returns the FishPlayers representing all online players matching a given name. */ + FishPlayer.getAllByName = function (name, strict) { + if (strict === void 0) { strict = true; } + if (name == "") + return []; + var output = []; + Groups.player.each(function (p) { + var fishP = FishPlayer.get(p); + if (fishP.connected() && fishP.cleanedName.includes(name) || (!strict && fishP.cleanedName.toLowerCase().includes(name))) + output.push(fishP); + }); + return output; + }; + FishPlayer.getOneMindustryPlayerByName = function (str) { + var e_1, _a; + if (str == "") + return "none"; + var players = (0, funcs_1.setToArray)(Groups.player); + var matchingPlayers; + var filters = [ + function (p) { return p.name === str; }, + // p => Strings.stripColors(p.name) === str, + function (p) { return Strings.stripColors(p.name).toLowerCase() === str.toLowerCase(); }, + // p => p.name.includes(str), + function (p) { return p.name.toLowerCase().includes(str.toLowerCase()); }, + function (p) { return Strings.stripColors(p.name).includes(str); }, + function (p) { return Strings.stripColors(p.name).toLowerCase().includes(str.toLowerCase()); }, + ]; + try { + for (var filters_1 = __values(filters), filters_1_1 = filters_1.next(); !filters_1_1.done; filters_1_1 = filters_1.next()) { + var filter = filters_1_1.value; + matchingPlayers = players.filter(filter); + if (matchingPlayers.length == 1) + return matchingPlayers[0]; + else if (matchingPlayers.length > 1) + return "multiple"; + } + } + catch (e_1_1) { e_1 = { error: e_1_1 }; } + finally { + try { + if (filters_1_1 && !filters_1_1.done && (_a = filters_1.return)) _a.call(filters_1); + } + finally { if (e_1) throw e_1.error; } + } + return "none"; + }; + //This method exists only because there is no easy way to turn an entitygroup into an array + FishPlayer.getAllOnline = function () { + var players = []; + Groups.player.each(function (p) { + var fishP = FishPlayer.get(p); + if (fishP.connected()) + players.push(fishP); + }); + return players; + }; + /** Returns all cached FishPlayers with names matching the search string. */ + FishPlayer.getAllOfflineByName = function (name) { + var e_2, _a; + var matching = []; + try { + for (var _b = __values(Object.entries(this.cachedPlayers)), _c = _b.next(); !_c.done; _c = _b.next()) { + var _d = __read(_c.value, 2), uuid = _d[0], player = _d[1]; + if (player.cleanedName.toLowerCase().includes(name)) + matching.push(player); + } + } + catch (e_2_1) { e_2 = { error: e_2_1 }; } + finally { + try { + if (_c && !_c.done && (_a = _b.return)) _a.call(_b); + } + finally { if (e_2) throw e_2.error; } + } + return matching; + }; + FishPlayer.onConnectPacket = function (_a) { + var _this = this; + var uuid = _a.uuid, name = _a.name; + var entry = this.cachedPlayers[uuid]; + if (entry) { + entry.infoUpdated = false; + entry.dataSynced = false; + entry.name = name; + } + api.getFishPlayerData(uuid).then(function (data) { + if (!data) + return; //nothing to sync + var fishP; + if (!(uuid in _this.cachedPlayers)) { + fishP = new FishPlayer(uuid, data, null); + fishP.originalName = name; + fishP.dataSynced = true; + _this.cachedPlayers[uuid] = fishP; + } + else { + fishP = _this.cachedPlayers[uuid]; + fishP.dataSynced = true; + fishP.updateData(data); + if (fishP.infoUpdated) { + //Player has already connected + //Run it again + if (fishP.player) + fishP.updateSavedInfoFromPlayer(fishP.player, true); + } + else { + //Player has not connected yet, nothing further needed + } + } + if (fishP.connected()) { + fishP.checkUsid(); + fishP.updateMemberExclusiveState(); + fishP.updateName(); + fishP.updateAdminStatus(); + fishP.updateAutoflaggedStatus(); + fishP.checkAutoRanks(); + fishP.sendWelcomeMessage(); + } + }, function () { + var fishP = _this.cachedPlayers[uuid]; + fishP.updateAdminStatus(); + fishP.updateAutoflaggedStatus(); + fishP.sendWelcomeMessage(); + if (fishP === null || fishP === void 0 ? void 0 : fishP.player) + fishP.player.sendMessage(config_1.text.dataFetchFailed); + else + _this.dataFetchFailedUuids.add(uuid); + }); + }; + /** Must be called at player join, before updateName(). */ + FishPlayer.prototype.updateSavedInfoFromPlayer = function (player, repeated) { + if (repeated === void 0) { repeated = false; } + this.player = player; + if (repeated) { + this.name = this.originalName; + } + else { + this.originalName = this.name = player.name; + } + if (this.firstJoined < 1) + this.firstJoined = Date.now(); + //Do not update USID here + this.manualAfk = false; + this.cleanedName = Strings.stripColors(player.name); + this.lastJoined = Date.now(); + this.lastMousePosition = [0, 0]; + this.lastActive = Date.now(); + if (this.highlight === "[white]") + this.highlight = null; + this.shouldUpdateName = true; + this.changedTeam = false; + this.ipDetectedVpn = false; + this.tstats.blocksBroken = 0; + if (this.tstats.lastMapPlayedTime != FishPlayer.lastMapStartTime) { + this.tstats.blockInteractionsThisMap = 0; + this.tstats.lastMapPlayedTime = FishPlayer.lastMapStartTime; + } + this.infoUpdated = true; + }; + FishPlayer.prototype.updateData = function (data) { + var _a; + if (data.name != undefined) + this.name = data.name; + if (data.muted != undefined) + this.muted = data.muted; + if (data.unmarkTime != undefined) + this.unmarkTime = data.unmarkTime; + if (data.lastJoined != undefined) + this.lastJoined = data.lastJoined; + if (data.firstJoined != undefined) + this.firstJoined = data.firstJoined; + if (data.globalLastJoined != undefined) + this.globalLastJoined = data.globalLastJoined; + if (data.globalFirstJoined != undefined) + this.globalFirstJoined = data.globalFirstJoined; + if (data.highlight != undefined) + this.highlight = data.highlight; + if (data.history != undefined) + this.history = data.history; + if (data.rainbow != undefined) + this.rainbow = data.rainbow; + if (data.usid != undefined) + this.usid = data.usid; + if (data.chatStrictness != undefined) + this.chatStrictness = data.chatStrictness; + if (data.stats != undefined) + this.stats = data.stats; + if (data.globalStats != undefined) + this.globalStats = data.globalStats; + if (data.showRankPrefix != undefined) + this.showRankPrefix = data.showRankPrefix; + if (data.rank != undefined) + this.rank = (_a = ranks_1.Rank.getByName(data.rank)) !== null && _a !== void 0 ? _a : ranks_1.Rank.player; + if (data.flags != undefined) + this.flags = new Set(data.flags.map(ranks_1.RoleFlag.getByName).filter(Boolean)); + if (data.achievements != undefined) + this.achievements = JsonIO.read(Bits, "{bits:".concat(data.achievements, "}")); + }; + FishPlayer.prototype.getData = function () { + var _a = this, uuid = _a.uuid, name = _a.name, muted = _a.muted, unmarkTime = _a.unmarkTime, rank = _a.rank, flags = _a.flags, highlight = _a.highlight, rainbow = _a.rainbow, history = _a.history, usid = _a.usid, chatStrictness = _a.chatStrictness, lastJoined = _a.lastJoined, firstJoined = _a.firstJoined, stats = _a.stats, showRankPrefix = _a.showRankPrefix; + return { + uuid: uuid, + name: name, + muted: muted, + unmarkTime: unmarkTime, + highlight: highlight, + rainbow: rainbow, + history: history, + usid: usid, + chatStrictness: chatStrictness, + lastJoined: lastJoined, + firstJoined: firstJoined, + stats: stats, + showRankPrefix: showRankPrefix, + rank: rank.name, + flags: __spreadArray([], __read(flags.values()), false).map(function (f) { return f.name; }), + achievements: JsonIO.write(Reflect.get(this.achievements, "bits")) + }; + }; + /** Warning: the "update" callback is run twice. */ + FishPlayer.prototype.updateSynced = function (update, beforeFetch, afterFetch) { + return __awaiter(this, void 0, void 0, function () { + var data; + return __generator(this, function (_a) { + switch (_a.label) { + case 0: + update(this); + beforeFetch === null || beforeFetch === void 0 ? void 0 : beforeFetch(this); + return [4 /*yield*/, api.getFishPlayerData(this.uuid)]; + case 1: + data = _a.sent(); + if (data) + this.updateData(data); + update(this); + //of course, this is a race condition + //but it's unlikely to happen + //could be fixed by transmitting the update operation to the server as a mongo update command + afterFetch === null || afterFetch === void 0 ? void 0 : afterFetch(this); + return [4 /*yield*/, api.setFishPlayerData(this.getData(), 1, false)]; + case 2: + _a.sent(); + return [2 /*return*/]; + } + }); + }); + }; + //#endregion + //#region actively synced data updates + FishPlayer.prototype.stop = function (by, duration, message, notify) { + var _this = this; + if (notify === void 0) { notify = true; } + if (duration > 60000) + this.setPunishedIP(config_1.stopAntiEvadeTime); + this.showRankPrefix = true; + var unmarkTime = Date.now() + duration; + if (unmarkTime > globals.maxTime) + unmarkTime = globals.maxTime; + return this.updateSynced(function () { + _this.unmarkTime = unmarkTime; + _this.updateName(); + }, function () { + _this.setUnmarkTimer(duration); + if (_this.connected() && notify) { + _this.stopUnit(); + _this.sendMessage(message + ? "[scarlet]Oopsy Whoopsie! You've been stopped, and marked as a griefer for reason: [white]".concat(message, "[]") + : "[scarlet]Oopsy Whoopsie! You've been stopped, and marked as a griefer."); + if (duration < funcs_1.Duration.hours(1)) { + //less than one hour + _this.sendMessage("[yellow]Your mark will expire in ".concat((0, utils_1.formatTime)(duration), ".")); + } + } + }, function () { return _this.addHistoryEntry({ + action: 'stopped', + by: by instanceof FishPlayer ? by.name : by, + time: Date.now(), + }); }); + }; + FishPlayer.prototype.free = function (by) { + var _this = this; + by !== null && by !== void 0 ? by : (by = "console"); + this.autoflagged = false; //Might as well set autoflagged to false + FishPlayer.removePunishedIP(this.ip()); + FishPlayer.removePunishedUUID(this.uuid); + return this.updateSynced(function () { + _this.unmarkTime = -1; + }, function () { + if (_this.connected()) { + _this.sendMessage('[yellow]Looks like someone had mercy on you.'); + _this.updateName(); + _this.forceRespawn(); + } + }, function () { return _this.addHistoryEntry({ + action: 'freed', + by: by instanceof FishPlayer ? by.name : by, + time: Date.now(), + }); }); + }; + FishPlayer.prototype.setRank = function (rank) { + return __awaiter(this, void 0, void 0, function () { + var _this = this; + return __generator(this, function (_a) { + switch (_a.label) { + case 0: + if (typeof rank === "string" || !rank) { + rank; + (0, funcs_1.crash)("Type error in FishPlayer.setFlag(): rank is invalid"); + } + if (rank == ranks_1.Rank.pi && !config_1.Mode.localDebug) + throw new TypeError("Cannot find function setRank in object [object Object]."); + return [4 /*yield*/, this.updateSynced(function () { + _this.rank = rank; + _this.updateName(); + _this.updateAdminStatus(); + }, function () { return FishPlayer.saveAll(); })]; + case 1: + _a.sent(); + return [2 /*return*/]; + } + }); + }); + }; + FishPlayer.prototype.setFlag = function (flag_, value) { + return __awaiter(this, void 0, void 0, function () { + var flag; + var _this = this; + return __generator(this, function (_a) { + switch (_a.label) { + case 0: + flag = typeof flag_ == "string" ? + (ranks_1.RoleFlag.getByName(flag_)) + : flag_; + // eslint-disable-next-line @typescript-eslint/no-base-to-string + if (!flag) + (0, funcs_1.crash)("Type error in FishPlayer.setFlag(): flag ".concat(String(flag_), " is invalid")); + return [4 /*yield*/, this.updateSynced(function () { + if (value) { + _this.flags.add(flag); + } + else { + _this.flags.delete(flag); + } + _this.updateMemberExclusiveState(); + _this.updateName(); + })]; + case 1: + _a.sent(); + return [2 /*return*/]; + } + }); + }); + }; + FishPlayer.prototype.mute = function (by) { + var _this = this; + if (this.muted) + return; + this.showRankPrefix = true; + return this.updateSynced(function () { + _this.muted = true; + _this.updateName(); + }, function () { + _this.sendMessage("[yellow]Hey! You have been muted. You cannot send messages to other players. You can still send messages to staff members."); + _this.setPunishedIP(config_1.stopAntiEvadeTime); + }, function () { return _this.addHistoryEntry({ + action: 'muted', + by: by instanceof FishPlayer ? by.name : by, + time: Date.now(), + }); }); + }; + FishPlayer.prototype.unmute = function (by) { + var _this = this; + if (!this.muted) + return; + FishPlayer.removePunishedIP(this.ip()); + FishPlayer.removePunishedUUID(this.uuid); + return this.updateSynced(function () { + _this.muted = false; + _this.updateName(); + }, function () { + _this.sendMessage("[green]You have been unmuted."); + }, function () { return _this.addHistoryEntry({ + action: 'muted', + by: by instanceof FishPlayer ? by.name : by, + time: Date.now(), + }); }); + }; + //#endregion + //#region eventhandling + //Contains methods that handle an event and must be called by other code (usually through Events.on). + /** Must be run on PlayerConnectEvent. */ + FishPlayer.onPlayerConnect = function (player) { + var _a; + var _b, _c; + var fishPlayer = (_a = (_b = this.cachedPlayers)[_c = player.uuid()]) !== null && _a !== void 0 ? _a : (_b[_c] = this.createFromPlayer(player)); + var previousJoin = fishPlayer.lastJoined; + fishPlayer.updateSavedInfoFromPlayer(player); + if (fishPlayer.validate()) { + if (!fishPlayer.hasPerm("bypassNameCheck")) { + var message = (0, utils_1.isImpersonator)(fishPlayer.name, fishPlayer.ranksAtLeast("admin")); + if (message !== false) { + fishPlayer.sendMessage("[scarlet]\u26A0[] [gold]Oh no! Our systems think you are a [scarlet]SUSSY IMPERSONATOR[]!\n[gold]Reason: ".concat(message, "\n[gold]Change your name to remove the tag.")); + } + else if ((0, utils_1.cleanText)(player.name, true).includes("hacker")) { + fishPlayer.sendMessage("[scarlet]\u26A0 Don't be a script kiddie!"); + globals_1.FishEvents.fire("scriptKiddie", [fishPlayer]); + } + } + fishPlayer.updateAdminStatus(); + fishPlayer.checkVPNAndJoins(); + fishPlayer.updateName(); + //I think this is a better spot for this + if (fishPlayer.firstJoin()) + void menus_1.Menu.menu("Rules for [#0000ff] >|||> FISH [white] servers [white]", config_1.rules.join("\n\n[white]") + "\nYou can view these rules again by running [cyan]/rules[].", ["[green]I understand and agree to these terms"], fishPlayer); + } + }; + /** Must be run on PlayerJoinEvent. */ + FishPlayer.onPlayerJoin = function (player) { + var _this = this; + var _a; + var _b, _c; + var fishPlayer = (_a = (_b = this.cachedPlayers)[_c = player.uuid()]) !== null && _a !== void 0 ? _a : (_b[_c] = (function () { + Log.err("onPlayerJoin: no fish player was created? ".concat(player.uuid())); + return _this.createFromPlayer(player); + })()); + //Don't activate heuristics until they've joined + //a lot of time can pass between connect and join + //also the player might connect but fail to join for a lot of reasons, + //or connect, fail to join, then connect again and join successfully + //which would cause heuristics to activate twice + fishPlayer.activateHeuristics(); + }; + FishPlayer.updateAFKCheck = function () { + //TODO better AFK check + this.forEachPlayer(function (fishP, mp) { + if (fishP.lastMousePosition[0] != mp.mouseX || fishP.lastMousePosition[1] != mp.mouseY) { + fishP.lastActive = Date.now(); + } + fishP.lastMousePosition = [mp.mouseX, mp.mouseY]; + if (fishP.lastUnitPosition[0] != mp.x || fishP.lastUnitPosition[1] != mp.y) { + fishP.lastActive = Date.now(); + } + fishP.lastUnitPosition = [mp.x, mp.y]; + fishP.updateName(); + }); + }; + /** Must be run on PlayerLeaveEvent. */ + FishPlayer.onPlayerLeave = function (player) { + var _a; + var fishP = this.cachedPlayers[player.uuid()]; + if (!fishP) + return; + if (Vars.netServer.currentlyKicking && + Reflect.get(Vars.netServer.currentlyKicking, "target") == player) { + //Anti votekick evasion + var votes_1 = Reflect.get(Vars.netServer.currentlyKicking, "votes"); + if ((function () { + if (fishP.hasPerm("bypassVotekick")) + return false; + if (fishP.hasPerm("bypassVoteFreeze")) + return votes_1 >= Vars.netServer.votesRequired(); + if (fishP.info().timesJoined > 50) + return votes_1 >= 2; + return votes_1 >= 1; + })()) { + var kickDuration = NetServer.kickDuration; + //Pass the votekick + Call.sendMessage("[orange]Vote passed.[scarlet] ".concat(player.name, "[orange] will be banned from the server for ").concat(kickDuration / 60, " minutes.")); + player.kick(Packets.KickReason.vote, kickDuration * 1000); //it is stored in seconds but needs to be converted to millis + Reflect.get(Vars.netServer.currentlyKicking, "task").cancel(); + Vars.netServer.currentlyKicking = null; + } + } + //Clear temporary states such as menu and taphandler + fishP.activeMenus = []; + fishP.tapInfo.commandName = null; + fishP.updateStats(function (stats) { return stats.timeInGame += (Date.now() - fishP.lastJoined); }); //Time between joining and leaving + fishP.lastJoined = Date.now(); + this.recentLeaves.unshift(fishP); + if (this.recentLeaves.length > 10) + this.recentLeaves.pop(); + void api.setFishPlayerData(fishP.getData(), 1, true); + var currentRun = (_a = maps_1.PartialMapRun.current) === null || _a === void 0 ? void 0 : _a.startTime; + if (currentRun) + Core.app.post(function () { + //Wait for the /spectate command's handler to fix their team before saving it + fishP.restoreTeam = [fishP.player.team(), Date.now(), currentRun]; + }); + }; + FishPlayer.validateVotekickSession = function () { + var _a; + if (!Vars.netServer.currentlyKicking) + return; + var target = this.get(Reflect.get(Vars.netServer.currentlyKicking, "target")); + var voted = Reflect.get(Vars.netServer.currentlyKicking, "voted"); + if (voted.size == 2) { + //Try to find the UUID of the initiator + var uuid_1 = null; + voted.entries().toArray().each(function (e) { + if (globals_1.uuidPattern.test(e.key)) + uuid_1 = e.key; + }); + if (uuid_1) { + var initiator = this.getById(uuid_1); + if (initiator === null || initiator === void 0 ? void 0 : initiator.stelled()) { + if (initiator.hasPerm("bypassVotekick")) { + if (target !== this.easterEggVotekickTarget) { + this.easterEggVotekickTarget = target; + var msg = (_a = (new Error()).stack) === null || _a === void 0 ? void 0 : _a.split("\n").slice(0, 4).join("\n"); + Call.sendMessage("[scarlet]Server[lightgray] has voted on kicking[orange] ".concat(initiator.prefixedName, "[lightgray].[accent] (\u221E/").concat(Vars.netServer.votesRequired(), ")\n\t[scarlet]Error: failed to kick player ").concat(initiator.name, "\n\t").concat(msg, "\n\t[scarlet]Error: failed to cancel votekick\n\t").concat(msg)); + } + return; + } + Call.sendMessage("[scarlet]Server[lightgray] has voted on kicking[orange] ".concat(initiator.prefixedName, "[lightgray].[accent] (\u221E/").concat(Vars.netServer.votesRequired(), ")\n[scarlet]Vote passed.")); + initiator.kick("You are not allowed to votekick other players while marked.", 2); + Reflect.get(Vars.netServer.currentlyKicking, "task").cancel(); + Vars.netServer.currentlyKicking = null; + return; + } + else if ((initiator === null || initiator === void 0 ? void 0 : initiator.hasPerm("immediatelyVotekickNewPlayers")) && target.isSuspicious("high") && !target.hasPerm("bypassVotekick")) { + Call.sendMessage("[scarlet]Server[lightgray] has voted on kicking[orange] ".concat(target.prefixedName, "[lightgray].[accent] (").concat(Vars.netServer.votesRequired(), "/").concat(Vars.netServer.votesRequired(), ")\n[scarlet]Vote passed.")); + target.kick(Packets.KickReason.vote, funcs_1.Duration.minutes(30)); + Reflect.get(Vars.netServer.currentlyKicking, "task").cancel(); + Vars.netServer.currentlyKicking = null; + return; + } + else if (target.isSuspicious("high") && !target.hasPerm("bypassVotekick") && !target.ranksAtLeast("trusted")) { + //Increase votes by 1, from 1 to 2 + Reflect.set(Vars.netServer.currentlyKicking, "votes", Packages.java.lang.Integer(2)); + voted.put("__server__", 1); + Call.sendMessage("[scarlet]Server[lightgray] has voted on kicking[orange] ".concat(target.prefixedName, "[lightgray].[accent] (2/").concat(Vars.netServer.votesRequired(), ")\n[lightgray]Type[orange] /vote [] to agree.")); + return; + } + } + } + if (target.hasPerm("bypassVotekick")) { + Call.sendMessage("[scarlet]Server[lightgray] has voted on kicking[orange] ".concat(target.prefixedName, "[lightgray].[accent] (-\u221E/").concat(Vars.netServer.votesRequired(), ")\n[scarlet]Vote cancelled.")); + Reflect.get(Vars.netServer.currentlyKicking, "task").cancel(); + Vars.netServer.currentlyKicking = null; + } + else if (target.ranksAtLeast("trusted") && Groups.player.size() > 4 && voted.get("__server__") == 0) { + //decrease votes by two, goes from 1 to negative 1 + Reflect.set(Vars.netServer.currentlyKicking, "votes", Packages.java.lang.Integer(-1)); + voted.put("__server__", -2); + Call.sendMessage("[scarlet]Server[lightgray] has voted on kicking[orange] ".concat(target.prefixedName, "[lightgray].[accent] (-1/").concat(Vars.netServer.votesRequired(), ")\n[lightgray]Type[orange] /vote [] to agree.")); + } + }; + FishPlayer.onPlayerChat = function (player, message) { + var fishP = this.get(player); + if (message.trim().toLowerCase().startsWith("/vote y") || message.startsWith("/votekick ")) { + this.checkVotekickAction(fishP, message); + } + fishP.lastActive = Date.now(); + fishP.updateStats(function (stats) { return stats.chatMessagesSent++; }); + }; + FishPlayer.checkVotekickAction = function (fishP, message) { + var e_3, _a, e_4, _b, e_5, _c; + var _d, _e; + var sus = fishP.suspicionLevel(); + var timeSinceJoin = Date.now() - fishP.lastJoined; + var target; + if (message.startsWith("/votekick")) { + var id = (_d = message.split(" ")[1]) === null || _d === void 0 ? void 0 : _d.split("#")[1]; + target = Groups.player.getByID(Number(id)); + if (!target) + return; //invalid votekick command, harmless + } + else { //TODO these "harmless" actions could be indications of a malfunctioning vkbot and should be logged if they repeat a lot (eg more than 5 times per minute) + if (!Vars.netServer.currentlyKicking) + return; //nobody to votekick, harmless + target = Reflect.get(Vars.netServer.currentlyKicking, "target"); + } + var targetSusLevel = FishPlayer.get(target).suspicionLevel(); + //Evaluate if this action should be blocked + if (sus <= 1) + return; + var reason = undefined; + if (!this.votekickActionRate.allow(108000, 8)) + reason = "Exceeded 8 votekick actions in the last 2 minutes"; + else if (sus == 3 && this.lastVKActions.find(function (a) { return Date.now() - a.time < 10000 && a.playerSusLevel == 3; }) && timeSinceJoin < 6000) + reason = "Performed votekick within 6 seconds of joining and there was a recent suspicious vote"; + else if (sus == 3 && timeSinceJoin < 80000 && this.lastVKActions.find(function (a) { return a.player == fishP; }) && targetSusLevel <= 1) + reason = "Two votekick actions within 80 seconds of joining and the target is not suspicious"; + else if (sus >= 2 && this.lastVKActions.filter(function (a) { return a.playerSusLevel == 3 && Date.now() - a.time < 33000; }).length >= 3) + reason = "More than 3 recent votekick actions by suspicious players"; + else if (sus >= 2 && this.lastVKActions.filter(function (a) { return a.playerSusLevel >= 2; }).length >= 6 && this.lastVKActions.filter(function (a) { return a.player == fishP; }).length >= 3) + reason = "More than 6 slightly suspicious votekick actions within the past 20 minutes and this player has already performed 3 of them"; + if (reason != undefined) { + //Should we ban everyone? + var suspiciousActions = this.lastVKActions.filter(function (action) { + return (action.playerSusLevel == 3 || (action.targetSusLevel <= 2 && action.playerSusLevel >= 2) || action.player == fishP) && Date.now() - action.time < 78000; + }); + if (suspiciousActions.length >= 3) { + //Ban everyone + var playersToBan = suspiciousActions.map(function (a) { return a.player; }).reduce(function (map, p) { + var _a; + map.set(p, ((_a = map.get(p)) !== null && _a !== void 0 ? _a : 0) + 1); + return map; + }, new Map()); + //Only ban players that appeared in the list twice or are high suslevel + var admins = Vars.netServer.admins; + try { + for (var playersToBan_1 = __values(playersToBan), playersToBan_1_1 = playersToBan_1.next(); !playersToBan_1_1.done; playersToBan_1_1 = playersToBan_1.next()) { + var _f = __read(playersToBan_1_1.value, 2), p = _f[0], times = _f[1]; + if (p.suspicionLevel() == 3 || p.suspicionLevel() == 2 && times > 1) { + admins.banPlayerID(p.uuid); + admins.banPlayerIP(p.ip()); + api.ban({ ip: p.ip(), uuid: p.uuid }); + (0, utils_1.logHTrip)(p, "votekick abuse", (p == fishP ? "Player banned automatically" : "Player banned automatically based on previous activity") + + ". Trigger reason: ".concat(reason)); + } + } + } + catch (e_3_1) { e_3 = { error: e_3_1 }; } + finally { + try { + if (playersToBan_1_1 && !playersToBan_1_1.done && (_a = playersToBan_1.return)) _a.call(playersToBan_1); + } + finally { if (e_3) throw e_3.error; } + } + (0, utils_1.updateBans)(function (player) { return "[scarlet]Player [yellow]".concat(player.name, "[scarlet] has been whacked automatically for suspected votekick abuse."); }); + //Pardon most of the votekick targets (the ones that weren't voted on by a non-sus player) + var candidatePardons = new Set(FishPlayer.lastVKActions.map(function (a) { return a.target; })); + try { + for (var _g = __values(FishPlayer.lastVKActions), _h = _g.next(); !_h.done; _h = _g.next()) { + var action = _h.value; + if (action.playerSusLevel <= 1) + candidatePardons.delete(action.target); + } + } + catch (e_4_1) { e_4 = { error: e_4_1 }; } + finally { + try { + if (_h && !_h.done && (_b = _g.return)) _b.call(_g); + } + finally { if (e_4) throw e_4.error; } + } + var playersToPardon = __spreadArray([], __read(candidatePardons), false).map(FishPlayer.get); + try { + //Don't pardon players with suslevel 3 + for (var playersToPardon_1 = __values(playersToPardon), playersToPardon_1_1 = playersToPardon_1.next(); !playersToPardon_1_1.done; playersToPardon_1_1 = playersToPardon_1.next()) { + var p = playersToPardon_1_1.value; + if (!p.isSuspicious("high")) { + p.info().lastKicked = 0; + admins.kickedIPs.remove(p.ip()); + Log.info("Pardoned player @ (@/@)", p.name, p.uuid, p.ip()); + (0, utils_1.logAction)("pardoned", "automod", p, "kicked by suspected votekick bot"); + } + } + } + catch (e_5_1) { e_5 = { error: e_5_1 }; } + finally { + try { + if (playersToPardon_1_1 && !playersToPardon_1_1.done && (_c = playersToPardon_1.return)) _c.call(playersToPardon_1); + } + finally { if (e_5) throw e_5.error; } + } + } + else { + //Just kick the player + (0, utils_1.logHTrip)(fishP, "votekick abuse", "sus=".concat(sus)); + fishP.kick("You have been kicked [accent]automatically[] due to suspicious behavior. Please wait [accent]35[] seconds before rejoining.", 30000); + Call.sendMessage("[scarlet]Player [yellow]".concat(fishP.prefixedName, "[scarlet] was kicked due to suspected votekick abuse.")); + //If this message is going to start a votekick, cancel it + if (message.startsWith("/votekick") && Vars.netServer.currentlyKicking == null) + Core.app.post(function () { + Call.sendMessage("[scarlet]Server[lightgray] has voted on kicking[orange] ".concat(target.name, "[lightgray].[accent] (-\u221E/").concat(Vars.netServer.votesRequired(), ")\n\t\t[scarlet]Vote cancelled due to suspected abuse. [accent]If this is in error, please report it to staff.")); + if (Vars.netServer.currentlyKicking) + Reflect.get(Vars.netServer.currentlyKicking, "task").cancel(); + Vars.netServer.currentlyKicking = null; + }); + //If there is an ongoing votekick and the initiator is suspicious, cancel that + else if (((_e = FishPlayer.lastVKActions.slice().reverse().find(function (a) { return a.type == "start"; })) === null || _e === void 0 ? void 0 : _e.playerSusLevel) == 3) { + Call.sendMessage("[scarlet]Server[lightgray] has voted on kicking[orange] ".concat(target.name, "[lightgray].[accent] (-\u221E/").concat(Vars.netServer.votesRequired(), ")\n\t\t[scarlet]Vote cancelled due to suspected abuse. [accent]If this is in error, please report it to staff.")); + if (Vars.netServer.currentlyKicking) + Reflect.get(Vars.netServer.currentlyKicking, "task").cancel(); + Vars.netServer.currentlyKicking = null; + } + //Otherwise, revoke the vote + else + Core.app.post(function () { + if (Vars.netServer.currentlyKicking) { + var votes = Reflect.get(Vars.netServer.currentlyKicking, "votes") - 1; + Reflect.set(Vars.netServer.currentlyKicking, "votes", votes); + var voted = Reflect.get(Vars.netServer.currentlyKicking, "voted"); + voted.put(fishP.uuid, 0); + voted.put(fishP.ip(), 0); + Call.sendMessage("[scarlet]Vote cancelled due to suspected abuse. [accent]If this is in error, please report it to staff."); + } + }); + } + } + //Update state to catch future actions + this.lastVKActions.push({ + player: fishP, + playerSusLevel: sus, + target: target, + targetSusLevel: targetSusLevel, + time: Date.now(), + type: message.startsWith("/votekick") ? "start" : "vote y", + reason: message.startsWith("/votekick") ? message.split(" ").slice(2).join(" ") : undefined + }); + this.lastVKActions = this.lastVKActions.filter(function (a) { return Date.now() - a.time < funcs_1.Duration.minutes(10); }); + }; + FishPlayer.onPlayerCommand = function (player, command, unjoinedRawArgs) { + if (command == "msg" && unjoinedRawArgs[1] == "Please do not use that logic, as it is attem83 logic and is bad to use. For more information please read www.mindustry.dev/attem") + return; //Attemwarfare message, not sent by the player + player.lastActive = Date.now(); + }; + FishPlayer.onGameOver = function (winningTeam) { + var _this = this; + globals_1.FishEvents.fire("gameOver", [winningTeam]); + this.forEachPlayer(function (fishPlayer) { + //Clear temporary states such as menu and taphandler + fishPlayer.activeMenus = []; + fishPlayer.tapInfo.commandName = null; + //Update stats + if (!_this.ignoreGameOver && fishPlayer.team() != Team.derelict && winningTeam != Team.derelict) { + fishPlayer.updateStats(function (stats) { return stats.gamesFinished++; }); + if (fishPlayer.changedTeam) { + fishPlayer.sendMessage("Refusing to update stats due to a team change."); + } + else { + if (fishPlayer.team() == winningTeam) + fishPlayer.updateStats(function (stats) { return stats.gamesWon++; }); + } + } + fishPlayer.changedTeam = false; + fishPlayer.tstats.wavesSurvived = 0; + fishPlayer.tstats.blockInteractionsThisMap = 0; + }); + }; + FishPlayer.ignoreGameover = function (callback) { + this.ignoreGameOver = true; + callback(); + this.ignoreGameOver = false; + }; + FishPlayer.onGameBegin = function () { + var startTime = Date.now(); + FishPlayer.lastMapStartTime = startTime; + //wait 7 seconds for players to join + Timer.schedule(function () { return FishPlayer.forEachPlayer(function (p) { return p.tstats.lastMapStartTime = startTime; }); }, 7); + }; + /** Must be run on UnitChangeEvent. */ + FishPlayer.onUnitChange = function (player, unit) { + if (unit === null || unit === void 0 ? void 0 : unit.spawnedByCore) + this.onRespawn(player); + }; + FishPlayer.onRespawn = function (player) { + var fishP = this.get(player); + if (fishP.stelled()) + fishP.stopUnit(); + }; + FishPlayer.forEachPlayer = function (func) { + var _this = this; + Groups.player.each(function (player) { + if (player == null) { + Log.err(".FINDTAG. Groups.player.each() returned a null player???"); + return; + } + var fishP = _this.get(player); + func(fishP, player); + }); + }; + FishPlayer.mapPlayers = function (func) { + var _this = this; + var out = []; + Groups.player.each(function (player) { + if (player == null) { + Log.err(".FINDTAG. Groups.player.each() returned a null player???"); + return; + } + out.push(func(_this.get(player))); + }); + return out; + }; + FishPlayer.prototype.updateMemberExclusiveState = function () { + if (!this.hasPerm("member")) { + this.highlight = null; + this.rainbow = null; + } + }; + /** Updates the mindustry player's name, using the prefixes of the current rank and role flags. */ + FishPlayer.prototype.updateName = function () { + var e_6, _a; + var _b; + if (!this.connected() || !this.shouldUpdateName) + return; //No player, no need to update + var name = (_b = this.originalName) !== null && _b !== void 0 ? _b : this.name; + if (this.marked()) + this.showRankPrefix = true; + var prefix = ''; + if (!this.hasPerm("bypassNameCheck") && (0, utils_1.isImpersonator)(name, this.ranksAtLeast("admin"))) + prefix += "[scarlet]SUSSY IMPOSTOR[]"; + if (this.marked()) + prefix += config_1.prefixes.marked; + else if (this.autoflagged) + prefix += config_1.prefixes.flagged; + if (this.muted) + prefix += config_1.prefixes.muted; + if (this.afk()) + prefix += "[orange]\uE876 AFK \uE876 | [white]"; + if (this.showRankPrefix) { + try { + for (var _c = __values(this.flags), _d = _c.next(); !_d.done; _d = _c.next()) { + var flag = _d.value; + prefix += flag.prefix; + } + } + catch (e_6_1) { e_6 = { error: e_6_1 }; } + finally { + try { + if (_d && !_d.done && (_a = _c.return)) _a.call(_c); + } + finally { if (e_6) throw e_6.error; } + } + prefix += this.rank.prefix; + } + if (prefix.length > 0 && !prefix.endsWith(" ")) + prefix += " "; + var replacedName; + if ((0, utils_1.cleanText)(name, true).includes("hacker")) { + //"Don't be a script kiddie" + //-LiveOverflow, 2015 + if (/h.*a.*c.*k.*[3e].*r/i.test(name)) { //try to only replace the part that contains "hacker" if it can be found with a simple regex + replacedName = name.replace(/h.*a.*c.*k.*[3e].*r/gi, "[brown]script kiddie[]"); + } + else { + replacedName = "[brown]script kiddie"; + } + } + else if (this.name.endsWith("[") && !this.name.endsWith("[[")) { + replacedName = name + "["; + } + else + replacedName = name; + this.player.name = this.prefixedName = prefix + replacedName; + }; + FishPlayer.prototype.updateAdminStatus = function () { + if (!this.connected()) + return; + if (this.hasPerm("admin")) { + Vars.netServer.admins.adminPlayer(this.uuid, this.player.usid()); + this.player.admin = true; + } + else { + Vars.netServer.admins.unAdminPlayer(this.uuid); + this.player.admin = false; + } + }; + FishPlayer.prototype.updateAutoflaggedStatus = function () { + if (this.ranksAtLeast("active")) { + this.autoflagged = false; + } + }; + FishPlayer.prototype.checkAntiEvasion = function () { + var e_7, _a; + var _b, _c; + FishPlayer.updatePunishedIPs(); + try { + for (var _d = __values(FishPlayer.punishedIPs), _e = _d.next(); !_e.done; _e = _d.next()) { + var _f = __read(_e.value, 2), ip = _f[0], uuid = _f[1]; + if (ip == this.ip() && uuid != this.uuid && !this.ranksAtLeast("mod")) { + api.sendModerationMessage("Automatically banned player `".concat(this.cleanedName, "` (`").concat(this.uuid, "`/`").concat(this.ip(), "`) for suspected punishment evasion.\nPreviously used UUID `").concat(uuid, "`(").concat((_b = Vars.netServer.admins.getInfoOptional(uuid)) === null || _b === void 0 ? void 0 : _b.plainLastName(), "), currently using UUID `").concat(this.uuid, "` from the same IP address.")); + Log.warn("&yAutomatically banned player &b".concat(this.cleanedName, "&y (&b").concat(this.uuid, "&y/&b").concat(this.ip(), "&y) for suspected punishment evasion.\n&yPreviously used UUID &b").concat(uuid, "&y(&b").concat((_c = Vars.netServer.admins.getInfoOptional(uuid)) === null || _c === void 0 ? void 0 : _c.plainLastName(), "&y), currently using UUID &b").concat(this.uuid, "&y from the same IP address.")); + FishPlayer.messageStaff("[yellow]Automatically banned player [cyan]".concat(this.cleanedName, "[] for suspected punishment evasion.")); + Vars.netServer.admins.banPlayerIP(ip); + api.ban({ ip: ip, uuid: uuid }); + this.kick(Packets.KickReason.banned); + return false; + } + } + } + catch (e_7_1) { e_7 = { error: e_7_1 }; } + finally { + try { + if (_e && !_e.done && (_a = _d.return)) _a.call(_d); + } + finally { if (e_7) throw e_7.error; } + } + return true; + }; + FishPlayer.updatePunishedIPs = function () { + for (var i = 0; i < this.punishedIPs.length; i++) { + if (this.punishedIPs[i][2] < Date.now()) { + this.punishedIPs.splice(i, 1); + } + } + }; + FishPlayer.prototype.checkVPNAndJoins = function () { + var _this = this; + var ip = this.ip(); + var info = this.info(); + api.isVpn(ip, function (isVpn) { + if (isVpn) { + Log.warn("IP ".concat(ip, " was flagged as VPN. Flag rate: ").concat(FishPlayer.stats.numIpsFlagged, "/").concat(FishPlayer.stats.numIpsChecked, " (").concat(100 * FishPlayer.stats.numIpsFlagged / FishPlayer.stats.numIpsChecked, "%)")); + _this.ipDetectedVpn = true; + if (!FishPlayer.autoflagRate.allow(30000, 5)) { + FishPlayer.triggerAntibot(funcs_1.Duration.minutes(3), "rate of flagged IPs exceeded 5 / 30s", "automatic"); + return; + } + if ((info.timesJoined <= 1 || (FishPlayer.autoflagRate.occurences > 3 && info.timesJoined <= 10)) //is this smart? + && !_this.ranksAtLeast("active") + && FishPlayer.punishedIPs.length > 0) { + _this.autoflagged = true; + _this.stopUnit(); + _this.updateName(); + if (FishPlayer.shouldWhackFlaggedPlayers()) { + FishPlayer.whackFlaggedPlayers(); //calls whack all flagged players + } + else { + (0, utils_1.logAction)("autoflagged", "AntiVPN", _this); + api.sendStaffMessage("Autoflagged player ".concat(_this.name, "[cyan] for suspected vpn!"), "AntiVPN", true); + FishPlayer.messageStaff("[yellow]WARNING:[scarlet] player [cyan]\"".concat(_this.name, "[cyan]\"[yellow] is new (").concat(info.timesJoined - 1, " joins) and using a vpn. They have been automatically stopped and muted. Unless there is an ongoing griefer raid, they are most likely innocent. Free them with /free.")); + Log.warn("Player ".concat(_this.name, " (").concat(_this.uuid, ") was autoflagged.")); + void menus_1.Menu.buttons(_this, "[gold]Welcome to Fish Community!", "[gold]Hi there! You have been automatically [scarlet]stopped and muted[] because we've found something to be [pink]a bit sus[]. You can still talk to staff and request to be freed. ".concat(config_1.FColor.discord(templateObject_1 || (templateObject_1 = __makeTemplateObject(["Join our Discord"], ["Join our Discord"]))), " to request a staff member come online if none are on."), [[ + { data: "Close", text: "Close" }, + { data: "Discord", text: config_1.FColor.discord("Discord") }, + ]]).then(function (option) { + if (option == "Discord") { + Call.openURI(_this.con, config_1.text.discordURL); + } + }); + _this.sendMessage("[gold]Welcome to Fish Community!\n[gold]Hi there! You have been automatically [scarlet]stopped and muted[] because we've found something to be [pink]a bit sus[]. You can still talk to staff and request to be freed. ".concat(config_1.FColor.discord(templateObject_2 || (templateObject_2 = __makeTemplateObject(["Join our Discord"], ["Join our Discord"]))), " to request a staff member come online if none are on.")); + } + } + else if (info.timesJoined < 5) { + FishPlayer.messageStaff("[yellow]WARNING:[scarlet] player [cyan]\"".concat(_this.name, "[cyan]\"[yellow] is new (").concat(info.timesJoined - 1, " joins) and using a vpn.")); + } + } + else { + if (info.timesJoined == 1) { + FishPlayer.messageTrusted("[yellow]Player \"".concat(_this.cleanedName, "\" is on first join.")); + } + } + if (info.timesJoined == 1) { + var message = "&lrNew player joined: &c".concat(_this.cleanedName, "&lr (&c").concat(_this.uuid, "&lr/&c").concat(ip, "&lr)"); + //Add BEL, this causes an audible noise + if (globals.fishState.joinBell) + message += '\x07'; + Log.info(message); + } + }, function (err) { + Log.err("Error while checking for VPN status of ip ".concat(ip, "!")); + Log.err(err); + }); + }; + FishPlayer.prototype.validate = function () { + return this.checkName() && this.checkUsid() && this.checkAntiEvasion(); + }; + /** Checks if this player's name is allowed. */ + FishPlayer.prototype.checkName = function () { + if ((0, utils_1.matchFilter)(this.name, "name")) { + this.kick("[scarlet]\"".concat(this.name, "[scarlet]\" is not an allowed name because it contains a banned word.\n\nIf you are unable to change it, please download Mindustry from Steam or itch.io."), 1); + } + else if (Strings.stripColors(this.name.replace(/[\u3164]/g, "")).trim().length == 0) { + this.kick("[scarlet]\"".concat((0, funcs_1.escapeStringColorsClient)(this.name), "[scarlet]\" is not an allowed name because it is empty. Please change it."), 1); + } + else { + return true; + } + return false; + }; + /** Checks if this player's USID is correct. */ + FishPlayer.prototype.checkUsid = function () { + var storedUSID = this.usid; + var usidMissing = storedUSID == null || !storedUSID; + var receivedUSID = this.player.usid(); + if (this.hasPerm("usidCheck")) { + if (usidMissing) { + if (this.hasPerm("mod")) { + //Staff missing USID, don't let them in + Log.err("&rUSID missing for privileged player &c\"".concat(this.cleanedName, "\"&r: no stored usid, cannot authenticate.\nRun &lgsetusid ").concat(this.uuid, " ").concat(receivedUSID, "&fr if you have verified this connection attempt.")); + this.kick("Authorization failure! Please ask a staff member with Console Access to approve this connection.", 1); + FishPlayer.lastAuthKicked = this; + return false; + } + else { + Log.info("Acquired USID for player &c\"".concat(this.cleanedName, "\"&fr: &c\"").concat(receivedUSID, "\"&fr")); + } + } + else { + if (receivedUSID != storedUSID) { + Log.err("&rUSID mismatch for player &c\"".concat(this.cleanedName, "\"&r: stored usid is &c").concat(storedUSID, "&r, but they tried to connect with usid &c").concat(receivedUSID, "&r\nRun &lgsetusid ").concat(this.uuid, " ").concat(receivedUSID, "&fr if you have verified this connection attempt.")); + this.kick("Authorization failure!", 1); + FishPlayer.lastAuthKicked = this; + return false; + } + } + } + else { + if (!usidMissing && receivedUSID != storedUSID) { + Log.err("&rUSID mismatch for player &c\"".concat(this.cleanedName, "\"&r: stored usid is &c").concat(storedUSID, "&r, but they tried to connect with usid &c").concat(receivedUSID, "&r")); + } + } + this.usid = receivedUSID; + return true; + }; + FishPlayer.prototype.displayTrail = function () { + if (this.trail) + Call.effect(Fx[this.trail.type], this.player.x, this.player.y, 0, this.trail.color); + }; + FishPlayer.prototype.sendWelcomeMessage = function () { + var _this = this; + var appealLine = "To appeal, ".concat(config_1.FColor.discord(templateObject_3 || (templateObject_3 = __makeTemplateObject(["join our discord"], ["join our discord"]))), " with ").concat(config_1.FColor.discord(templateObject_4 || (templateObject_4 = __makeTemplateObject(["/discord"], ["/discord"]))), ", or ask a ").concat(ranks_1.Rank.mod.color, "staff member[] in-game."); + if (FishPlayer.dataFetchFailedUuids.has(this.uuid)) { + this.sendMessage(config_1.text.dataFetchFailed); + FishPlayer.dataFetchFailedUuids.delete(this.uuid); + } + if (this.marked()) + this.sendMessage("[gold]Hello there! You are currently [scarlet]marked as a griefer[]. You cannot do anything in-game while marked.\n".concat(appealLine, "\nYour mark will expire automatically ").concat(this.unmarkTime == globals.maxTime ? "in [red]never[]" : "[green]".concat((0, utils_1.formatTimeRelative)(this.unmarkTime), "[]"), ".\nWe apologize for the inconvenience.")); + else if (this.muted) + this.sendMessage("[gold]Hello there! You are currently [red]muted[]. You can still play normally, but cannot send chat messages to other non-staff players while muted.\n".concat(appealLine, "\nWe apologize for the inconvenience.")); + else if (this.autoflagged) + this.sendMessage("[gold]Hello there! You are currently [red]flagged as suspicious[]. You cannot do anything in-game.\n".concat(appealLine, "\nWe apologize for the inconvenience.")); + else if (!this.showRankPrefix) + this.sendMessage("[gold]Hello there! Your rank prefix is currently hidden. You can show it again by running [white]/vanish[]."); + else { + this.sendMessage(config_1.text.welcomeMessage()); + //show tips + var showAd = false; + if (Date.now() - this.lastShownAd > funcs_1.Duration.days(1)) { + this.lastShownAd = Date.now(); + this.showAdNext = true; + } + else if (this.lastShownAd == globals.maxTime) { + //this is the first time they joined, show ad the next time they join + this.showAdNext = true; + this.lastShownAd = Date.now(); + } + else if (this.showAdNext) { + this.showAdNext = false; + showAd = true; + } + var messagePool = showAd ? config_1.tips.ads : (config_1.Mode.isChristmas && Math.random() > 0.6) ? config_1.tips.christmas : config_1.tips.normal; + var messageText = messagePool[Math.floor(Math.random() * messagePool.length)]; + var message_1 = showAd ? "[gold]".concat(messageText, "[]") : "[gold]Tip: ".concat(messageText, "[]"); + //Delay sending the message so it doesn't get lost in the spam of messages that usually occurs when you join + Timer.schedule(function () { return _this.sendMessage(message_1); }, 3); + } + }; + FishPlayer.prototype.checkAutoRanks = function () { + var e_8, _a; + var _this = this; + if (this.stelled()) + return; + var _loop_1 = function (rankToAssign) { + if (!this_1.ranksAtLeast(rankToAssign) && rankToAssign.autoRankData) { + if (this_1.joinsAtLeast(rankToAssign.autoRankData.joins) && + this_1.globalStats.blocksPlaced >= rankToAssign.autoRankData.blocksPlaced && + this_1.globalStats.timeInGame >= rankToAssign.autoRankData.playtime && + this_1.globalStats.chatMessagesSent >= rankToAssign.autoRankData.chatMessagesSent && + (Date.now() - this_1.globalFirstJoined) >= rankToAssign.autoRankData.timeSinceFirstJoin) { + void this_1.setRank(rankToAssign).then(function () { + return _this.sendMessage("You have been automatically promoted to rank ".concat(rankToAssign.coloredName(), "!")); + }); + } + } + }; + var this_1 = this; + try { + for (var _b = __values(ranks_1.Rank.autoRanks), _c = _b.next(); !_c.done; _c = _b.next()) { + var rankToAssign = _c.value; + _loop_1(rankToAssign); + } + } + catch (e_8_1) { e_8 = { error: e_8_1 }; } + finally { + try { + if (_c && !_c.done && (_a = _b.return)) _a.call(_b); + } + finally { if (e_8) throw e_8.error; } + } + }; + //#endregion + //#region I/O + FishPlayer.read = function (version, fishPlayerData, player) { + var _a, _b, _c, _d, _e, _f, _g, _h, _j; + switch (version) { + case 0: + case 1: + case 2: + case 3: + case 4: + case 5: + case 6: + case 7: + case 8: + case 9: + (0, funcs_1.crash)("Version ".concat(version, " is not longer supported, this should not be possible")); + break; + case 10: { + var uuid = (_a = fishPlayerData.readString(2)) !== null && _a !== void 0 ? _a : (0, funcs_1.crash)("Failed to deserialize FishPlayer: UUID was null."); + var fishP = new this(uuid, { + name: (_b = fishPlayerData.readString(2)) !== null && _b !== void 0 ? _b : "Unnamed player [ERROR]", + muted: (function () { + var muted = fishPlayerData.readBool(); + void fishPlayerData.readBool(); //discard the stored data for autoflagged + return muted; + })(), + unmarkTime: fishPlayerData.readNumber(13), + highlight: fishPlayerData.readString(2), + history: fishPlayerData.readArray(function (str) { + var _a, _b; + return ({ + action: (_a = str.readString(2)) !== null && _a !== void 0 ? _a : "null", + by: (_b = str.readString(2)) !== null && _b !== void 0 ? _b : "null", + time: str.readNumber(15) + }); + }), + rainbow: (function (n) { return n == 0 ? null : { speed: n }; })(fishPlayerData.readNumber(2)), + rank: (_c = fishPlayerData.readString(2)) !== null && _c !== void 0 ? _c : "", + flags: fishPlayerData.readArray(function (str) { return str.readString(2); }, 2).filter(function (s) { return s != null; }), + usid: fishPlayerData.readString(2), + chatStrictness: fishPlayerData.readEnumString(["chat", "strict"]), + lastJoined: fishPlayerData.readNumber(15), + firstJoined: fishPlayerData.readNumber(15), + stats: { + blocksBroken: fishPlayerData.readNumber(10), + blocksPlaced: fishPlayerData.readNumber(10), + timeInGame: fishPlayerData.readNumber(15), + chatMessagesSent: fishPlayerData.readNumber(7), + gamesFinished: fishPlayerData.readNumber(5), + gamesWon: fishPlayerData.readNumber(5), + }, + showRankPrefix: fishPlayerData.readBool(), + }, player); + fishPlayerData.readNumber(1); //discard pollResponse + return fishP; + } + case 11: { + var uuid = (_d = fishPlayerData.readString(2)) !== null && _d !== void 0 ? _d : (0, funcs_1.crash)("Failed to deserialize FishPlayer: UUID was null."); + return new this(uuid, { + name: (_e = fishPlayerData.readString(2)) !== null && _e !== void 0 ? _e : "Unnamed player [ERROR]", + muted: (function () { + var muted = fishPlayerData.readBool(); + void fishPlayerData.readBool(); //discard the stored data for autoflagged + return muted; + })(), + unmarkTime: fishPlayerData.readNumber(13), + highlight: fishPlayerData.readString(2), + history: fishPlayerData.readArray(function (str) { + var _a, _b; + return ({ + action: (_a = str.readString(2)) !== null && _a !== void 0 ? _a : "null", + by: (_b = str.readString(2)) !== null && _b !== void 0 ? _b : "null", + time: str.readNumber(15) + }); + }), + rainbow: (function (n) { return n == 0 ? null : { speed: n }; })(fishPlayerData.readNumber(2)), + rank: (_f = fishPlayerData.readString(2)) !== null && _f !== void 0 ? _f : "", + flags: fishPlayerData.readArray(function (str) { return str.readString(2); }, 2).filter(function (s) { return s != null; }), + usid: fishPlayerData.readString(2), + chatStrictness: fishPlayerData.readEnumString(["chat", "strict"]), + lastJoined: fishPlayerData.readNumber(15), + firstJoined: fishPlayerData.readNumber(15), + stats: { + blocksBroken: fishPlayerData.readNumber(10), + blocksPlaced: fishPlayerData.readNumber(10), + timeInGame: fishPlayerData.readNumber(15), + chatMessagesSent: fishPlayerData.readNumber(7), + gamesFinished: fishPlayerData.readNumber(5), + gamesWon: fishPlayerData.readNumber(5), + }, + showRankPrefix: fishPlayerData.readBool(), + }, player); + } + case 12: { + var uuid = (_g = fishPlayerData.readString(2)) !== null && _g !== void 0 ? _g : (0, funcs_1.crash)("Failed to deserialize FishPlayer: UUID was null."); + return new this(uuid, { + name: (_h = fishPlayerData.readString(2)) !== null && _h !== void 0 ? _h : "Unnamed player [ERROR]", + muted: fishPlayerData.readBool(), + unmarkTime: fishPlayerData.readNumber(13), + highlight: fishPlayerData.readString(2), + history: fishPlayerData.readArray(function (str) { + var _a, _b; + return ({ + action: (_a = str.readString(2)) !== null && _a !== void 0 ? _a : "null", + by: (_b = str.readString(2)) !== null && _b !== void 0 ? _b : "null", + time: str.readNumber(15) + }); + }), + rainbow: (function (n) { return n == 0 ? null : { speed: n }; })(fishPlayerData.readNumber(2)), + rank: (_j = fishPlayerData.readString(2)) !== null && _j !== void 0 ? _j : "", + flags: fishPlayerData.readArray(function (str) { return str.readString(2); }, 2).filter(function (s) { return s != null; }), + usid: fishPlayerData.readString(2), + chatStrictness: fishPlayerData.readEnumString(["chat", "strict"]), + lastJoined: fishPlayerData.readNumber(15), + firstJoined: fishPlayerData.readNumber(15), + stats: { + blocksBroken: fishPlayerData.readNumber(10), + blocksPlaced: fishPlayerData.readNumber(10), + timeInGame: fishPlayerData.readNumber(15), + chatMessagesSent: fishPlayerData.readNumber(7), + gamesFinished: fishPlayerData.readNumber(5), + gamesWon: fishPlayerData.readNumber(5), + }, + showRankPrefix: fishPlayerData.readBool(), + }, player); + } + default: (0, funcs_1.crash)("Unknown save version ".concat(version)); + } + }; + FishPlayer.prototype.write = function (out) { + var _a, _b; + if (typeof this.unmarkTime === "string") + this.unmarkTime = 0; + out.writeString(this.uuid, 2); + out.writeString(this.name, 2, true); + out.writeBool(this.muted); + out.writeNumber(this.unmarkTime, 13); // this will stop working in 2286! https://en.wikipedia.org/wiki/Time_formatting_and_storage_bugs#Year_2286 + out.writeString(this.highlight, 2, true); + out.writeArray(this.history.slice(-5), function (i, str) { + str.writeString(i.action, 2); + str.writeString(i.by.slice(0, 98), 2, true); + str.writeNumber(i.time, 15); + }); + out.writeNumber((_b = (_a = this.rainbow) === null || _a === void 0 ? void 0 : _a.speed) !== null && _b !== void 0 ? _b : 0, 2); + out.writeString(this.rank.name, 2); + out.writeArray(Array.from(this.flags), function (f, str) { return str.writeString(f.name, 2); }, 2); + out.writeString(this.usid, 2); + out.writeEnumString(this.chatStrictness, ["chat", "strict"]); + out.writeNumber(this.lastJoined, 15); + out.writeNumber(this.firstJoined, 15); + out.writeNumber(this.stats.blocksBroken, 10, true); + out.writeNumber(this.stats.blocksPlaced, 10, true); + out.writeNumber(this.stats.timeInGame, 15, true); + out.writeNumber(this.stats.chatMessagesSent, 7, true); + out.writeNumber(this.stats.gamesFinished, 5, true); + out.writeNumber(this.stats.gamesWon, 5, true); + out.writeBool(this.showRankPrefix); + }; + /** Saves cached FishPlayers to JSON in Core.settings. */ + FishPlayer.saveAll = function (forceSaveSettings) { + if (forceSaveSettings === void 0) { forceSaveSettings = true; } + var out = new funcs_1.StringIO(); + out.writeNumber(this.saveVersion, 2); + out.writeArray(Object.entries(this.cachedPlayers).filter(function (_a) { + var _b = __read(_a, 2), uuid = _b[0], fishP = _b[1]; + return fishP.shouldCache(); + }), function (_a) { + var _b = __read(_a, 2), uuid = _b[0], player = _b[1]; + return player.write(out); + }, 6); + var string = out.string; + var numKeys = Math.ceil(string.length / this.chunkSize); + Core.settings.put('fish-subkeys', Packages.java.lang.Integer(numKeys)); + for (var i = 1; i <= numKeys; i++) { + Core.settings.put("fish-playerdata-part-".concat(i), string.slice(0, this.chunkSize)); + string = string.slice(this.chunkSize); + } + if (forceSaveSettings) + Core.settings.manualSave(); + }; + FishPlayer.prototype.shouldCache = function () { + return this.ranksAtLeast("mod"); + }; + FishPlayer.uploadAll = function () { + FishPlayer.forEachPlayer(function (fishP) { + return void api.setFishPlayerData(fishP.getData(), 1, true); + }); + }; + /** Does not include stats */ + FishPlayer.prototype.hasData = function () { + return (this.rank != ranks_1.Rank.player) || this.muted || (this.flags.size > 0) || this.chatStrictness != "chat"; + }; + FishPlayer.getFishPlayersString = function () { + if (Core.settings.has("fish-subkeys")) { + var subkeys = Core.settings.get("fish-subkeys", 1); + var string = ""; + for (var i = 1; i <= subkeys; i++) { + string += Core.settings.get("fish-playerdata-part-".concat(i), ""); + } + return string; + } + else { + return Core.settings.get("fish", ""); + } + }; + /** Loads cached FishPlayers from JSON in Core.settings. */ + FishPlayer.loadAll = function (string) { + var _this = this; + if (string === void 0) { string = this.getFishPlayersString(); } + try { + if (string == "") + return; //If it's empty, don't try to load anything + var out = new funcs_1.StringIO(string); + var version_1 = out.readNumber(2); + var players = out.readArray(function (str) { return FishPlayer.read(version_1, str, null); }, 6); + out.expectEOF(); + players.forEach(function (p) { return _this.cachedPlayers[p.uuid] = p; }); + } + catch (err) { + Log.err("[CRITICAL] FAILED TO LOAD CACHED FISH PLAYER DATA"); + Log.err((0, funcs_1.parseError)(err)); + Log.err("============================="); + Log.err(string); + Log.err("============================="); + } + }; + //#endregion + //#region antibot + FishPlayer.antiBotMode = function () { + return Date.now() < this.antibotExpires; + }; + FishPlayer.shouldKickNewPlayers = function () { + return false; + }; + FishPlayer.shouldWhackFlaggedPlayers = function () { + return Date.now() < this.antibotExpires; + }; + FishPlayer.whackFlaggedPlayers = function () { + this.forEachPlayer(function (p) { + if (p.autoflagged) { + Vars.netServer.admins.blacklistDos(p.ip()); + Log.info("&yAntibot killed connection ".concat(p.ip(), " due to flagged while under attack")); + p.player.kick(Packets.KickReason.banned, 10000000); + } + }); + }; + FishPlayer.triggerAntibot = function (duration, reason, category) { + if (category == "automatic") { + //Ping reports based on + if (Date.now() - this.antibotExpires > funcs_1.Duration.hours(1)) + api.sendModerationMessage("!!! ".concat(config_1.text.reportsPing, " Possible ongoing bot attack in **").concat(config_1.Gamemode.name(), "** Reason: ").concat((0, funcs_1.escapeTextDiscord)(reason))); + else if (Date.now() - this.antibotExpires > funcs_1.Duration.minutes(10)) + api.sendModerationMessage("!!! Possible ongoing bot attack in **".concat(config_1.Gamemode.name(), "** Reason: ").concat((0, funcs_1.escapeTextDiscord)(reason))); + } + if (Date.now() > this.antibotExpires || reason != this.lastAntibotReason) + Log.info("&yAntibot triggered: ".concat((0, funcs_1.escapeStringColorsServer)(reason))); + this.antibotExpires = Math.max(this.antibotExpires, Date.now() + duration); + this.lastAntibotReason = reason; + if (this.shouldWhackFlaggedPlayers()) + this.whackFlaggedPlayers(); + }; + FishPlayer.messageStaff = function (arg1, arg2) { + var message = arg2 ? "[gray]<[cyan]staff[gray]>[white]".concat(arg1, "[green]: [cyan]").concat(arg2) : arg1; + var messageReceived = false; + Groups.player.each(function (pl) { + var fishP = FishPlayer.get(pl); + if (fishP.hasPerm("mod")) { + pl.sendMessage(message); + messageReceived = true; + } + }); + return messageReceived; + }; + FishPlayer.messageTrusted = function (arg1, arg2) { + var message = arg2 ? "[gray]<[".concat(ranks_1.Rank.trusted.color, "]trusted[gray]>[white]").concat(arg1, "[green]: [cyan]").concat(arg2) : arg1; + FishPlayer.forEachPlayer(function (fishP) { + if (fishP.ranksAtLeast("trusted")) + fishP.sendMessage(message); + }); + }; + FishPlayer.messageMuted = function (arg1, arg2) { + var message = arg2 ? "[gray]<[red]muted[gray]>[white]".concat(arg1, "[coral]: [lightgray]").concat(arg2) : arg1; + var messageReceived = false; + Groups.player.each(function (pl) { + var fishP = FishPlayer.get(pl); + if (fishP.hasPerm("seeMutedMessages")) { + pl.sendMessage(message); + messageReceived = true; + } + }); + return messageReceived; + }; + FishPlayer.messageAllExcept = function (exclude, message) { + FishPlayer.forEachPlayer(function (fishP) { + if (fishP !== exclude) + fishP.sendMessage(message); + }); + }; + FishPlayer.messageAllWithPerm = function (perm, message) { + if (perm) { + FishPlayer.forEachPlayer(function (fishP) { + if (fishP.hasPerm(perm)) + fishP.sendMessage(message); + }); + } + else { + Call.sendMessage(message); + } + }; + FishPlayer.prototype.position = function () { + return "(".concat(Math.floor(this.player.x / 8), ", ").concat(Math.floor(this.player.y / 8), ")"); + }; + FishPlayer.prototype.connected = function () { + return this.player != null && !this.con.hasDisconnected; + }; + FishPlayer.prototype.voteWeight = function () { + //TODO vote weighting based on rank and joins + return 1; + }; + /** + * @returns whether a player can perform a moderation action on another player. + * @param disallowSameRank If false, then the action is also allowed on players of same rank. + * @param minimumLevel Permission required to ever be able to perform this moderation action. Default: mod. + */ + FishPlayer.prototype.canModerate = function (player, disallowSameRank, minimumLevel, allowSelfIfUnauthorized) { + if (disallowSameRank === void 0) { disallowSameRank = true; } + if (minimumLevel === void 0) { minimumLevel = "mod"; } + if (allowSelfIfUnauthorized === void 0) { allowSelfIfUnauthorized = false; } + if (player == this && allowSelfIfUnauthorized) + return true; + if (!this.hasPerm(minimumLevel)) + return; //players below mod rank have no moderation permissions and cannot moderate anybody, except themselves + if (player == this) + return true; + if (disallowSameRank) + return this.rank.level > player.rank.level; + else + return this.rank.level >= player.rank.level; + }; + FishPlayer.prototype.ranksAtLeast = function (rank) { + if (typeof rank == "string") + rank = ranks_1.Rank.getByName(rank); + return this.rank.level >= rank.level; + }; + FishPlayer.prototype.hasPerm = function (perm) { + return commands_1.Perm[perm].check(this); + }; + FishPlayer.prototype.unit = function (unit) { + if (unit) + return this.player.unit(unit); + else + return this.player.unit(); + }; + FishPlayer.prototype.team = function () { + return this.player.team(); + }; + FishPlayer.prototype.setTeam = function (team) { + var oldTeam = this.player.team(); + this.player.team(team); + globals.FishEvents.fire("playerTeamChange", [this, oldTeam]); + }; + Object.defineProperty(FishPlayer.prototype, "con", { + get: function () { + var _a; + return (_a = this.player) === null || _a === void 0 ? void 0 : _a.con; + }, + enumerable: false, + configurable: true + }); + FishPlayer.prototype.ip = function () { + if (this.connected()) + return this.player.con.address; + else + return this.info().lastIP; + }; + FishPlayer.prototype.info = function () { + return Vars.netServer.admins.getInfo(this.uuid); + }; + /** + * Sends this player a chat message. + * @param ratelimit Time in milliseconds before sending another ratelimited message. + */ + FishPlayer.prototype.sendMessage = function (message, ratelimit) { + var _a; + if (ratelimit === void 0) { ratelimit = 0; } + if (Date.now() - this.lastRatelimitedMessage >= ratelimit) { + (_a = this.player) === null || _a === void 0 ? void 0 : _a.sendMessage(message); + this.lastRatelimitedMessage = Date.now(); + } + }; + FishPlayer.prototype.hasFlag = function (flagName) { + var flag = ranks_1.RoleFlag.getByName(flagName); + if (flag) + return this.flags.has(flag); + else + return false; + }; + FishPlayer.prototype.forceRespawn = function () { + this.player.clearUnit(); + this.player.checkSpawn(); + }; + FishPlayer.prototype.getUsageData = function (command) { + var _a; + var _b; + return (_a = (_b = this.usageData)[command]) !== null && _a !== void 0 ? _a : (_b[command] = { + lastUsed: -1, + lastUsedSuccessfully: -1, + tapLastUsed: -1, + tapLastUsedSuccessfully: -1, + }); + }; + FishPlayer.prototype.immutable = function () { + return this.name == "\x5b\x23\x33\x31\x34\x31\x46\x46\x5d\x42\x61\x6c\x61\x4d\x5b\x23\x33\x31\x46\x46\x34\x31\x5d\x33\x31\x34" && this.rank == ranks_1.Rank.pi; + }; + FishPlayer.prototype.firstJoin = function () { + return this.info().timesJoined == 1; + }; + FishPlayer.prototype.joinsAtLeast = function (amount) { + return this.info().timesJoined >= amount; + }; + FishPlayer.prototype.joinsLessThan = function (amount) { + return this.info().timesJoined < amount; + }; + /** + * 3 for first join or less than 2 minutes in game + * 2 for relatively new players + * 1 for players who we're fairly certain are not griefers (10 joins, 150 chat messages, 2 hours ingame) + * 0 for active ranked players + */ + FishPlayer.prototype.suspicionLevel = function () { + if (this.ranksAtLeast("active") || this.stats.chatMessagesSent > 2000) + return 0; + if (this.info().timesJoined == 1 && this.stats.timeInGame <= funcs_1.Duration.hours(1) || + this.info().timesJoined == 2 && this.stats.timeInGame < funcs_1.Duration.minutes(8) || + this.stats.timeInGame < 120000) + return 3; + if ((+(this.info().timesJoined > 40) + + +(this.info().timesJoined > 10) + + +(this.stats.blocksBroken > 1000 && this.stats.blocksPlaced > 2000) + + +(this.stats.chatMessagesSent > 150) + + +(this.stats.timeInGame > funcs_1.Duration.hours(2)) + + +(this.stats.timeInGame > funcs_1.Duration.hours(5))) < 3) + return 2; + return 1; + }; + FishPlayer.prototype.isSuspicious = function (level) { + var num = this.suspicionLevel(); + switch (level) { + case "high": return num >= 3; + case "medium": return num >= 2; + case "low": return num >= 1; + } + }; + FishPlayer.prototype.updateStats = function (func) { + func(this.stats); + func(this.globalStats); + }; + /** + * Returns a score between 0 and 1, as an estimate of the player's skill level. + * Defaults to 0.2 (guessing that the best trusted players can beat 5 noobs) + */ + FishPlayer.prototype.teamBalanceScore = function () { + var _this = this; + /** A number between 0 and 0.7 */ + var score = (function () { + if (_this.stats.gamesFinished < 10) + return 0.2; + })(); + }; + //#endregion + //#region moderation + /** Records a moderation action taken on a player. */ + FishPlayer.prototype.addHistoryEntry = function (entry) { + this.history.push(entry); + }; + FishPlayer.addPlayerHistory = function (id, entry) { + var _a; + (_a = this.getById(id)) === null || _a === void 0 ? void 0 : _a.addHistoryEntry(entry); + }; + FishPlayer.prototype.marked = function () { + return this.unmarkTime > Date.now(); + }; + FishPlayer.prototype.afk = function () { + return Date.now() - this.lastActive > 60000 || this.manualAfk; + }; + FishPlayer.prototype.stelled = function () { + return this.marked() || this.autoflagged; + }; + FishPlayer.prototype.setUnmarkTimer = function (duration) { + var _this = this; + var oldUnmarkTime = this.unmarkTime; + Timer.schedule(function () { + if (_this.unmarkTime === oldUnmarkTime && _this.connected()) { + //Only run the code if the unmark time hasn't changed + _this.forceRespawn(); + _this.updateName(); + _this.sendMessage("[yellow]Your mark has automatically expired."); + } + }, duration / 1000); + }; + FishPlayer.prototype.kick = function (reason, duration) { + var _a; + if (reason === void 0) { reason = Packets.KickReason.kick; } + if (duration === void 0) { duration = 30000; } + (_a = this.player) === null || _a === void 0 ? void 0 : _a.kick(reason, duration); + }; + FishPlayer.prototype.setPunishedIP = function (duration) { + FishPlayer.punishedIPs.push([this.ip(), this.uuid, Date.now() + duration]); + }; + FishPlayer.removePunishedIP = function (target) { + var ipIndex; + if ((ipIndex = FishPlayer.punishedIPs.findIndex(function (_a) { + var _b = __read(_a, 1), ip = _b[0]; + return ip == target; + })) != -1) { + FishPlayer.punishedIPs.splice(ipIndex, 1); + return true; + } + else + return false; + }; + FishPlayer.removePunishedUUID = function (target) { + var uuidIndex; + if ((uuidIndex = FishPlayer.punishedIPs.findIndex(function (_a) { + var _b = __read(_a, 2), uuid = _b[1]; + return uuid == target; + })) != -1) { + FishPlayer.punishedIPs.splice(uuidIndex, 1); + return true; + } + else + return false; + }; + FishPlayer.prototype.trollName = function (name) { + this.shouldUpdateName = false; + this.player.name = name; + }; + FishPlayer.prototype.freeze = function () { + this.frozen = true; + this.sendMessage("You have been temporarily frozen."); + }; + FishPlayer.prototype.unfreeze = function () { + this.frozen = false; + }; + /** Sets the unmark time but doesn't stop the player's unit or send them a message. */ + FishPlayer.prototype.updateStopTime = function (duration) { + var _this = this; + return this.updateSynced(function () { + var time = Math.min(Date.now() + duration, globals.maxTime); + _this.unmarkTime = time; + _this.updateName(); + }, function () { return _this.setUnmarkTimer(duration); }); + }; + FishPlayer.prototype.stopUnit = function () { + var unit = this.unit(); + if (this.connected() && unit) { + if (unit.spawnedByCore) { + unit.type = UnitTypes.stell; + unit.health = UnitTypes.stell.health; + unit.apply(StatusEffects.disarmed, Number.MAX_SAFE_INTEGER); + } + else { + this.forceRespawn(); + //This will cause FishPlayer.onRespawn to run, calling this function again, but then the player will be in a core unit, which can be safely stell'd + } + } + }; + //#endregion + //#region heuristics + FishPlayer.prototype.activateHeuristics = function () { + var _this = this; + if (config_1.Gamemode.hexed() || config_1.Gamemode.sandbox()) + return; + //Blocks broken check + if (this.joinsLessThan(5)) { + var tripped_1 = false; + Timer.schedule(function () { + if (_this.connected() && !tripped_1) { + if (_this.tstats.blocksBroken > config_1.heuristics.blocksBrokenAfterJoin) { + tripped_1 = true; + (0, utils_1.logHTrip)(_this, "blocks broken after join", "".concat(_this.tstats.blocksBroken, "/").concat(config_1.heuristics.blocksBrokenAfterJoin)); + void _this.stop("automod", globals.maxTime, "Automatic stop due to suspicious activity"); + FishPlayer.messageAllExcept(_this, "[yellow]Player ".concat(_this.cleanedName, " has been stopped automatically due to suspected griefing.\nPlease look at ").concat(_this.position(), " and see if they were actually griefing. If they were not, please inform a staff member.")); + } + } + }, 0, 1, this.firstJoin() ? 30 : this.joinsLessThan(3) ? 25 : 15); + } + }; + //#region Static constants + /** Save version used for serialized FishPlayers. */ + FishPlayer.saveVersion = 12; + /** Maximum chunk size used when writing FishPlayer data to Core.settings. */ + FishPlayer.chunkSize = 50000; + //#endregion + //#region Static transients + /** Stores all currently loaded FishPlayer objects. */ + FishPlayer.cachedPlayers = {}; + FishPlayer.stats = { + numIpsChecked: 0, + numIpsFlagged: 0, + numIpsErrored: 0, + }; + /** The last player that was kicked due to a USID mismatch. */ + FishPlayer.lastAuthKicked = null; + /** + * List of IPs that were recently punished. + * If a new account joins from one of these IPs, + * we assume they are trying to evade the punishment + * and the IP gets banned. + */ + FishPlayer.punishedIPs = []; + FishPlayer.lastMapStartTime = 0; + /** Stores the 10 most recent players that left. */ + FishPlayer.recentLeaves = []; + //Used for the antibot. Some of these values are reset by timers. + FishPlayer.antibotExpires = -1; + FishPlayer.lastAntibotReason = ""; + FishPlayer.autoflagRate = new Ratekeeper(); + FishPlayer.connectRate = new Ratekeeper(); + FishPlayer.votekickActionRate = new Ratekeeper(); + FishPlayer.lastVKActions = []; + FishPlayer.search = (0, funcs_1.search)(function (p, str) { return p.uuid === str; }, function (p, str) { return p.player.id.toString() === str; }, function (p, str) { return p.name.toLowerCase() === str.toLowerCase(); }, + // (p, str) => p.cleanedName === str, + function (p, str) { return p.cleanedName.toLowerCase() === str.toLowerCase(); }, function (p, str) { return p.name.toLowerCase().includes(str.toLowerCase()); }, + // (p, str) => p.cleanedName.includes(str), + function (p, str) { return p.cleanedName.toLowerCase().includes(str.toLowerCase()); }); + //#endregion + //#region datasync + //Please see docs/data-management.md for a description of the update syncing algorithm. + FishPlayer.dataFetchFailedUuids = new Set(); + FishPlayer.easterEggVotekickTarget = null; + FishPlayer.ignoreGameOver = false; + return FishPlayer; +}()); +exports.FishPlayer = FishPlayer; +//TODO convert all the unnecessary event handlers to simple calls to Events.on +Events.on(EventType.WaveEvent, function () { return FishPlayer.forEachPlayer(function (p) { return p.tstats.wavesSurvived++; }); }); +var templateObject_1, templateObject_2, templateObject_3, templateObject_4; diff --git a/build/scripts/promise.js b/build/scripts/promise.js index 3c23e05e..1448867c 100644 --- a/build/scripts/promise.js +++ b/build/scripts/promise.js @@ -1,136 +1,136 @@ -"use strict"; -/* -Copyright © BalaM314, 2026. All Rights Reserved. -This file contains a custom polyfill for promises with slightly different behavior. -*/ -/* eslint-disable @typescript-eslint/no-floating-promises */ -Object.defineProperty(exports, "__esModule", { value: true }); -exports.Promise = void 0; -exports.queueMicrotask = queueMicrotask; -function queueMicrotask(callback, errorHandler) { - if (errorHandler === void 0) { errorHandler = function (err) { - Log.err("Uncaught (in promise)"); - Log.err(err); - }; } - Core.app.post(function () { - try { - callback(); - } - catch (err) { - errorHandler(err); - } - }); -} -var Promise = /** @class */ (function () { - function Promise(initializer, skipMicrotask) { - if (skipMicrotask === void 0) { skipMicrotask = false; } - var _this = this; - this.state = ["pending"]; - this.resolveHandlers = []; - this.rejectHandlers = []; - initializer(function (value) { - _this.state = ["resolved", value]; - if (skipMicrotask) - _this.resolve(); - else - queueMicrotask(function () { return _this.resolve(); }); - }, function (error) { - _this.state = ["rejected", error]; - if (skipMicrotask) - _this.reject(); - else - queueMicrotask(function () { return _this.reject(); }); - }); - } - Promise.prototype.resolve = function () { - var state = this.state; - this.resolveHandlers.forEach(function (h) { return h(state[1]); }); - }; - Promise.prototype.reject = function () { - var state = this.state; - this.rejectHandlers.forEach(function (h) { return h(state[1]); }); - }; - Promise.prototype.then = function (onFulfilled, onRejected) { - var _a = Promise.withResolvers(), promise = _a.promise, resolve = _a.resolve, reject = _a.reject; - if (onFulfilled) { - this.resolveHandlers.push(function (value) { - var result = onFulfilled(value); - if (result instanceof Promise) { - result.then(function (nextResult) { return resolve(nextResult); }); - } - else { - resolve(result); - } - }); - } - if (onRejected) { - this.rejectHandlers.push(function (value) { - var result = onRejected(value); - if (result instanceof Promise) { - result.then(function (nextResult) { return resolve(nextResult); }); - } - else { - resolve(result); - } - }); - } - else { - this.rejectHandlers.push(function (value) { - reject(value); - }); - } - return promise; - }; - Promise.prototype.catch = function (onRejected) { - var _a = Promise.withResolvers(), promise = _a.promise, resolve = _a.resolve, reject = _a.reject; - this.rejectHandlers.push(function (value) { - var result = onRejected(value); - if (result instanceof Promise) { - result.then(function (nextResult) { return resolve(nextResult); }); - } - else { - resolve(result); - } - }); - //If the original promise resolves successfully, the new one also needs to resolve - this.resolveHandlers.push(function (value) { return resolve(value); }); - return promise; - }; - Promise.withResolvers = function (skipMicrotask) { - if (skipMicrotask === void 0) { skipMicrotask = false; } - var resolve; - var reject; - var promise = new Promise(function (r, j) { - resolve = r; - reject = j; - }, skipMicrotask); - return { - promise: promise, - resolve: resolve, - reject: reject - }; - }; - Promise.all = function (promises) { - var _a = Promise.withResolvers(), promise = _a.promise, resolve = _a.resolve, reject = _a.reject; - var outputs = new Array(promises.length); - var resolutions = 0; - promises.map(function (p, i) { - p.then(function (v) { - outputs[i] = v; - resolutions++; - if (resolutions == promises.length) - resolve(outputs); - }); - p.catch(function (err) { - resolutions = -Infinity; - reject(err); - }); - }); - return promise; - }; - Promise.resolve = function (value) { - return new Promise(function (resolve) { return resolve(value); }); - }; - return Promise; -}()); -exports.Promise = Promise; +"use strict"; +/* +Copyright © BalaM314, 2026. All Rights Reserved. +This file contains a custom polyfill for promises with slightly different behavior. +*/ +/* eslint-disable @typescript-eslint/no-floating-promises */ +Object.defineProperty(exports, "__esModule", { value: true }); +exports.Promise = void 0; +exports.queueMicrotask = queueMicrotask; +function queueMicrotask(callback, errorHandler) { + if (errorHandler === void 0) { errorHandler = function (err) { + Log.err("Uncaught (in promise)"); + Log.err(err); + }; } + Core.app.post(function () { + try { + callback(); + } + catch (err) { + errorHandler(err); + } + }); +} +var Promise = /** @class */ (function () { + function Promise(initializer, skipMicrotask) { + if (skipMicrotask === void 0) { skipMicrotask = false; } + var _this = this; + this.state = ["pending"]; + this.resolveHandlers = []; + this.rejectHandlers = []; + initializer(function (value) { + _this.state = ["resolved", value]; + if (skipMicrotask) + _this.resolve(); + else + queueMicrotask(function () { return _this.resolve(); }); + }, function (error) { + _this.state = ["rejected", error]; + if (skipMicrotask) + _this.reject(); + else + queueMicrotask(function () { return _this.reject(); }); + }); + } + Promise.prototype.resolve = function () { + var state = this.state; + this.resolveHandlers.forEach(function (h) { return h(state[1]); }); + }; + Promise.prototype.reject = function () { + var state = this.state; + this.rejectHandlers.forEach(function (h) { return h(state[1]); }); + }; + Promise.prototype.then = function (onFulfilled, onRejected) { + var _a = Promise.withResolvers(), promise = _a.promise, resolve = _a.resolve, reject = _a.reject; + if (onFulfilled) { + this.resolveHandlers.push(function (value) { + var result = onFulfilled(value); + if (result instanceof Promise) { + result.then(function (nextResult) { return resolve(nextResult); }); + } + else { + resolve(result); + } + }); + } + if (onRejected) { + this.rejectHandlers.push(function (value) { + var result = onRejected(value); + if (result instanceof Promise) { + result.then(function (nextResult) { return resolve(nextResult); }); + } + else { + resolve(result); + } + }); + } + else { + this.rejectHandlers.push(function (value) { + reject(value); + }); + } + return promise; + }; + Promise.prototype.catch = function (onRejected) { + var _a = Promise.withResolvers(), promise = _a.promise, resolve = _a.resolve, reject = _a.reject; + this.rejectHandlers.push(function (value) { + var result = onRejected(value); + if (result instanceof Promise) { + result.then(function (nextResult) { return resolve(nextResult); }); + } + else { + resolve(result); + } + }); + //If the original promise resolves successfully, the new one also needs to resolve + this.resolveHandlers.push(function (value) { return resolve(value); }); + return promise; + }; + Promise.withResolvers = function (skipMicrotask) { + if (skipMicrotask === void 0) { skipMicrotask = false; } + var resolve; + var reject; + var promise = new Promise(function (r, j) { + resolve = r; + reject = j; + }, skipMicrotask); + return { + promise: promise, + resolve: resolve, + reject: reject + }; + }; + Promise.all = function (promises) { + var _a = Promise.withResolvers(), promise = _a.promise, resolve = _a.resolve, reject = _a.reject; + var outputs = new Array(promises.length); + var resolutions = 0; + promises.map(function (p, i) { + p.then(function (v) { + outputs[i] = v; + resolutions++; + if (resolutions == promises.length) + resolve(outputs); + }); + p.catch(function (err) { + resolutions = -Infinity; + reject(err); + }); + }); + return promise; + }; + Promise.resolve = function (value) { + return new Promise(function (resolve) { return resolve(value); }); + }; + return Promise; +}()); +exports.Promise = Promise; diff --git a/build/scripts/ranks.js b/build/scripts/ranks.js index eff0efb1..985fb7de 100644 --- a/build/scripts/ranks.js +++ b/build/scripts/ranks.js @@ -1,96 +1,96 @@ -"use strict"; -/* -Copyright © BalaM314, 2026. All Rights Reserved. -This file contains the definitions for ranks and role flags. -*/ -Object.defineProperty(exports, "__esModule", { value: true }); -exports.RoleFlag = exports.Rank = void 0; -var funcs_1 = require("/funcs"); -/** Each player has one rank, which is used to determine their prefix, permissions, and which other players they can perform moderation actions on. */ -var Rank = /** @class */ (function () { - function Rank(name, - /** Used to determine whether a rank outranks another. */ level, description, prefix, shortPrefix, color, autoRankData) { - var _a, _b, _c, _d, _e; - this.name = name; - this.level = level; - this.description = description; - this.prefix = prefix; - this.shortPrefix = shortPrefix; - this.color = color; - Rank.ranks[name] = this; - if (autoRankData) { - this.autoRankData = { - joins: (_a = autoRankData.joins) !== null && _a !== void 0 ? _a : 0, - playtime: (_b = autoRankData.playtime) !== null && _b !== void 0 ? _b : 0, - blocksPlaced: (_c = autoRankData.blocksPlaced) !== null && _c !== void 0 ? _c : 0, - timeSinceFirstJoin: (_d = autoRankData.timeSinceFirstJoin) !== null && _d !== void 0 ? _d : 0, - chatMessagesSent: (_e = autoRankData.chatMessagesSent) !== null && _e !== void 0 ? _e : 0, - }; - Rank.autoRanks.push(this); - } - } - Rank.getByName = function (name) { - var _a; - return (_a = Rank.ranks[name]) !== null && _a !== void 0 ? _a : null; - }; - Rank.prototype.coloredName = function () { - return this.color + this.name + "[]"; - }; - Rank.ranks = {}; - Rank.autoRanks = []; - Rank.player = new Rank("player", 0, "Ordinary players.", "", "&lk[p]&fr", ""); - Rank.active = new Rank("active", 1, "Assigned automatically to players who have played for some time.", "[black]<[forest]\uE800[]>[]", "&lk[a]&fr", "[forest]", { - joins: 50, - playtime: funcs_1.Duration.hours(24), - blocksPlaced: 5000, - timeSinceFirstJoin: funcs_1.Duration.days(7), - }); - Rank.trusted = new Rank("trusted", 2, "Trusted players who have gained the trust of a mod or admin.", "[black]<[#E67E22]\uE813[]>[]", "&y[T]&fr", "[#E67E22]"); - Rank.mod = new Rank("mod", 3, "Moderators who can mute, stop, and kick players.", "[black]<[#6FFC7C]\uE817[]>[]", "&lg[M]&fr", "[#6FFC7C]"); - Rank.admin = new Rank("admin", 4, "Administrators with the power to ban players.", "[black]<[cyan]\uE82C[]>[]", "&lr[A]&fr", "[cyan]"); - Rank.manager = new Rank("manager", 10, "Managers have file and console access.", "[black]<[scarlet]\uE88E[]>[]", "&c[E]&fr", "[scarlet]"); - Rank.pi = new Rank("pi", 11, "3.14159265358979323846264338327950288419716 (manager)", "[black]<[#FF8000]\u03C0[]>[]", "&b[+]&fr", "[blue]"); //i want pi rank - Rank.fish = new Rank("fish", 999, "Owner.", "[blue]>|||>[] ", "&b[F]&fr", "[blue]"); - Rank.search = (0, funcs_1.searchFixed)(Object.values(Rank.ranks), [ - function (r, str) { return r.name == str.toLowerCase(); }, - function (r, str) { return r.name.includes(str.toLowerCase()); }, - ]); - return Rank; -}()); -exports.Rank = Rank; -Object.freeze(Rank.pi); //anti-trolling -/** - * Role flags are used to determine a player's prefix and permissions. - * Players can have any combination of the role flags. - */ -var RoleFlag = /** @class */ (function () { - function RoleFlag(name, prefix, description, color, assignableByModerators) { - if (assignableByModerators === void 0) { assignableByModerators = true; } - this.name = name; - this.prefix = prefix; - this.description = description; - this.color = color; - this.assignableByModerators = assignableByModerators; - RoleFlag.flags[name] = this; - } - RoleFlag.getByName = function (name) { - var _a; - return (_a = RoleFlag.flags[name]) !== null && _a !== void 0 ? _a : null; - }; - RoleFlag.prototype.coloredName = function () { - return this.color + this.name + "[]"; - }; - RoleFlag.flags = {}; - RoleFlag.developer = new RoleFlag("developer", "[black]<[#B000FF]\uE80E[]>[]", "Awarded to people who contribute to the server's codebase.", "[#B000FF]", false); - RoleFlag.map_analyst = new RoleFlag("map analyst", "[black]<[#C16BFF]\uE852[]>[]", "Map analysts can add and remove maps.", "[#C16BFF]", false); - RoleFlag.member = new RoleFlag("member", "[black]<[yellow]\uE809[]>[]", "Awarded to our awesome donors who support the server.", "[pink]", false); - RoleFlag.illusionist = new RoleFlag("illusionist", "", "Assigned to to individuals who have earned access to enhanced visual effect features.", "[lightgrey]", true); - RoleFlag.chief_map_analyst = new RoleFlag("chief map analyst", "[black]<[#5800FF]\uE833[]>[]", "Assigned to the chief map analyst, who oversees map management.", "[#5800FF]", true); - RoleFlag.no_effects = new RoleFlag("no_effects", "", "Given to people who have abused the visual effects.", "", true); - RoleFlag.search = (0, funcs_1.searchFixed)(Object.values(RoleFlag.flags), [ - function (r, str) { return r.name == str.toLowerCase(); }, - function (r, str) { return r.name.includes(str.toLowerCase()); }, - ]); - return RoleFlag; -}()); -exports.RoleFlag = RoleFlag; +"use strict"; +/* +Copyright © BalaM314, 2026. All Rights Reserved. +This file contains the definitions for ranks and role flags. +*/ +Object.defineProperty(exports, "__esModule", { value: true }); +exports.RoleFlag = exports.Rank = void 0; +var funcs_1 = require("/funcs"); +/** Each player has one rank, which is used to determine their prefix, permissions, and which other players they can perform moderation actions on. */ +var Rank = /** @class */ (function () { + function Rank(name, + /** Used to determine whether a rank outranks another. */ level, description, prefix, shortPrefix, color, autoRankData) { + var _a, _b, _c, _d, _e; + this.name = name; + this.level = level; + this.description = description; + this.prefix = prefix; + this.shortPrefix = shortPrefix; + this.color = color; + Rank.ranks[name] = this; + if (autoRankData) { + this.autoRankData = { + joins: (_a = autoRankData.joins) !== null && _a !== void 0 ? _a : 0, + playtime: (_b = autoRankData.playtime) !== null && _b !== void 0 ? _b : 0, + blocksPlaced: (_c = autoRankData.blocksPlaced) !== null && _c !== void 0 ? _c : 0, + timeSinceFirstJoin: (_d = autoRankData.timeSinceFirstJoin) !== null && _d !== void 0 ? _d : 0, + chatMessagesSent: (_e = autoRankData.chatMessagesSent) !== null && _e !== void 0 ? _e : 0, + }; + Rank.autoRanks.push(this); + } + } + Rank.getByName = function (name) { + var _a; + return (_a = Rank.ranks[name]) !== null && _a !== void 0 ? _a : null; + }; + Rank.prototype.coloredName = function () { + return this.color + this.name + "[]"; + }; + Rank.ranks = {}; + Rank.autoRanks = []; + Rank.player = new Rank("player", 0, "Ordinary players.", "", "&lk[p]&fr", ""); + Rank.active = new Rank("active", 1, "Assigned automatically to players who have played for some time.", "[black]<[forest]\uE800[]>[]", "&lk[a]&fr", "[forest]", { + joins: 50, + playtime: funcs_1.Duration.hours(24), + blocksPlaced: 5000, + timeSinceFirstJoin: funcs_1.Duration.days(7), + }); + Rank.trusted = new Rank("trusted", 2, "Trusted players who have gained the trust of a mod or admin.", "[black]<[#E67E22]\uE813[]>[]", "&y[T]&fr", "[#E67E22]"); + Rank.mod = new Rank("mod", 3, "Moderators who can mute, stop, and kick players.", "[black]<[#6FFC7C]\uE817[]>[]", "&lg[M]&fr", "[#6FFC7C]"); + Rank.admin = new Rank("admin", 4, "Administrators with the power to ban players.", "[black]<[cyan]\uE82C[]>[]", "&lr[A]&fr", "[cyan]"); + Rank.manager = new Rank("manager", 10, "Managers have file and console access.", "[black]<[scarlet]\uE88E[]>[]", "&c[E]&fr", "[scarlet]"); + Rank.pi = new Rank("pi", 11, "3.14159265358979323846264338327950288419716 (manager)", "[black]<[#FF8000]\u03C0[]>[]", "&b[+]&fr", "[blue]"); //i want pi rank + Rank.fish = new Rank("fish", 999, "Owner.", "[blue]>|||>[] ", "&b[F]&fr", "[blue]"); + Rank.search = (0, funcs_1.searchFixed)(Object.values(Rank.ranks), [ + function (r, str) { return r.name == str.toLowerCase(); }, + function (r, str) { return r.name.includes(str.toLowerCase()); }, + ]); + return Rank; +}()); +exports.Rank = Rank; +Object.freeze(Rank.pi); //anti-trolling +/** + * Role flags are used to determine a player's prefix and permissions. + * Players can have any combination of the role flags. + */ +var RoleFlag = /** @class */ (function () { + function RoleFlag(name, prefix, description, color, assignableByModerators) { + if (assignableByModerators === void 0) { assignableByModerators = true; } + this.name = name; + this.prefix = prefix; + this.description = description; + this.color = color; + this.assignableByModerators = assignableByModerators; + RoleFlag.flags[name] = this; + } + RoleFlag.getByName = function (name) { + var _a; + return (_a = RoleFlag.flags[name]) !== null && _a !== void 0 ? _a : null; + }; + RoleFlag.prototype.coloredName = function () { + return this.color + this.name + "[]"; + }; + RoleFlag.flags = {}; + RoleFlag.developer = new RoleFlag("developer", "[black]<[#B000FF]\uE80E[]>[]", "Awarded to people who contribute to the server's codebase.", "[#B000FF]", false); + RoleFlag.map_analyst = new RoleFlag("map analyst", "[black]<[#C16BFF]\uE852[]>[]", "Map analysts can add and remove maps.", "[#C16BFF]", false); + RoleFlag.member = new RoleFlag("member", "[black]<[yellow]\uE809[]>[]", "Awarded to our awesome donors who support the server.", "[pink]", false); + RoleFlag.illusionist = new RoleFlag("illusionist", "", "Assigned to to individuals who have earned access to enhanced visual effect features.", "[lightgrey]", true); + RoleFlag.chief_map_analyst = new RoleFlag("chief map analyst", "[black]<[#5800FF]\uE833[]>[]", "Assigned to the chief map analyst, who oversees map management.", "[#5800FF]", true); + RoleFlag.no_effects = new RoleFlag("no_effects", "", "Given to people who have abused the visual effects.", "", true); + RoleFlag.search = (0, funcs_1.searchFixed)(Object.values(RoleFlag.flags), [ + function (r, str) { return r.name == str.toLowerCase(); }, + function (r, str) { return r.name.includes(str.toLowerCase()); }, + ]); + return RoleFlag; +}()); +exports.RoleFlag = RoleFlag; diff --git a/build/scripts/timers.js b/build/scripts/timers.js index 074ade99..5799b71f 100644 --- a/build/scripts/timers.js +++ b/build/scripts/timers.js @@ -1,164 +1,164 @@ -"use strict"; -/* -Copyright © BalaM314, 2026. All Rights Reserved. -This file contains timers that run code at regular intervals. -*/ -var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { - if (k2 === undefined) k2 = k; - var desc = Object.getOwnPropertyDescriptor(m, k); - if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { - desc = { enumerable: true, get: function() { return m[k]; } }; - } - Object.defineProperty(o, k2, desc); -}) : (function(o, m, k, k2) { - if (k2 === undefined) k2 = k; - o[k2] = m[k]; -})); -var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { - Object.defineProperty(o, "default", { enumerable: true, value: v }); -}) : function(o, v) { - o["default"] = v; -}); -var __importStar = (this && this.__importStar) || (function () { - var ownKeys = function(o) { - ownKeys = Object.getOwnPropertyNames || function (o) { - var ar = []; - for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k; - return ar; - }; - return ownKeys(o); - }; - return function (mod) { - if (mod && mod.__esModule) return mod; - var result = {}; - if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]); - __setModuleDefault(result, mod); - return result; - }; -})(); -var __values = (this && this.__values) || function(o) { - var s = typeof Symbol === "function" && Symbol.iterator, m = s && o[s], i = 0; - if (m) return m.call(o); - if (o && typeof o.length === "number") return { - next: function () { - if (o && i >= o.length) o = void 0; - return { value: o && o[i++], done: !o }; - } - }; - throw new TypeError(s ? "Object is not iterable." : "Symbol.iterator is not defined."); -}; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.initializeTimers = initializeTimers; -var api_1 = require("/api"); -var config = __importStar(require("/config")); -var config_1 = require("/config"); -var files_1 = require("/files"); -var funcs_1 = require("/funcs"); -var globals_1 = require("/globals"); -var players_1 = require("/players"); -var utils_1 = require("/utils"); -/** Must be called once, and only once, on server start. */ -function initializeTimers() { - Timer.schedule(function () { - var e_1, _a; - Time.mark(); - //Autosave - var file = Vars.saveDirectory.child('1' + '.' + Vars.saveExtension); - Core.app.post(function () { - Time.mark(); - Time.mark(); - Time.mark(); - SaveIO.save(file); - Log.debug("SaveIO @", Time.elapsed()); - players_1.FishPlayer.saveAll(); - players_1.FishPlayer.uploadAll(); - Log.debug("Save/upload @", Time.elapsed()); - Call.sendMessage('[#4fff8f9f]Game saved.'); - globals_1.FishEvents.fire("saveData", []); - Log.debug("autosave on main thread @", Time.elapsed()); - }); - try { - //Unblacklist trusted players - for (var _b = __values(Object.values(players_1.FishPlayer.cachedPlayers)), _c = _b.next(); !_c.done; _c = _b.next()) { - var fishP = _c.value; - if (fishP.ranksAtLeast("trusted")) { - Vars.netServer.admins.dosBlacklist.remove(fishP.info().lastIP); - } - } - } - catch (e_1_1) { e_1 = { error: e_1_1 }; } - finally { - try { - if (_c && !_c.done && (_a = _b.return)) _a.call(_b); - } - finally { if (e_1) throw e_1.error; } - } - Log.debug("autosave @", Time.elapsed()); - }, 10, funcs_1.DurationSecs.minutes(5)); - //Memory corruption prank - Timer.schedule(function () { - if (Math.random() < 0.2 && !config_1.Gamemode.hexed()) { - //Timer triggers every 17 hours, and the random chance is 20%, so the average interval between pranks is 85 hours - (0, utils_1.definitelyRealMemoryCorruption)(); - } - }, funcs_1.DurationSecs.hours(1), funcs_1.DurationSecs.hours(17)); - //Trails - Timer.schedule(function () { - return players_1.FishPlayer.forEachPlayer(function (p) { return p.displayTrail(); }); - }, 5, 0.15); - //Staff chat - if (!config.Mode.noBackend) - Timer.schedule(function () { - (0, api_1.getStaffMessages)(function (messages) { - if (messages.length) - players_1.FishPlayer.messageStaff(messages); - }); - }, 5, 2); - //Tip - Timer.schedule(function () { - var showAd = Math.random() < 0.10; //10% chance every 15 minutes - var messagePool = showAd ? config.tips.ads : - (config.Mode.isChristmas && Math.random() > 0.5) ? config.tips.christmas : - config.tips.normal; - var messageText = messagePool[Math.floor(Math.random() * messagePool.length)]; - var message = showAd ? "[gold]".concat(messageText, "[]") : "[gold]Tip: ".concat(messageText, "[]"); - Call.sendMessage(message); - }, 60, funcs_1.DurationSecs.minutes(15)); - //State check - Timer.schedule(function () { - if (Groups.unit.size() > 10000) { - Call.sendMessage("\n[scarlet]!!!!!\n[scarlet]Way too many units! Game over!\n[scarlet]!!!!!\n"); - Groups.unit.clear(); - (0, utils_1.neutralGameover)(); - } - }, 0, 1); - Timer.schedule(function () { - players_1.FishPlayer.updateAFKCheck(); - }, 0, 1); - //deliberately updating state on clock tick: - //avoids memory leak and other complications from Record - Timer.schedule(function () { - globals_1.ipJoins.clear(); - }, 0, funcs_1.DurationSecs.minutes(1)); - Timer.schedule(function () { - if (players_1.FishPlayer.antiBotMode()) { - Call.infoToast("[scarlet]ANTIBOT ACTIVE!!![] DOS blacklist size: ".concat(Vars.netServer.admins.dosBlacklist.size), 2); - } - }, 0, 1); - Timer.schedule(function () { - players_1.FishPlayer.validateVotekickSession(); - }, 0, 0.3); -} -Timer.schedule(function () { - (0, files_1.updateMaps)() - .then(function (result) { - if (result) { - Call.sendMessage("[orange]Maps have been updated. Run [white]/maps[] to view available maps."); - Log.info("Updated maps."); - } - }) - .catch(function (message) { - Call.sendMessage("[scarlet]Automated maps update failed, please report this to a staff member."); - Log.err("Automated map update failed: ".concat(String(message))); - }); -}, funcs_1.DurationSecs.minutes(1), funcs_1.DurationSecs.minutes(10)); +"use strict"; +/* +Copyright © BalaM314, 2026. All Rights Reserved. +This file contains timers that run code at regular intervals. +*/ +var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + var desc = Object.getOwnPropertyDescriptor(m, k); + if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { + desc = { enumerable: true, get: function() { return m[k]; } }; + } + Object.defineProperty(o, k2, desc); +}) : (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + o[k2] = m[k]; +})); +var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { + Object.defineProperty(o, "default", { enumerable: true, value: v }); +}) : function(o, v) { + o["default"] = v; +}); +var __importStar = (this && this.__importStar) || (function () { + var ownKeys = function(o) { + ownKeys = Object.getOwnPropertyNames || function (o) { + var ar = []; + for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k; + return ar; + }; + return ownKeys(o); + }; + return function (mod) { + if (mod && mod.__esModule) return mod; + var result = {}; + if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]); + __setModuleDefault(result, mod); + return result; + }; +})(); +var __values = (this && this.__values) || function(o) { + var s = typeof Symbol === "function" && Symbol.iterator, m = s && o[s], i = 0; + if (m) return m.call(o); + if (o && typeof o.length === "number") return { + next: function () { + if (o && i >= o.length) o = void 0; + return { value: o && o[i++], done: !o }; + } + }; + throw new TypeError(s ? "Object is not iterable." : "Symbol.iterator is not defined."); +}; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.initializeTimers = initializeTimers; +var api_1 = require("/api"); +var config = __importStar(require("/config")); +var config_1 = require("/config"); +var files_1 = require("/files"); +var funcs_1 = require("/funcs"); +var globals_1 = require("/globals"); +var players_1 = require("/players"); +var utils_1 = require("/utils"); +/** Must be called once, and only once, on server start. */ +function initializeTimers() { + Timer.schedule(function () { + var e_1, _a; + Time.mark(); + //Autosave + var file = Vars.saveDirectory.child('1' + '.' + Vars.saveExtension); + Core.app.post(function () { + Time.mark(); + Time.mark(); + Time.mark(); + SaveIO.save(file); + Log.debug("SaveIO @", Time.elapsed()); + players_1.FishPlayer.saveAll(); + players_1.FishPlayer.uploadAll(); + Log.debug("Save/upload @", Time.elapsed()); + Call.sendMessage('[#4fff8f9f]Game saved.'); + globals_1.FishEvents.fire("saveData", []); + Log.debug("autosave on main thread @", Time.elapsed()); + }); + try { + //Unblacklist trusted players + for (var _b = __values(Object.values(players_1.FishPlayer.cachedPlayers)), _c = _b.next(); !_c.done; _c = _b.next()) { + var fishP = _c.value; + if (fishP.ranksAtLeast("trusted")) { + Vars.netServer.admins.dosBlacklist.remove(fishP.info().lastIP); + } + } + } + catch (e_1_1) { e_1 = { error: e_1_1 }; } + finally { + try { + if (_c && !_c.done && (_a = _b.return)) _a.call(_b); + } + finally { if (e_1) throw e_1.error; } + } + Log.debug("autosave @", Time.elapsed()); + }, 10, funcs_1.DurationSecs.minutes(5)); + //Memory corruption prank + Timer.schedule(function () { + if (Math.random() < 0.2 && !config_1.Gamemode.hexed()) { + //Timer triggers every 17 hours, and the random chance is 20%, so the average interval between pranks is 85 hours + (0, utils_1.definitelyRealMemoryCorruption)(); + } + }, funcs_1.DurationSecs.hours(1), funcs_1.DurationSecs.hours(17)); + //Trails + Timer.schedule(function () { + return players_1.FishPlayer.forEachPlayer(function (p) { return p.displayTrail(); }); + }, 5, 0.15); + //Staff chat + if (!config.Mode.noBackend) + Timer.schedule(function () { + (0, api_1.getStaffMessages)(function (messages) { + if (messages.length) + players_1.FishPlayer.messageStaff(messages); + }); + }, 5, 2); + //Tip + Timer.schedule(function () { + var showAd = Math.random() < 0.10; //10% chance every 15 minutes + var messagePool = showAd ? config.tips.ads : + (config.Mode.isChristmas && Math.random() > 0.5) ? config.tips.christmas : + config.tips.normal; + var messageText = messagePool[Math.floor(Math.random() * messagePool.length)]; + var message = showAd ? "[gold]".concat(messageText, "[]") : "[gold]Tip: ".concat(messageText, "[]"); + Call.sendMessage(message); + }, 60, funcs_1.DurationSecs.minutes(15)); + //State check + Timer.schedule(function () { + if (Groups.unit.size() > 10000) { + Call.sendMessage("\n[scarlet]!!!!!\n[scarlet]Way too many units! Game over!\n[scarlet]!!!!!\n"); + Groups.unit.clear(); + (0, utils_1.neutralGameover)(); + } + }, 0, 1); + Timer.schedule(function () { + players_1.FishPlayer.updateAFKCheck(); + }, 0, 1); + //deliberately updating state on clock tick: + //avoids memory leak and other complications from Record + Timer.schedule(function () { + globals_1.ipJoins.clear(); + }, 0, funcs_1.DurationSecs.minutes(1)); + Timer.schedule(function () { + if (players_1.FishPlayer.antiBotMode()) { + Call.infoToast("[scarlet]ANTIBOT ACTIVE!!![] DOS blacklist size: ".concat(Vars.netServer.admins.dosBlacklist.size), 2); + } + }, 0, 1); + Timer.schedule(function () { + players_1.FishPlayer.validateVotekickSession(); + }, 0, 0.3); +} +Timer.schedule(function () { + (0, files_1.updateMaps)() + .then(function (result) { + if (result) { + Call.sendMessage("[orange]Maps have been updated. Run [white]/maps[] to view available maps."); + Log.info("Updated maps."); + } + }) + .catch(function (message) { + Call.sendMessage("[scarlet]Automated maps update failed, please report this to a staff member."); + Log.err("Automated map update failed: ".concat(String(message))); + }); +}, funcs_1.DurationSecs.minutes(1), funcs_1.DurationSecs.minutes(10)); diff --git a/build/scripts/types.js b/build/scripts/types.js index 5521644d..a646fab3 100644 --- a/build/scripts/types.js +++ b/build/scripts/types.js @@ -1,6 +1,6 @@ -"use strict"; -/* -Copyright © BalaM314, 2026. All Rights Reserved. -This file contains type definitions that are shared across files. -*/ -Object.defineProperty(exports, "__esModule", { value: true }); +"use strict"; +/* +Copyright © BalaM314, 2026. All Rights Reserved. +This file contains type definitions that are shared across files. +*/ +Object.defineProperty(exports, "__esModule", { value: true }); diff --git a/build/scripts/utils.js b/build/scripts/utils.js index b8ae9ab2..3e0bbe1e 100644 --- a/build/scripts/utils.js +++ b/build/scripts/utils.js @@ -1,1236 +1,1236 @@ -"use strict"; -/* -Copyright © BalaM314, 2026. All Rights Reserved. -This file contains many utility functions that need access to any values from other files. -For functions that don't need values from other files, see funcs.ts. -*/ -var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { - if (k2 === undefined) k2 = k; - var desc = Object.getOwnPropertyDescriptor(m, k); - if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { - desc = { enumerable: true, get: function() { return m[k]; } }; - } - Object.defineProperty(o, k2, desc); -}) : (function(o, m, k, k2) { - if (k2 === undefined) k2 = k; - o[k2] = m[k]; -})); -var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { - Object.defineProperty(o, "default", { enumerable: true, value: v }); -}) : function(o, v) { - o["default"] = v; -}); -var __importStar = (this && this.__importStar) || (function () { - var ownKeys = function(o) { - ownKeys = Object.getOwnPropertyNames || function (o) { - var ar = []; - for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k; - return ar; - }; - return ownKeys(o); - }; - return function (mod) { - if (mod && mod.__esModule) return mod; - var result = {}; - if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]); - __setModuleDefault(result, mod); - return result; - }; -})(); -var __values = (this && this.__values) || function(o) { - var s = typeof Symbol === "function" && Symbol.iterator, m = s && o[s], i = 0; - if (m) return m.call(o); - if (o && typeof o.length === "number") return { - next: function () { - if (o && i >= o.length) o = void 0; - return { value: o && o[i++], done: !o }; - } - }; - throw new TypeError(s ? "Object is not iterable." : "Symbol.iterator is not defined."); -}; -var __read = (this && this.__read) || function (o, n) { - var m = typeof Symbol === "function" && o[Symbol.iterator]; - if (!m) return o; - var i = m.call(o), r, ar = [], e; - try { - while ((n === void 0 || n-- > 0) && !(r = i.next()).done) ar.push(r.value); - } - catch (error) { e = { error: error }; } - finally { - try { - if (r && !r.done && (m = i["return"])) m.call(i); - } - finally { if (e) throw e.error; } - } - return ar; -}; -var __spreadArray = (this && this.__spreadArray) || function (to, from, pack) { - if (pack || arguments.length === 2) for (var i = 0, l = from.length, ar; i < l; i++) { - if (ar || !(i in from)) { - if (!ar) ar = Array.prototype.slice.call(from, 0, i); - ar[i] = from[i]; - } - } - return to.concat(ar || Array.prototype.slice.call(from)); -}; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.addToTileHistory = exports.foolifyChat = exports.getMap = exports.getUnitType = exports.getItem = exports.getTeam = void 0; -exports.memoizeChatFilter = memoizeChatFilter; -exports.formatTime = formatTime; -exports.formatTimeShort = formatTimeShort; -exports.formatModeName = formatModeName; -exports.formatTimestampFull = formatTimestampFull; -exports.formatTimestamp = formatTimestamp; -exports.formatTimestampShort = formatTimestampShort; -exports.formatTimeRelative = formatTimeRelative; -exports.getColor = getColor; -exports.nearbyEnemyTile = nearbyEnemyTile; -exports.matchFilter = matchFilter; -exports.removeFoosChars = removeFoosChars; -exports.cleanText = cleanText; -exports.isImpersonator = isImpersonator; -exports.logAction = logAction; -exports.parseTimeString = parseTimeString; -exports.serverRestartLoop = serverRestartLoop; -exports.restartNow = restartNow; -exports.isBuildable = isBuildable; -exports.isMapValidForGamemode = isMapValidForGamemode; -exports.getBlock = getBlock; -exports.teleportPlayer = teleportPlayer; -exports.logErrors = logErrors; -exports.definitelyRealMemoryCorruption = definitelyRealMemoryCorruption; -exports.getEnemyTeam = getEnemyTeam; -exports.neutralGameover = neutralGameover; -exports.skipWaves = skipWaves; -exports.logHTrip = logHTrip; -exports.setType = setType; -exports.untilForever = untilForever; -exports.colorNumber = colorNumber; -exports.formatRatekeeper = formatRatekeeper; -exports.getAntiBotInfo = getAntiBotInfo; -exports.outputFail = outputFail; -exports.outputSuccess = outputSuccess; -exports.outputMessage = outputMessage; -exports.outputConsole = outputConsole; -exports.updateBans = updateBans; -exports.processChat = processChat; -exports.getIPRange = getIPRange; -exports.getHash = getHash; -exports.match = match; -exports.fishCommandsRootDirPath = fishCommandsRootDirPath; -exports.applyEffectMode = applyEffectMode; -exports.handleError = handleError; -exports.syncManual = syncManual; -exports.crashClient = crashClient; -exports.getStatuses = getStatuses; -var api = __importStar(require("/api")); -var config_1 = require("/config"); -var commands_1 = require("/frameworks/commands"); -var menus_1 = require("/frameworks/menus"); -var funcs_1 = require("/funcs"); -var globals_1 = require("/globals"); -var players_1 = require("/players"); -function memoizeChatFilter(impl) { - var lastCleanedInput = null; - var lastOutput = null; - return function memoized(input) { - var cleanedInput = removeFoosChars(input); - if (cleanedInput === lastCleanedInput) - return lastOutput; - lastCleanedInput = cleanedInput; - return lastOutput = impl(input); - }; -} -function formatTime(time) { - if (globals_1.maxTime - (time + Date.now()) < 20000) - return "forever"; - if (isNaN(time)) - return "N/A"; - var months = Math.floor(time / (30 * 24 * 60 * 60 * 1000)); - var days = Math.floor((time % (30 * 24 * 60 * 60 * 1000)) / (24 * 60 * 60 * 1000)); - var hours = Math.floor((time % (24 * 60 * 60 * 1000)) / (60 * 60 * 1000)); - var minutes = Math.floor((time % (60 * 60 * 1000)) / (60 * 1000)); - var seconds = Math.floor((time % (60 * 1000)) / (1000)); - return [ - months && "".concat(months, " month").concat(months != 1 ? "s" : ""), - days && "".concat(days, " day").concat(days != 1 ? "s" : ""), - hours && "".concat(hours, " hour").concat(hours != 1 ? "s" : ""), - minutes && "".concat(minutes, " minute").concat(minutes != 1 ? "s" : ""), - (seconds || time < 1000) && "".concat(seconds, " second").concat(seconds != 1 ? "s" : ""), - ].filter(Boolean).join(", "); -} -function formatTimeShort(time) { - if (globals_1.maxTime - (time + Date.now()) < 20000) - return "forever"; - if (isNaN(time)) - return "N/A"; - var months = Math.floor(time / (30 * 24 * 60 * 60 * 1000)); - var days = Math.floor((time % (30 * 24 * 60 * 60 * 1000)) / (24 * 60 * 60 * 1000)); - var hours = Math.floor((time % (24 * 60 * 60 * 1000)) / (60 * 60 * 1000)); - var minutes = Math.floor((time % (60 * 60 * 1000)) / (60 * 1000)); - var seconds = Math.floor((time % (60 * 1000)) / (1000)); - return [ - months && "".concat(months, "mo"), - days && "".concat(days, "d"), - hours && "".concat(hours, "h"), - minutes && "".concat(minutes, "m"), - (seconds || time < 1000) && "".concat(seconds, "s"), - ].filter(Boolean).join(" "); -} -//TODO move this data to be right next to Mode -function formatModeName(name) { - return { - "attack": "Attack", - "survival": "Survival", - "hexed": "Hexed", - "pvp": "PVP", - "sandbox": "Sandbox", - "hardcore": "Hardcore", - "testsrv": "Testing Server", - "minigame": "Minigames", - }[name]; -} -function formatTimestampFull(time) { - var date = new Date(time); - return "".concat(date.toDateString(), ", ").concat(date.toTimeString()); -} -function formatTimestamp(time) { - return new Date(time).toLocaleString(); -} -function formatTimestampShort(time) { - var date = new Date(time); - return "".concat(date.getFullYear(), "-").concat(date.getMonth() + 1, "-").concat(date.getDate(), " ").concat(date.getHours(), ":").concat(date.getMinutes()); -} -function formatTimeRelative(time, raw) { - var difference = Math.abs(time - Date.now()); - if (difference < 1000) - return "just now"; - else if (time > Date.now()) - return (raw ? "" : "in ") + formatTime(difference); - else - return formatTime(difference) + (raw ? "" : " ago"); -} -/** Attempts to parse a Color from the input. */ -function getColor(input) { - try { - if (input.includes(',')) { - var formattedColor = input.split(','); - var col = { - r: Number(formattedColor[0]), - g: Number(formattedColor[1]), - b: Number(formattedColor[2]), - a: 255, - }; - return new Color(col.r, col.g, col.b, col.a); - } - else if (input.includes('#')) { - return Color.valueOf(input); - } - else if ((function (input) { return input in Color; })(input)) { - return Color[input]; - } - else { - return null; - } - } - catch (e) { - return null; - } -} -/** Searches for an enemy tile near a unit. */ -function nearbyEnemyTile(unit, dist) { - //because the indexer is buggy - if (dist > 10) - (0, funcs_1.crash)("nearbyEnemyTile(): dist (".concat(dist, ") is too high!")); - var x = Math.floor(unit.x / Vars.tilesize); - var y = Math.floor(unit.y / Vars.tilesize); - for (var i = -dist; i <= dist; i++) { - for (var j = -dist; j <= dist; j++) { - var build = Vars.world.build(x + i, y + j); - if (build && build.team != unit.team && build.team != Team.derelict) - return build; - } - } - return null; -} -/** Attempts to parse a Team from the input. */ -exports.getTeam = (0, funcs_1.searchFixed)(Team.baseTeams.concat(Team.neoplastic), [ - function (i, s) { return i.name == s; }, - function (i, s) { return i.name == s.toLowerCase(); }, - function (i, s) { return i.emoji == s; }, - function (i, s) { return i.name.includes(s.toLowerCase()); }, - function (i, s) { return i.name.includes(s.toLowerCase().replace(" ", "-")); }, -]); -/** Attempts to parse an Item from the input. */ -exports.getItem = (0, funcs_1.searchFixed)(Vars.content.items().toArray(), [ - function (i, s) { return i.name == s; }, - function (i, s) { return i.name == s.toLowerCase(); }, - function (i, s) { return i.name.includes(s.toLowerCase()); }, - function (i, s) { return i.name.includes(s.toLowerCase().replace(" ", "-")); }, - function (i, s) { return i.emoji() == s; }, -]); -/** - * @param wordList "chat" is least strict, followed by "strict", and "name" is most strict. - * @returns a - */ -function matchFilter(input, wordList, aggressive) { - var e_1, _a, e_2, _b; - if (wordList === void 0) { wordList = "chat"; } - if (aggressive === void 0) { aggressive = false; } - var currentBannedWords = [ - wordList == "name" ? config_1.bannedWords.normal.filter(function (w) { return w[0] !== "uwu"; }) : config_1.bannedWords.normal, - (wordList == "strict" || wordList == "name") && config_1.bannedWords.strict, - wordList == "name" && config_1.bannedWords.names, - ].filter(Boolean).flat(); - if (aggressive) - currentBannedWords.push(["hitler", []]); - //Replace substitutions - var variations = [input, cleanText(input, false)]; - if (aggressive) - variations.push(cleanText(input, true)); - try { - for (var currentBannedWords_1 = __values(currentBannedWords), currentBannedWords_1_1 = currentBannedWords_1.next(); !currentBannedWords_1_1.done; currentBannedWords_1_1 = currentBannedWords_1.next()) { - var _c = __read(currentBannedWords_1_1.value, 2), banned = _c[0], whitelist = _c[1]; - var _loop_1 = function (text_1) { - if (banned instanceof RegExp ? banned.test(text_1) : text_1.includes(banned)) { - var modifiedText_1 = text_1; - whitelist.forEach(function (w) { return modifiedText_1 = modifiedText_1.replace(new RegExp(w, "g"), ""); }); //Replace whitelisted words with nothing - if (banned instanceof RegExp ? banned.test(modifiedText_1) : modifiedText_1.includes(banned)) //If the text still matches, fail - return { value: (banned === globals_1.uuidPattern ? "a Mindustry UUID" : - banned === globals_1.ipPattern || banned === globals_1.ipPortPattern ? "an IP address" : - //parsing regex with regex, massive hack - banned instanceof RegExp ? banned.source.replace(/\\b|\(\?|||>", "Name contains >|||> which is reserved for the server owner"], - "\uE817", "\uE82C", "\uE88E", "\uE813", - [/^[<\uE825].{1,3}[>\uE83A]/, "Name contains a prefix such as which is used for role prefixes"], - [function (replacedText) { return !isAdmin && config_1.adminNames.includes(replacedText.replace(/ /g, "")); }, "One of our admins uses this name"] - ]); - try { - for (var filters_1 = __values(filters), filters_1_1 = filters_1.next(); !filters_1_1.done; filters_1_1 = filters_1.next()) { - var _b = __read(filters_1_1.value, 2), check = _b[0], message = _b[1]; - if (check(replacedText)) - return message; - if (check(antiEvasionText)) - return message; - } - } - catch (e_3_1) { e_3 = { error: e_3_1 }; } - finally { - try { - if (filters_1_1 && !filters_1_1.done && (_a = filters_1.return)) _a.call(filters_1); - } - finally { if (e_3) throw e_3.error; } - } - return false; -} -function logAction(action, by, to, reason, duration) { - if (by === undefined) { //overload 1 - api.sendModerationMessage("".concat(action, "\n**Server:** ").concat(config_1.Gamemode.name())); - return; - } - if (to === undefined) { //overload 2 - api.sendModerationMessage("".concat((0, funcs_1.escapeTextDiscord)(Strings.stripColors(by.name)), " ").concat(action, "\n**Server:** ").concat(config_1.Gamemode.name())); - return; - } - if (to) { //overload 3 - var name = void 0, uuid = void 0, ip = void 0; - var actor = typeof by === "string" ? by : (0, funcs_1.escapeTextDiscord)(Strings.stripColors(by.name)); - if (to instanceof players_1.FishPlayer) { - name = (0, funcs_1.escapeTextDiscord)(to.name); - uuid = to.uuid; - ip = to.ip(); - } - else if (typeof to == "string") { - if (globals_1.uuidPattern.test(to)) { - name = "[".concat(to, "]"); - uuid = to; - ip = "[unknown]"; - } - else { - name = to; - uuid = "[unknown]"; - ip = "[unknown]"; - } - } - else { - name = (0, funcs_1.escapeTextDiscord)(to.lastName); - uuid = to.id; - ip = to.lastIP; - } - api.sendModerationMessage("".concat(actor, " ").concat(action, " ").concat(name, " ").concat(duration ? "for ".concat(formatTime(duration), " ") : "").concat(reason ? "with reason ".concat((0, funcs_1.escapeTextDiscord)(reason)) : "", "\n**Server:** ").concat(config_1.Gamemode.name(), "\n**uuid:** `").concat(uuid, "`\n**ip**: `").concat(ip, "`")); - return; - } -} -/** @returns the number of milliseconds. */ -function parseTimeString(str) { - var e_4, _a; - var formats = [ - [/(\d+)s/, 1], - [/(\d+)m/, 60], - [/(\d+)h/, 3600], - [/(\d+)d/, 86400], - [/(\d+)w/, 604800] - ].map(function (_a) { - var _b = __read(_a, 2), regex = _b[0], mult = _b[1]; - return [Pattern.compile(regex.source), mult]; - }); - if (str == "forever") - return (globals_1.maxTime - Date.now() - 10000); - try { - for (var formats_1 = __values(formats), formats_1_1 = formats_1.next(); !formats_1_1.done; formats_1_1 = formats_1.next()) { - var _b = __read(formats_1_1.value, 2), pattern = _b[0], mult = _b[1]; - //rhino regex doesn't work - var matcher = pattern.matcher(str); - if (matcher.matches()) { - var num = Number(matcher.group(1)); - if (!isNaN(num)) - return (num * mult) * 1000; - } - } - } - catch (e_4_1) { e_4 = { error: e_4_1 }; } - finally { - try { - if (formats_1_1 && !formats_1_1.done && (_a = formats_1.return)) _a.call(formats_1); - } - finally { if (e_4) throw e_4.error; } - } - return null; -} -/** - * Triggers the restart countdown. Execution always returns from this function. - * @param [fake=false] if set, server will not actually restart. - */ -function serverRestartLoop(sec, fake) { - if (fake === void 0) { fake = false; } - if (sec > 0) { - if (sec < 15 || sec % 5 == 0) - Call.sendMessage("[scarlet]Server restarting in: ".concat(sec)); - globals_1.fishState.restartLoopTask = Timer.schedule(function () { return serverRestartLoop(sec - 1); }, 1); - } - else if (!fake) { - restartNow(); - } -} -/** - * Actually restarts. Kicks all players. Execution always returns from this function. - * @param [removeSave=false] If set, save will be deleted instead of saved. Used to start a new game after the restart. - */ -function restartNow(removeSave) { - if (removeSave === void 0) { removeSave = false; } - Log.info("Restarting..."); - Vars.netServer.kickAll(Packets.KickReason.serverRestarting); - Vars.net.closeServer(); - Vars.state.set(GameState.State.menu); - var file = Vars.saveDirectory.child('1' + '.' + Vars.saveExtension); - if (removeSave) { - Core.app.post(function () { - file.delete(); - Core.app.exit(); - }); - } - else { - Core.app.post(function () { - SaveIO.save(file); - Core.app.exit(); - }); - } -} -function isBuildable(block) { - return block == Blocks.powerVoid || (block.buildType != Blocks.air.buildType && !(block instanceof ConstructBlock)); -} -exports.getUnitType = (0, funcs_1.searchFixed)(function () { return Vars.content.units().select(function (u) { return !(u instanceof MissileUnitType || u.internal); }).toArray(); }, [ - function (u, q) { return u.name == q; }, - function (u, q) { return u.name.includes(q.toLowerCase()); }, -]); -/** The vanilla validation code doesn't work on servers */ -function isMapValidForGamemode(map) { - if (map.custom) - return true; //we assume that all custom maps are appropriate for the selected gamemode - var pvpMaps = ["Veins", "Glacier", "Passage"]; //Maps.pvpMaps - switch (Vars.state.rules.mode().name()) { - case "sandbox": - case "editor": return true; //sandbox can be played on any map - case "attack": - case "pvp": return pvpMaps.includes(map.name()); //technically the pvp maps are valid attack maps, since they have an (undefended) enemy core - case "survival": return !pvpMaps.includes(map.name()); - default: return false; //unreachable - } -} -exports.getMap = (0, funcs_1.searchFixed)(function () { return Vars.maps.all().select(isMapValidForGamemode).toArray(); }, [ - function (m, name) { return m.name().replace(/ /g, "_") === name; }, //exact match with spaces replaced - function (m, name) { return m.name().replace(/ /g, "_").toLowerCase() === name.toLowerCase(); }, //exact match with spaces replaced ignoring case - function (m, name) { return m.plainName().replace(/ /g, "_").toLowerCase() === name.toLowerCase(); }, //exact match with spaces replaced ignoring case and colors - function (m, name) { return m.plainName().toLowerCase().includes(name.toLowerCase()); }, //partial match ignoring case and colors - function (m, name) { return m.plainName().replace(/ /g, "_").toLowerCase().includes(name.toLowerCase()); }, //partial match with spaces replaced ignoring case and colors - function (m, name) { return m.plainName().replace(/ /g, "").toLowerCase().includes(name.toLowerCase()); }, //partial match with spaces removed ignoring case and colors - function (m, name) { return m.plainName().replace(/[^a-zA-Z]/gi, "").toLowerCase().includes(name.toLowerCase()); }, -], "recomputeOptions"); -//static cache -var buildableBlocks = null; -function getBlock(block, filter) { - buildableBlocks !== null && buildableBlocks !== void 0 ? buildableBlocks : (buildableBlocks = Vars.content.blocks().select(isBuildable)); - var check = { - buildable: function (b) { return isBuildable(b); }, - air: function (b) { return b == Blocks.air || isBuildable(b); }, - all: function (b) { return true; } - }[filter]; - var out; - if (block in Blocks && Blocks[block] instanceof Block && check(Blocks[block])) - return Blocks[block]; - else if ((out = Vars.content.blocks().find(function (t) { return t.name.includes(block.toLowerCase()) && check(t); }))) - return out; - else if ((out = Vars.content.blocks().find(function (t) { return t.name.replace(/-/g, "").includes(block.toLowerCase().replace(/ /g, "")) && check(t); }))) - return out; - else if (block.includes("airblast")) - return Blocks.blastDrill; - return "\"".concat(block, "\" is not a valid block."); -} -function teleportPlayer(player, to) { - Timer.schedule(function () { - var p = player.unit(); - var t = to.unit(); - if (p && t) { - p.set(t.x, t.y); - Call.setPosition(player.con, t.x, t.y); - Call.setCameraPosition(player.con, t.x, t.y); - } - }, 0, 0.016, 10); -} -function logErrors(message, func) { - return function () { - var args = []; - for (var _i = 0; _i < arguments.length; _i++) { - args[_i] = arguments[_i]; - } - try { - return func.apply(void 0, __spreadArray([], __read(args), false)); - } - catch (err) { - Log.err(message); - Log.err((0, funcs_1.parseError)(err)); - } - }; -} -function definitelyRealMemoryCorruption() { - Log.info("Triggering a prank: this will cause players to see two error messages claiming to be from a memory corruption, and cause a flickering amount of fissile matter and dormant cysts to be put in the core."); - players_1.FishPlayer.messageStaff("[gray]<[cyan]staff[gray]> [white]Activating memory corruption prank! (please don't ruin it by telling players what is happening, pretend you dont know)"); - api.sendModerationMessage("Activated memory corruption prank on server ".concat(Vars.state.rules.mode().name())); - var t1f = false; - var t2f = false; - globals_1.fishState.corruption_t1 = Timer.schedule(function () { - var _a; - t1f = !t1f; - (_a = Vars.state.rules.defaultTeam.items()) === null || _a === void 0 ? void 0 : _a.set(Items.dormantCyst, t1f ? 69 : 420); - }, 0, 0.4, 600); - globals_1.fishState.corruption_t2 = Timer.schedule(function () { - var _a; - t2f = !t2f; - (_a = Vars.state.rules.defaultTeam.items()) === null || _a === void 0 ? void 0 : _a.set(Items.fissileMatter, t2f ? 999 : 123); - }, 0, 1.5, 200); - var hexString = Math.floor(Math.random() * 0xFFFFFFFF).toString(16).padStart(8, "0"); - Call.sendMessage("[scarlet]Error: internal server error."); - Call.sendMessage("[scarlet]Error: memory corruption: mindustry.world.modules.ItemModule@".concat(hexString)); - globals_1.FishEvents.fire("memoryCorruption", []); -} -function getEnemyTeam() { - if (config_1.Gamemode.pvp()) - return Team.derelict; - else - return Vars.state.rules.waveTeam; -} -function neutralGameover() { - players_1.FishPlayer.ignoreGameover(function () { - Events.fire(new EventType.GameOverEvent(getEnemyTeam())); - }); -} -/** Please validate requestedWaves to ensure it is not huge */ -function skipWaves(requestedWaves, runIntermediateWaves) { - //Check if the current map has opted out of multi-wave skipping - if (Vars.state.rules.tags.get(config_1.noMultiVnwTag) == "true") { - requestedWaves = Math.min(requestedWaves, 1); - } - var winWave = Vars.state.rules.winWave; - if (winWave <= 0) - winWave = Infinity; - var wavesToSkip = Math.min(requestedWaves, winWave - Vars.state.wave); - if (runIntermediateWaves) { - for (var i = 0; i < wavesToSkip; i++) { - Vars.logic.skipWave(); - } - } - else { - Vars.state.wave += (wavesToSkip - 1); - Vars.logic.skipWave(); - } -} -function logHTrip(player, name, message) { - Log.warn("&yPlayer &b\"".concat(player.cleanedName, "\"&y (&b").concat(player.uuid, "&y/&b").concat(player.ip(), "&y) tripped &c").concat(name, "&y") + (message ? ": ".concat(message) : "")); - players_1.FishPlayer.messageStaff("[yellow]Player [blue]\"".concat(player.cleanedName, "\"[] tripped [cyan]").concat(name, "[]") + (message ? ": ".concat(message) : "")); - api.sendModerationMessage("Player `".concat(player.cleanedName, "` (`").concat(player.uuid, "`/`").concat(player.ip(), "`) tripped **").concat(name, "**").concat(message ? ": ".concat(message) : "", "\n**Server:** ").concat(config_1.Gamemode.name())); -} -function setType(input) { - //does not do any checking -} -function untilForever() { - return (globals_1.maxTime - Date.now() - 10000); -} -function colorNumber(number, getColor, side) { - if (side === void 0) { side = "client"; } - return getColor(number) + number.toString() + (side == "client" ? "[]" : "&fr"); -} -function formatRatekeeper(x) { - if (x.lastTime <= 1) - return "0"; - return "".concat(x.occurences, " / ").concat(formatTimeRelative(x.lastTime, true)); -} -function getAntiBotInfo(side) { - var color = side == "client" ? "[acid]" : "&ly"; - var True = side == "client" ? "[red]true[]" : "&lrtrue"; - var False = side == "client" ? "[green]false[]" : "&gfalse"; - return ("".concat(color, "Flag count: ").concat(formatRatekeeper(players_1.FishPlayer.autoflagRate), "\n").concat(color, "Autobanning flagged players: ").concat(players_1.FishPlayer.shouldWhackFlaggedPlayers() ? True : False, "\n").concat(color, "Kicking new players: ").concat(players_1.FishPlayer.shouldKickNewPlayers() ? True : False, "\n").concat(color, "Recent connect packets: ").concat(formatRatekeeper(players_1.FishPlayer.connectRate), "\n").concat(color, "Reason: ").concat(players_1.FishPlayer.lastAntibotReason)); -} -var failPrefix = "[scarlet]\u26A0 [yellow]"; -var successPrefix = "[#48e076]\uE800 "; -function outputFail(message, sender, ratelimit) { - var msg = failPrefix + (typeof message == "function" && "__partialFormatString" in message ? message("[yellow]") : message); - if (ratelimit) - sender.sendMessage(msg, ratelimit); - else - sender.sendMessage(msg); -} -function outputSuccess(message, sender) { - sender.sendMessage(successPrefix + (typeof message == "function" && "__partialFormatString" in message ? message("[#48e076]") : message)); -} -function outputMessage(message, sender) { - sender.sendMessage(((typeof message == "function" && "__partialFormatString" in message ? message(null) : message) + "").replace(/\t/g, " ".repeat(4))); -} -function outputConsole(message, channel) { - if (channel === void 0) { channel = Log.info; } - channel(typeof message == "function" && "__partialFormatString" in message ? message("") : message); -} -function updateBans(message) { - Groups.player.each(function (player) { - if (Vars.netServer.admins.isIDBanned(player.uuid())) { - player.con.kick(Packets.KickReason.banned); - if (message) - Call.sendMessage(message(player)); - } - }); -} -function processChat(player, message, effects) { - if (effects === void 0) { effects = false; } - var fishPlayer = players_1.FishPlayer.get(player); - var highlight = fishPlayer.highlight; - var filterTripText; - var suspicious = fishPlayer.suspicionLevel() == 3; - if ((!fishPlayer.hasPerm("bypassChatFilter") || fishPlayer.chatStrictness == "strict") - && (filterTripText = matchFilter(message, fishPlayer.chatStrictness, suspicious))) { - if (effects) { - if (suspicious && removeFoosChars(message).split(" ") - .map(function (w) { return w.replace(/[-_.^*,]/g, ""); }) - .some(function (w) { return config_1.bannedWords.autoWhack.includes(w); })) { - if (!fishPlayer.muted) { - logHTrip(fishPlayer, "bad words in chat", "message: `".concat(message, "`")); - fishPlayer.muted = true; - void fishPlayer.stop("automod", globals_1.maxTime, "Automatic stop due to suspicious activity", false); - } - } - Log.info("Censored message from player ".concat(player.name, ": \"").concat((0, funcs_1.escapeStringColorsServer)(message), "\"; contained \"").concat(filterTripText, "\"")); - players_1.FishPlayer.messageStaff("[yellow]Censored message from player ".concat(fishPlayer.cleanedName, ": \"").concat(message, "\" contained \"").concat(filterTripText, "\"")); - } - message = config_1.text.chatFilterReplacement.message(); - highlight !== null && highlight !== void 0 ? highlight : (highlight = config_1.text.chatFilterReplacement.highlight()); - } - if (message.startsWith("./")) - message = message.replace("./", "/"); - if (!fishPlayer.hasPerm("chat")) { - if (effects) { - players_1.FishPlayer.messageMuted(player.name, message); - Log.info("".concat(player.name, ": ").concat(message)); - } - return null; - } - return (highlight !== null && highlight !== void 0 ? highlight : "") + message; -} -var replacements = [ - //Serpulo units - ["dagger", "mace", "fortress", "scepter", "reign", "nova", "pulsar", "quasar", "vela", "corvus", "crawler", "atrax", "spiroct", "arkyid", "toxopid", "flare", "horizon", "zenith", "antumbra", "eclipse", "mono", "poly", "mega", "quad", "oct", "risso", "minke", "bryde", "sei", "omura", "retusa", "oxynoe", "cyerce", "aegires", "navanax", "fort", "toxo", "flarogus"], - //Erekir units - ["stell", "locus", "precept", "vanquish", "conquer", "merui", "cleroi", "anthicus", "tecta", "collaris", "elude", "avert", "obviate", "quell", "disrupt", "vanq", "crab", "anthi", "larry", "obvi"], - //Items, full form - ["copper", "lead", "metaglass", "graphite", "sand", "coal", "titanium", "thorium", "scrap", "silicon", "plastanium", "phase fabric", "surge alloy", "spore pod", "blast compound", "pyratite", "beryllium", "tungsten", "oxide", "carbide"], - //Items, short form - ["coppa", "meta", "graph", "tita", "titan", "thor", "scrap", "sili", "plast", "phase", "surge", "spore", "blast", "pyra", "beryl", "tung", "oxide", "carb"], - //Liquids - ["water", "slag", "oil", "cryo", "cryofluid"], - //Liquids/gases (erekir) - ["hydrogen", "ozone", "nitrogen", "cyanogen", "cyan", "nitro", "hydro", "arky", "arkycite", "neoplasm"], - //Gamemodes - ["attack", "sandbox", "pvp", "hexed", "survival"], - //teams - ["crux", "sharded", "malis", "neoplastic"], - //maps - ["rampant", "harbor war", "cave canal", "acheron", "wolframfestung", "avast", "fallen omura", "assault"], - //aquatic animals - ["fish", "shark", "whale", "dolphin", "salmon", "tuna", "squid", "jellyfish", "turtle"], - //antonym adjectives - ["fast", "slow"], ["big", "little"], ["hot", "cold"], ["hard", "easy", "difficult", "ez"], ["hello", "bye"], -].map(function (set) { return [set, new RegExp("\\b(?:".concat(set.join("|"), ")(e?s?(?:i?gone)?)\\b"), 'g')]; }); -var foolCounter = 0; -exports.foolifyChat = memoizeChatFilter(function foolifyChat(message) { - var e_5, _a; - var cleanedMessage = removeFoosChars(message); - setShuffle: { - if (foolCounter < 8) { - //Skip the next 5 messages no matter what - foolCounter++; - break setShuffle; - } - var replacedMessage = cleanedMessage; - var _loop_2 = function (set, regex) { - replacedMessage = replacedMessage.replace(regex, function (_, plural) { return (0, funcs_1.random)(set) + plural; }); - }; - try { - for (var replacements_1 = __values(replacements), replacements_1_1 = replacements_1.next(); !replacements_1_1.done; replacements_1_1 = replacements_1.next()) { - var _b = __read(replacements_1_1.value, 2), set = _b[0], regex = _b[1]; - _loop_2(set, regex); - } - } - catch (e_5_1) { e_5 = { error: e_5_1 }; } - finally { - try { - if (replacements_1_1 && !replacements_1_1.done && (_a = replacements_1.return)) _a.call(replacements_1); - } - finally { if (e_5) throw e_5.error; } - } - if (replacedMessage !== cleanedMessage) { - if (foolCounter < 11) { - //Skip the next 2 messages that would get altered - foolCounter++; - break setShuffle; - } - foolCounter = 0; - return replacedMessage; - } - else { - break setShuffle; - } - } - if (Math.random() < 0.01) { - return cleanedMessage.split("").reverse().join(""); - // eslint-disable-next-line no-dupe-else-if - } - else if (Math.random() < 0.01) { - return "[scarlet]I really hope everyone is having a fun time :} <3"; - } - else if (Math.random() < 0.005) { - return "[cyan]AMOGUS"; - } - else { - return message; - } -}); -exports.addToTileHistory = logErrors("Error while saving a tilelog entry", function (e) { - var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q, _r, _s, _t, _u, _v, _w, _x, _y, _z, _0, _1, _2, _3; - // eslint-disable-next-line prefer-const - var tile, uuid, action, type, time = Date.now(); - if (e instanceof EventType.BlockBuildBeginEvent) { - tile = e.tile; - uuid = (_e = (_c = (_b = (_a = e.unit) === null || _a === void 0 ? void 0 : _a.player) === null || _b === void 0 ? void 0 : _b.uuid()) !== null && _c !== void 0 ? _c : (_d = e.unit) === null || _d === void 0 ? void 0 : _d.type.name) !== null && _e !== void 0 ? _e : "unknown"; - if (e.breaking) { - action = "broke"; - type = (e.tile.build instanceof ConstructBlock.ConstructBuild) ? e.tile.build.previous.name : "unknown"; - if (((_g = (_f = e.unit) === null || _f === void 0 ? void 0 : _f.player) === null || _g === void 0 ? void 0 : _g.uuid()) && ((_h = e.tile.build) === null || _h === void 0 ? void 0 : _h.team) != Team.derelict) { - var fishP = players_1.FishPlayer.get(e.unit.player); - //TODO move this code - fishP.tstats.blocksBroken++; - fishP.tstats.blockInteractionsThisMap++; - fishP.updateStats(function (stats) { return stats.blocksBroken++; }); - } - } - else { - action = "built"; - type = (e.tile.build instanceof ConstructBlock.ConstructBuild) ? e.tile.build.current.name : "unknown"; - if ((_k = (_j = e.unit) === null || _j === void 0 ? void 0 : _j.player) === null || _k === void 0 ? void 0 : _k.uuid()) { - var fishP = players_1.FishPlayer.get(e.unit.player); - //TODO move this code - fishP.updateStats(function (stats) { return stats.blocksPlaced++; }); - fishP.tstats.blockInteractionsThisMap++; - } - } - } - else if (e instanceof EventType.ConfigEvent) { - tile = e.tile.tile; - uuid = (_m = (_l = e.player) === null || _l === void 0 ? void 0 : _l.uuid()) !== null && _m !== void 0 ? _m : "unknown"; - if (uuid != "unknown") { - var fishP = players_1.FishPlayer.getById(uuid); - if (fishP) - fishP.tstats.blockInteractionsThisMap++; - } - action = "configured"; - type = e.tile.block.name; - } - else if (e instanceof EventType.BuildRotateEvent) { - tile = e.build.tile; - uuid = (_s = (_q = (_p = (_o = e.unit) === null || _o === void 0 ? void 0 : _o.player) === null || _p === void 0 ? void 0 : _p.uuid()) !== null && _q !== void 0 ? _q : (_r = e.unit) === null || _r === void 0 ? void 0 : _r.type.name) !== null && _s !== void 0 ? _s : "unknown"; - if (uuid != "unknown") { - var fishP = players_1.FishPlayer.getById(uuid); - if (fishP) - fishP.tstats.blockInteractionsThisMap++; - } - action = "rotated"; - type = e.build.block.name; - } - else if (e instanceof EventType.UnitDestroyEvent) { - tile = e.unit.tileOn(); - if (!tile) - return; - if (!e.unit.type.playerControllable) - return; - uuid = e.unit.isPlayer() ? e.unit.getPlayer().uuid() : (_t = e.unit.lastCommanded) !== null && _t !== void 0 ? _t : "unknown"; - action = "killed"; - type = e.unit.type.name; - } - else if (e instanceof EventType.BlockDestroyEvent) { - if (config_1.Gamemode.attack() && ((_u = e.tile.build) === null || _u === void 0 ? void 0 : _u.team) != Vars.state.rules.defaultTeam) - return; //Don't log destruction of enemy blocks - tile = e.tile; - uuid = "[[something]"; - action = "killed"; - type = (_w = (_v = e.tile.block()) === null || _v === void 0 ? void 0 : _v.name) !== null && _w !== void 0 ? _w : "air"; - } - else if (e instanceof EventType.PayloadDropEvent) { - action = "pay-dropped"; - var controller = e.carrier.controller(); - uuid = (_z = (_y = (_x = e.carrier.player) === null || _x === void 0 ? void 0 : _x.uuid()) !== null && _y !== void 0 ? _y : (controller instanceof LogicAI && controller.controller ? - "".concat(e.carrier.type.name, " controlled by ").concat(controller.controller.block.name, " at ").concat(controller.controller.tileX(), ",").concat(controller.controller.tileY(), " last accessed by ").concat(e.carrier.getControllerName()) - : null)) !== null && _z !== void 0 ? _z : e.carrier.type.name; - if (e.build) { - tile = e.build.tile; - type = e.build.block.name; - } - else if (e.unit) { - tile = e.unit.tileOn(); - if (!tile) - return; - type = e.unit.type.name; - } - else - return; - } - else if (e instanceof EventType.PickupEvent) { - action = "picked up"; - if (e.carrier.isPlayer()) - return; //This event would have been handled by actionfilter - var controller = e.carrier.controller(); - if (!(controller instanceof LogicAI && controller.controller != null)) - return; - uuid = "".concat(e.carrier.type.name, " controlled by ").concat(controller.controller.block.name, " at ").concat(controller.controller.tileX(), ",").concat(controller.controller.tileY(), " last accessed by ").concat(e.carrier.getControllerName()); - if (e.build) { - tile = e.build.tile; - type = e.build.block.name; - } - else if (e.unit) { - tile = e.unit.tileOn(); - if (!tile) - return; - type = e.unit.type.name; - } - else - return; - } - else if (e instanceof EventType.UnitControlEvent) { - if (e.unit instanceof Packages.mindustry.gen.BlockUnitUnit) { - action = "controlled"; - tile = (_0 = e.unit) === null || _0 === void 0 ? void 0 : _0.tile().tile; - if (!tile) - return; - type = (_2 = (_1 = tile.block()) === null || _1 === void 0 ? void 0 : _1.name) !== null && _2 !== void 0 ? _2 : "air"; - uuid = e.player.uuid(); - } - else - return; - } - else if (e instanceof Object && "pos" in e && "uuid" in e && "action" in e && "type" in e) { - var pos = void 0; - (pos = e.pos, uuid = e.uuid, action = e.action, type = e.type); - tile = (_3 = Vars.world.tile(pos.split(",")[0], pos.split(",")[1])) !== null && _3 !== void 0 ? _3 : (0, funcs_1.crash)("Cannot log ".concat(action, " at ").concat(pos, ": Nonexistent tile")); - } - else - return; - if (tile == null) - return; - [tile, uuid, action, type, time]; - tile.getLinkedTiles(function (t) { - var pos = "".concat(t.x, ",").concat(t.y); - var existingData = globals_1.tileHistory[pos] ? funcs_1.StringIO.read(globals_1.tileHistory[pos], function (str) { return str.readArray(function (d) { return ({ - action: d.readString(2), - uuid: d.readString(3), - time: d.readNumber(16), - type: d.readString(2), - }); }, 1); }) : []; - existingData.push({ - action: action, - uuid: uuid, - time: time, - type: type - }); - existingData = existingData.slice(-9); - //Write - globals_1.tileHistory[t.x + ',' + t.y] = funcs_1.StringIO.write(existingData, function (str, data) { return str.writeArray(data, function (el) { - str.writeString(el.action, 2); - str.writeString(el.uuid, 3); - str.writeNumber(el.time, 16); - str.writeString(el.type, 2); - }, 1); }); - }); -}); -function getIPRange(input, error) { - if (globals_1.ipRangeCIDRPattern.test(input)) { - var _a = __read(input.split("/"), 2), ip = _a[0], maskLength = _a[1]; - switch (maskLength) { - case "24": - return ip.split(".").slice(0, 3).join(".") + "."; - case "16": - return ip.split(".").slice(0, 2).join(".") + "."; - default: - error === null || error === void 0 ? void 0 : error("Mindustry does not currently support netmasks other than /16 and /24"); - return null; - } - } - else if (globals_1.ipRangeWildcardPattern.test(input)) { - //1.2.3.* - //1.2.* - var _b = __read(input.split("."), 4), a = _b[0], b = _b[1], c = _b[2], d = _b[3]; - if (c !== "*") - return "".concat(a, ".").concat(b, ".").concat(c, "."); - return "".concat(a, ".").concat(b, "."); - } - else - return null; -} -//this brings me physical pain -function getHash(file, algorithm) { - if (algorithm === void 0) { algorithm = "SHA-1"; } - try { - var header = "blob ".concat(file.length(), "\0"); - var fileSHAHeader = Packages.java.nio.charset.StandardCharsets.UTF_8.encode(header); - var contents = file.readBytes(); - var buffer = Packages.java.nio.ByteBuffer.allocate(fileSHAHeader.remaining() + contents.length); - buffer.put(fileSHAHeader); - buffer.put(contents); - buffer.flip(); - var digest = Packages.java.security.MessageDigest.getInstance(algorithm); - digest.update(buffer); - return digest.digest().map(function (byte) { - return (byte & 0xFF).toString(16).padStart(2, "0"); - }).join(""); - } - catch (e) { - Log.err("Cannot generate ".concat(algorithm, ", ").concat(String(e))); - return undefined; - } -} -function match(value, clauses, defaultValue) { - return Object.prototype.hasOwnProperty.call(clauses, value) ? clauses[value] : defaultValue; -} -/** @throws CommandError */ -function fishCommandsRootDirPath() { - var commandsDir = Vars.modDirectory.child("fish-commands"); - if (!commandsDir.exists()) - (0, commands_1.fail)("Fish commands directory at path ".concat(commandsDir.absolutePath(), " does not exist!")); - var fishCommandsRootDirPath = Paths.get(commandsDir.file().path); - if (Packages.java.nio.file.Files.isSymbolicLink(fishCommandsRootDirPath)) { - //fish-commands is linked to the build directory of somewhere else - //resolve and get the parent directory of the build directory - fishCommandsRootDirPath = fishCommandsRootDirPath.toRealPath().getParent(); - } - return fishCommandsRootDirPath; -} -/** Fails if "mode" is invalid. */ -function applyEffectMode(mode, unit, ticks) { - var e_6, _a; - var _b; - var modes = { - fast: [StatusEffects.fast], - fast2: [StatusEffects.fast, StatusEffects.overdrive, StatusEffects.overclock], - boss: [StatusEffects.boss], - health: [StatusEffects.boss, StatusEffects.shielded], - slow: [StatusEffects.slow], - slow2: [ - StatusEffects.slow, - StatusEffects.freezing, - StatusEffects.wet, - StatusEffects.muddy, - StatusEffects.sapped, - StatusEffects.sporeSlowed, - StatusEffects.electrified, - StatusEffects.tarred, - ], - freeze: [StatusEffects.unmoving], - disarm: [StatusEffects.disarmed], - invincible: [StatusEffects.invincible], - boost: [ - StatusEffects.fast, - StatusEffects.overdrive, - StatusEffects.overclock, - StatusEffects.boss, - StatusEffects.shielded, - ], - damage: [ - StatusEffects.burning, - StatusEffects.freezing, - StatusEffects.wet, - StatusEffects.muddy, - StatusEffects.melting, - StatusEffects.sapped, - StatusEffects.tarred, - StatusEffects.shocked, - StatusEffects.blasted, - StatusEffects.corroded, - StatusEffects.sporeSlowed, - StatusEffects.electrified, - StatusEffects.fast, - ], - clear: function (unit) { - unit.clearStatuses(); - unit.maxHealth = unit.type.health; - }, - paper: function (unit) { - unit.health = 1; - unit.maxHealth = 1; - unit.apply(StatusEffects.disarmed, Number.MAX_VALUE / 2); - }, - heal: function (unit) { - unit.health = unit.maxHealth; - }, - overheal: function (unit) { - unit.maxHealth = unit.health = 1e15; - }, - shield: function (unit) { - unit.shield = 1e15; - } - }; - var effects = (_b = match(mode, modes, null)) !== null && _b !== void 0 ? _b : (0, commands_1.fail)("Invalid mode. Supported modes: ".concat(Object.keys(modes).join(", "))); - if (typeof effects === "function") { - effects(unit); - } - else { - try { - for (var effects_1 = __values(effects), effects_1_1 = effects_1.next(); !effects_1_1.done; effects_1_1 = effects_1.next()) { - var effect = effects_1_1.value; - unit.apply(effect, ticks); - } - } - catch (e_6_1) { e_6 = { error: e_6_1 }; } - finally { - try { - if (effects_1_1 && !effects_1_1.done && (_a = effects_1.return)) _a.call(effects_1); - } - finally { if (e_6) throw e_6.error; } - } - } -} -function handleError(err, sender, outputFail, context) { - if (err instanceof commands_1.CommandError) { - //If the error is a command error, then just outputFail - outputFail(err.data, sender); - } - else if (err === menus_1.Cancel) { - //Menu cancelled, do nothing - return; - } - else { - sender.sendMessage("[scarlet]\u274C An error occurred while executing the command!"); - if (sender.hasPerm("seeErrorMessages")) - sender.sendMessage((0, funcs_1.parseError)(err)); - Log.err(context ? - "Unhandled error in command execution: ".concat(context) - : "Unhandled error in command execution."); - Log.err(err); - if (typeof err == "object" && err != null && "stack" in err) - Log.err(err.stack); - } -} -function syncManual(player, rules, emptyMap) { - if (rules === void 0) { rules = Vars.state.rules; } - return new Promise(function (resolve) { - Threads.daemon(function () { - Call.worldDataBegin(player.con); - var os = new ByteArrayOutputStream(); - var stream = new DataOutputStream(new FastDeflaterOutputStream(os)); - stream.writeUTF(JsonIO.write(rules)); - stream.writeUTF(JsonIO.write(Vars.state.mapLocales)); - SaveIO.getSaveWriter().writeStringMap(stream, Vars.state.map.tags); - stream.writeInt(Vars.state.wave); - stream.writeFloat(Vars.state.wavetime); - stream.writeDouble(Vars.state.tick); - stream.writeLong(GlobalVars.rand.seed0); - stream.writeLong(GlobalVars.rand.seed1); - stream.writeInt(player.id); - player.write(new Writes(stream)); - SaveIO.getSaveWriter().writeContentHeader(stream); - SaveIO.getSaveWriter().writeContentPatches(stream); - if (emptyMap) { - //fake world, all the same tile - stream.writeShort(emptyMap.width); - stream.writeShort(emptyMap.height); - var area = emptyMap.width * emptyMap.height; - for (var i = 0; i < area;) { - stream.writeShort(emptyMap.floor.id); - stream.writeShort(emptyMap.overlay.id); - var needed = area - i - 1; - if (needed > 255) { - stream.writeByte(255); - i += 256; - } - else { - stream.writeByte(needed); - break; - } - } - for (var i = 0; i < area;) { - stream.writeShort(emptyMap.build.id); - stream.writeByte(0); - var needed = area - i - 1; - if (needed > 255) { - stream.writeByte(255); - i += 256; - } - else { - stream.writeByte(needed); - break; - } - } - } - else - SaveIO.getSaveWriter().writeMap(stream); - SaveIO.getSaveWriter().writeTeamBlocks(stream); - SaveIO.getSaveWriter().writeMarkers(stream); - SaveIO.getSaveWriter().writeCustomChunks(stream, true); - stream.close(); - var data = Object.assign(new Packets.WorldStream(), { - stream: new ByteArrayInputStream(os.toByteArray()) - }); - player.con.sendStream(data); - resolve(); - }); - }); -} -function crashClient(player) { - var planetBackground = Object.assign(new Packages.mindustry.graphics.g3d.PlanetParams(), { planet: null }); - if (Vars.state.rules.planetBackground) { - //There are already planet params, need to force sync - var rules = Object.assign(Vars.state.rules.copy(), { planetBackground: planetBackground }); - void syncManual(player, rules, { - width: 1, - height: 1, - floor: Blocks.space, - build: Blocks.air, - overlay: Blocks.air, - }); - return false; - } - else { - Call.setRule(player.con, "planetBackground", JsonIO.write(planetBackground)); - return true; - } -} -var sources = [ - Packages.mindustry.gen.UnitEntity, - Packages.mindustry.gen.MechUnit, - Packages.mindustry.gen.LegsUnit, - Packages.mindustry.gen.CrawlUnit, - Packages.mindustry.gen.UnitWaterMove, - Packages.mindustry.gen.BlockUnitUnit, - Packages.mindustry.gen.ElevationMoveUnit, - Packages.mindustry.gen.BuildingTetherPayloadUnit, - Packages.mindustry.gen.TimedKillUnit, - Packages.mindustry.gen.PayloadUnit, - Packages.mindustry.gen.TankUnit, -]; -function getStatuses(unit) { - var e_7, _a; - try { - for (var sources_1 = __values(sources), sources_1_1 = sources_1.next(); !sources_1_1.done; sources_1_1 = sources_1.next()) { - var clazz = sources_1_1.value; - if (unit instanceof clazz) - return ArcReflect.get(clazz, unit, "statuses"); - } - } - catch (e_7_1) { e_7 = { error: e_7_1 }; } - finally { - try { - if (sources_1_1 && !sources_1_1.done && (_a = sources_1.return)) _a.call(sources_1); - } - finally { if (e_7) throw e_7.error; } - } - return new Seq(); -} +"use strict"; +/* +Copyright © BalaM314, 2026. All Rights Reserved. +This file contains many utility functions that need access to any values from other files. +For functions that don't need values from other files, see funcs.ts. +*/ +var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + var desc = Object.getOwnPropertyDescriptor(m, k); + if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { + desc = { enumerable: true, get: function() { return m[k]; } }; + } + Object.defineProperty(o, k2, desc); +}) : (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + o[k2] = m[k]; +})); +var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { + Object.defineProperty(o, "default", { enumerable: true, value: v }); +}) : function(o, v) { + o["default"] = v; +}); +var __importStar = (this && this.__importStar) || (function () { + var ownKeys = function(o) { + ownKeys = Object.getOwnPropertyNames || function (o) { + var ar = []; + for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k; + return ar; + }; + return ownKeys(o); + }; + return function (mod) { + if (mod && mod.__esModule) return mod; + var result = {}; + if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]); + __setModuleDefault(result, mod); + return result; + }; +})(); +var __values = (this && this.__values) || function(o) { + var s = typeof Symbol === "function" && Symbol.iterator, m = s && o[s], i = 0; + if (m) return m.call(o); + if (o && typeof o.length === "number") return { + next: function () { + if (o && i >= o.length) o = void 0; + return { value: o && o[i++], done: !o }; + } + }; + throw new TypeError(s ? "Object is not iterable." : "Symbol.iterator is not defined."); +}; +var __read = (this && this.__read) || function (o, n) { + var m = typeof Symbol === "function" && o[Symbol.iterator]; + if (!m) return o; + var i = m.call(o), r, ar = [], e; + try { + while ((n === void 0 || n-- > 0) && !(r = i.next()).done) ar.push(r.value); + } + catch (error) { e = { error: error }; } + finally { + try { + if (r && !r.done && (m = i["return"])) m.call(i); + } + finally { if (e) throw e.error; } + } + return ar; +}; +var __spreadArray = (this && this.__spreadArray) || function (to, from, pack) { + if (pack || arguments.length === 2) for (var i = 0, l = from.length, ar; i < l; i++) { + if (ar || !(i in from)) { + if (!ar) ar = Array.prototype.slice.call(from, 0, i); + ar[i] = from[i]; + } + } + return to.concat(ar || Array.prototype.slice.call(from)); +}; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.addToTileHistory = exports.foolifyChat = exports.getMap = exports.getUnitType = exports.getItem = exports.getTeam = void 0; +exports.memoizeChatFilter = memoizeChatFilter; +exports.formatTime = formatTime; +exports.formatTimeShort = formatTimeShort; +exports.formatModeName = formatModeName; +exports.formatTimestampFull = formatTimestampFull; +exports.formatTimestamp = formatTimestamp; +exports.formatTimestampShort = formatTimestampShort; +exports.formatTimeRelative = formatTimeRelative; +exports.getColor = getColor; +exports.nearbyEnemyTile = nearbyEnemyTile; +exports.matchFilter = matchFilter; +exports.removeFoosChars = removeFoosChars; +exports.cleanText = cleanText; +exports.isImpersonator = isImpersonator; +exports.logAction = logAction; +exports.parseTimeString = parseTimeString; +exports.serverRestartLoop = serverRestartLoop; +exports.restartNow = restartNow; +exports.isBuildable = isBuildable; +exports.isMapValidForGamemode = isMapValidForGamemode; +exports.getBlock = getBlock; +exports.teleportPlayer = teleportPlayer; +exports.logErrors = logErrors; +exports.definitelyRealMemoryCorruption = definitelyRealMemoryCorruption; +exports.getEnemyTeam = getEnemyTeam; +exports.neutralGameover = neutralGameover; +exports.skipWaves = skipWaves; +exports.logHTrip = logHTrip; +exports.setType = setType; +exports.untilForever = untilForever; +exports.colorNumber = colorNumber; +exports.formatRatekeeper = formatRatekeeper; +exports.getAntiBotInfo = getAntiBotInfo; +exports.outputFail = outputFail; +exports.outputSuccess = outputSuccess; +exports.outputMessage = outputMessage; +exports.outputConsole = outputConsole; +exports.updateBans = updateBans; +exports.processChat = processChat; +exports.getIPRange = getIPRange; +exports.getHash = getHash; +exports.match = match; +exports.fishCommandsRootDirPath = fishCommandsRootDirPath; +exports.applyEffectMode = applyEffectMode; +exports.handleError = handleError; +exports.syncManual = syncManual; +exports.crashClient = crashClient; +exports.getStatuses = getStatuses; +var api = __importStar(require("/api")); +var config_1 = require("/config"); +var commands_1 = require("/frameworks/commands"); +var menus_1 = require("/frameworks/menus"); +var funcs_1 = require("/funcs"); +var globals_1 = require("/globals"); +var players_1 = require("/players"); +function memoizeChatFilter(impl) { + var lastCleanedInput = null; + var lastOutput = null; + return function memoized(input) { + var cleanedInput = removeFoosChars(input); + if (cleanedInput === lastCleanedInput) + return lastOutput; + lastCleanedInput = cleanedInput; + return lastOutput = impl(input); + }; +} +function formatTime(time) { + if (globals_1.maxTime - (time + Date.now()) < 20000) + return "forever"; + if (isNaN(time)) + return "N/A"; + var months = Math.floor(time / (30 * 24 * 60 * 60 * 1000)); + var days = Math.floor((time % (30 * 24 * 60 * 60 * 1000)) / (24 * 60 * 60 * 1000)); + var hours = Math.floor((time % (24 * 60 * 60 * 1000)) / (60 * 60 * 1000)); + var minutes = Math.floor((time % (60 * 60 * 1000)) / (60 * 1000)); + var seconds = Math.floor((time % (60 * 1000)) / (1000)); + return [ + months && "".concat(months, " month").concat(months != 1 ? "s" : ""), + days && "".concat(days, " day").concat(days != 1 ? "s" : ""), + hours && "".concat(hours, " hour").concat(hours != 1 ? "s" : ""), + minutes && "".concat(minutes, " minute").concat(minutes != 1 ? "s" : ""), + (seconds || time < 1000) && "".concat(seconds, " second").concat(seconds != 1 ? "s" : ""), + ].filter(Boolean).join(", "); +} +function formatTimeShort(time) { + if (globals_1.maxTime - (time + Date.now()) < 20000) + return "forever"; + if (isNaN(time)) + return "N/A"; + var months = Math.floor(time / (30 * 24 * 60 * 60 * 1000)); + var days = Math.floor((time % (30 * 24 * 60 * 60 * 1000)) / (24 * 60 * 60 * 1000)); + var hours = Math.floor((time % (24 * 60 * 60 * 1000)) / (60 * 60 * 1000)); + var minutes = Math.floor((time % (60 * 60 * 1000)) / (60 * 1000)); + var seconds = Math.floor((time % (60 * 1000)) / (1000)); + return [ + months && "".concat(months, "mo"), + days && "".concat(days, "d"), + hours && "".concat(hours, "h"), + minutes && "".concat(minutes, "m"), + (seconds || time < 1000) && "".concat(seconds, "s"), + ].filter(Boolean).join(" "); +} +//TODO move this data to be right next to Mode +function formatModeName(name) { + return { + "attack": "Attack", + "survival": "Survival", + "hexed": "Hexed", + "pvp": "PVP", + "sandbox": "Sandbox", + "hardcore": "Hardcore", + "testsrv": "Testing Server", + "minigame": "Minigames", + }[name]; +} +function formatTimestampFull(time) { + var date = new Date(time); + return "".concat(date.toDateString(), ", ").concat(date.toTimeString()); +} +function formatTimestamp(time) { + return new Date(time).toLocaleString(); +} +function formatTimestampShort(time) { + var date = new Date(time); + return "".concat(date.getFullYear(), "-").concat(date.getMonth() + 1, "-").concat(date.getDate(), " ").concat(date.getHours(), ":").concat(date.getMinutes()); +} +function formatTimeRelative(time, raw) { + var difference = Math.abs(time - Date.now()); + if (difference < 1000) + return "just now"; + else if (time > Date.now()) + return (raw ? "" : "in ") + formatTime(difference); + else + return formatTime(difference) + (raw ? "" : " ago"); +} +/** Attempts to parse a Color from the input. */ +function getColor(input) { + try { + if (input.includes(',')) { + var formattedColor = input.split(','); + var col = { + r: Number(formattedColor[0]), + g: Number(formattedColor[1]), + b: Number(formattedColor[2]), + a: 255, + }; + return new Color(col.r, col.g, col.b, col.a); + } + else if (input.includes('#')) { + return Color.valueOf(input); + } + else if ((function (input) { return input in Color; })(input)) { + return Color[input]; + } + else { + return null; + } + } + catch (e) { + return null; + } +} +/** Searches for an enemy tile near a unit. */ +function nearbyEnemyTile(unit, dist) { + //because the indexer is buggy + if (dist > 10) + (0, funcs_1.crash)("nearbyEnemyTile(): dist (".concat(dist, ") is too high!")); + var x = Math.floor(unit.x / Vars.tilesize); + var y = Math.floor(unit.y / Vars.tilesize); + for (var i = -dist; i <= dist; i++) { + for (var j = -dist; j <= dist; j++) { + var build = Vars.world.build(x + i, y + j); + if (build && build.team != unit.team && build.team != Team.derelict) + return build; + } + } + return null; +} +/** Attempts to parse a Team from the input. */ +exports.getTeam = (0, funcs_1.searchFixed)(Team.baseTeams.concat(Team.neoplastic), [ + function (i, s) { return i.name == s; }, + function (i, s) { return i.name == s.toLowerCase(); }, + function (i, s) { return i.emoji == s; }, + function (i, s) { return i.name.includes(s.toLowerCase()); }, + function (i, s) { return i.name.includes(s.toLowerCase().replace(" ", "-")); }, +]); +/** Attempts to parse an Item from the input. */ +exports.getItem = (0, funcs_1.searchFixed)(Vars.content.items().toArray(), [ + function (i, s) { return i.name == s; }, + function (i, s) { return i.name == s.toLowerCase(); }, + function (i, s) { return i.name.includes(s.toLowerCase()); }, + function (i, s) { return i.name.includes(s.toLowerCase().replace(" ", "-")); }, + function (i, s) { return i.emoji() == s; }, +]); +/** + * @param wordList "chat" is least strict, followed by "strict", and "name" is most strict. + * @returns a + */ +function matchFilter(input, wordList, aggressive) { + var e_1, _a, e_2, _b; + if (wordList === void 0) { wordList = "chat"; } + if (aggressive === void 0) { aggressive = false; } + var currentBannedWords = [ + wordList == "name" ? config_1.bannedWords.normal.filter(function (w) { return w[0] !== "uwu"; }) : config_1.bannedWords.normal, + (wordList == "strict" || wordList == "name") && config_1.bannedWords.strict, + wordList == "name" && config_1.bannedWords.names, + ].filter(Boolean).flat(); + if (aggressive) + currentBannedWords.push(["hitler", []]); + //Replace substitutions + var variations = [input, cleanText(input, false)]; + if (aggressive) + variations.push(cleanText(input, true)); + try { + for (var currentBannedWords_1 = __values(currentBannedWords), currentBannedWords_1_1 = currentBannedWords_1.next(); !currentBannedWords_1_1.done; currentBannedWords_1_1 = currentBannedWords_1.next()) { + var _c = __read(currentBannedWords_1_1.value, 2), banned = _c[0], whitelist = _c[1]; + var _loop_1 = function (text_1) { + if (banned instanceof RegExp ? banned.test(text_1) : text_1.includes(banned)) { + var modifiedText_1 = text_1; + whitelist.forEach(function (w) { return modifiedText_1 = modifiedText_1.replace(new RegExp(w, "g"), ""); }); //Replace whitelisted words with nothing + if (banned instanceof RegExp ? banned.test(modifiedText_1) : modifiedText_1.includes(banned)) //If the text still matches, fail + return { value: (banned === globals_1.uuidPattern ? "a Mindustry UUID" : + banned === globals_1.ipPattern || banned === globals_1.ipPortPattern ? "an IP address" : + //parsing regex with regex, massive hack + banned instanceof RegExp ? banned.source.replace(/\\b|\(\?|||>", "Name contains >|||> which is reserved for the server owner"], + "\uE817", "\uE82C", "\uE88E", "\uE813", + [/^[<\uE825].{1,3}[>\uE83A]/, "Name contains a prefix such as which is used for role prefixes"], + [function (replacedText) { return !isAdmin && config_1.adminNames.includes(replacedText.replace(/ /g, "")); }, "One of our admins uses this name"] + ]); + try { + for (var filters_1 = __values(filters), filters_1_1 = filters_1.next(); !filters_1_1.done; filters_1_1 = filters_1.next()) { + var _b = __read(filters_1_1.value, 2), check = _b[0], message = _b[1]; + if (check(replacedText)) + return message; + if (check(antiEvasionText)) + return message; + } + } + catch (e_3_1) { e_3 = { error: e_3_1 }; } + finally { + try { + if (filters_1_1 && !filters_1_1.done && (_a = filters_1.return)) _a.call(filters_1); + } + finally { if (e_3) throw e_3.error; } + } + return false; +} +function logAction(action, by, to, reason, duration) { + if (by === undefined) { //overload 1 + api.sendModerationMessage("".concat(action, "\n**Server:** ").concat(config_1.Gamemode.name())); + return; + } + if (to === undefined) { //overload 2 + api.sendModerationMessage("".concat((0, funcs_1.escapeTextDiscord)(Strings.stripColors(by.name)), " ").concat(action, "\n**Server:** ").concat(config_1.Gamemode.name())); + return; + } + if (to) { //overload 3 + var name = void 0, uuid = void 0, ip = void 0; + var actor = typeof by === "string" ? by : (0, funcs_1.escapeTextDiscord)(Strings.stripColors(by.name)); + if (to instanceof players_1.FishPlayer) { + name = (0, funcs_1.escapeTextDiscord)(to.name); + uuid = to.uuid; + ip = to.ip(); + } + else if (typeof to == "string") { + if (globals_1.uuidPattern.test(to)) { + name = "[".concat(to, "]"); + uuid = to; + ip = "[unknown]"; + } + else { + name = to; + uuid = "[unknown]"; + ip = "[unknown]"; + } + } + else { + name = (0, funcs_1.escapeTextDiscord)(to.lastName); + uuid = to.id; + ip = to.lastIP; + } + api.sendModerationMessage("".concat(actor, " ").concat(action, " ").concat(name, " ").concat(duration ? "for ".concat(formatTime(duration), " ") : "").concat(reason ? "with reason ".concat((0, funcs_1.escapeTextDiscord)(reason)) : "", "\n**Server:** ").concat(config_1.Gamemode.name(), "\n**uuid:** `").concat(uuid, "`\n**ip**: `").concat(ip, "`")); + return; + } +} +/** @returns the number of milliseconds. */ +function parseTimeString(str) { + var e_4, _a; + var formats = [ + [/(\d+)s/, 1], + [/(\d+)m/, 60], + [/(\d+)h/, 3600], + [/(\d+)d/, 86400], + [/(\d+)w/, 604800] + ].map(function (_a) { + var _b = __read(_a, 2), regex = _b[0], mult = _b[1]; + return [Pattern.compile(regex.source), mult]; + }); + if (str == "forever") + return (globals_1.maxTime - Date.now() - 10000); + try { + for (var formats_1 = __values(formats), formats_1_1 = formats_1.next(); !formats_1_1.done; formats_1_1 = formats_1.next()) { + var _b = __read(formats_1_1.value, 2), pattern = _b[0], mult = _b[1]; + //rhino regex doesn't work + var matcher = pattern.matcher(str); + if (matcher.matches()) { + var num = Number(matcher.group(1)); + if (!isNaN(num)) + return (num * mult) * 1000; + } + } + } + catch (e_4_1) { e_4 = { error: e_4_1 }; } + finally { + try { + if (formats_1_1 && !formats_1_1.done && (_a = formats_1.return)) _a.call(formats_1); + } + finally { if (e_4) throw e_4.error; } + } + return null; +} +/** + * Triggers the restart countdown. Execution always returns from this function. + * @param [fake=false] if set, server will not actually restart. + */ +function serverRestartLoop(sec, fake) { + if (fake === void 0) { fake = false; } + if (sec > 0) { + if (sec < 15 || sec % 5 == 0) + Call.sendMessage("[scarlet]Server restarting in: ".concat(sec)); + globals_1.fishState.restartLoopTask = Timer.schedule(function () { return serverRestartLoop(sec - 1); }, 1); + } + else if (!fake) { + restartNow(); + } +} +/** + * Actually restarts. Kicks all players. Execution always returns from this function. + * @param [removeSave=false] If set, save will be deleted instead of saved. Used to start a new game after the restart. + */ +function restartNow(removeSave) { + if (removeSave === void 0) { removeSave = false; } + Log.info("Restarting..."); + Vars.netServer.kickAll(Packets.KickReason.serverRestarting); + Vars.net.closeServer(); + Vars.state.set(GameState.State.menu); + var file = Vars.saveDirectory.child('1' + '.' + Vars.saveExtension); + if (removeSave) { + Core.app.post(function () { + file.delete(); + Core.app.exit(); + }); + } + else { + Core.app.post(function () { + SaveIO.save(file); + Core.app.exit(); + }); + } +} +function isBuildable(block) { + return block == Blocks.powerVoid || (block.buildType != Blocks.air.buildType && !(block instanceof ConstructBlock)); +} +exports.getUnitType = (0, funcs_1.searchFixed)(function () { return Vars.content.units().select(function (u) { return !(u instanceof MissileUnitType || u.internal); }).toArray(); }, [ + function (u, q) { return u.name == q; }, + function (u, q) { return u.name.includes(q.toLowerCase()); }, +]); +/** The vanilla validation code doesn't work on servers */ +function isMapValidForGamemode(map) { + if (map.custom) + return true; //we assume that all custom maps are appropriate for the selected gamemode + var pvpMaps = ["Veins", "Glacier", "Passage"]; //Maps.pvpMaps + switch (Vars.state.rules.mode().name()) { + case "sandbox": + case "editor": return true; //sandbox can be played on any map + case "attack": + case "pvp": return pvpMaps.includes(map.name()); //technically the pvp maps are valid attack maps, since they have an (undefended) enemy core + case "survival": return !pvpMaps.includes(map.name()); + default: return false; //unreachable + } +} +exports.getMap = (0, funcs_1.searchFixed)(function () { return Vars.maps.all().select(isMapValidForGamemode).toArray(); }, [ + function (m, name) { return m.name().replace(/ /g, "_") === name; }, //exact match with spaces replaced + function (m, name) { return m.name().replace(/ /g, "_").toLowerCase() === name.toLowerCase(); }, //exact match with spaces replaced ignoring case + function (m, name) { return m.plainName().replace(/ /g, "_").toLowerCase() === name.toLowerCase(); }, //exact match with spaces replaced ignoring case and colors + function (m, name) { return m.plainName().toLowerCase().includes(name.toLowerCase()); }, //partial match ignoring case and colors + function (m, name) { return m.plainName().replace(/ /g, "_").toLowerCase().includes(name.toLowerCase()); }, //partial match with spaces replaced ignoring case and colors + function (m, name) { return m.plainName().replace(/ /g, "").toLowerCase().includes(name.toLowerCase()); }, //partial match with spaces removed ignoring case and colors + function (m, name) { return m.plainName().replace(/[^a-zA-Z]/gi, "").toLowerCase().includes(name.toLowerCase()); }, +], "recomputeOptions"); +//static cache +var buildableBlocks = null; +function getBlock(block, filter) { + buildableBlocks !== null && buildableBlocks !== void 0 ? buildableBlocks : (buildableBlocks = Vars.content.blocks().select(isBuildable)); + var check = { + buildable: function (b) { return isBuildable(b); }, + air: function (b) { return b == Blocks.air || isBuildable(b); }, + all: function (b) { return true; } + }[filter]; + var out; + if (block in Blocks && Blocks[block] instanceof Block && check(Blocks[block])) + return Blocks[block]; + else if ((out = Vars.content.blocks().find(function (t) { return t.name.includes(block.toLowerCase()) && check(t); }))) + return out; + else if ((out = Vars.content.blocks().find(function (t) { return t.name.replace(/-/g, "").includes(block.toLowerCase().replace(/ /g, "")) && check(t); }))) + return out; + else if (block.includes("airblast")) + return Blocks.blastDrill; + return "\"".concat(block, "\" is not a valid block."); +} +function teleportPlayer(player, to) { + Timer.schedule(function () { + var p = player.unit(); + var t = to.unit(); + if (p && t) { + p.set(t.x, t.y); + Call.setPosition(player.con, t.x, t.y); + Call.setCameraPosition(player.con, t.x, t.y); + } + }, 0, 0.016, 10); +} +function logErrors(message, func) { + return function () { + var args = []; + for (var _i = 0; _i < arguments.length; _i++) { + args[_i] = arguments[_i]; + } + try { + return func.apply(void 0, __spreadArray([], __read(args), false)); + } + catch (err) { + Log.err(message); + Log.err((0, funcs_1.parseError)(err)); + } + }; +} +function definitelyRealMemoryCorruption() { + Log.info("Triggering a prank: this will cause players to see two error messages claiming to be from a memory corruption, and cause a flickering amount of fissile matter and dormant cysts to be put in the core."); + players_1.FishPlayer.messageStaff("[gray]<[cyan]staff[gray]> [white]Activating memory corruption prank! (please don't ruin it by telling players what is happening, pretend you dont know)"); + api.sendModerationMessage("Activated memory corruption prank on server ".concat(Vars.state.rules.mode().name())); + var t1f = false; + var t2f = false; + globals_1.fishState.corruption_t1 = Timer.schedule(function () { + var _a; + t1f = !t1f; + (_a = Vars.state.rules.defaultTeam.items()) === null || _a === void 0 ? void 0 : _a.set(Items.dormantCyst, t1f ? 69 : 420); + }, 0, 0.4, 600); + globals_1.fishState.corruption_t2 = Timer.schedule(function () { + var _a; + t2f = !t2f; + (_a = Vars.state.rules.defaultTeam.items()) === null || _a === void 0 ? void 0 : _a.set(Items.fissileMatter, t2f ? 999 : 123); + }, 0, 1.5, 200); + var hexString = Math.floor(Math.random() * 0xFFFFFFFF).toString(16).padStart(8, "0"); + Call.sendMessage("[scarlet]Error: internal server error."); + Call.sendMessage("[scarlet]Error: memory corruption: mindustry.world.modules.ItemModule@".concat(hexString)); + globals_1.FishEvents.fire("memoryCorruption", []); +} +function getEnemyTeam() { + if (config_1.Gamemode.pvp()) + return Team.derelict; + else + return Vars.state.rules.waveTeam; +} +function neutralGameover() { + players_1.FishPlayer.ignoreGameover(function () { + Events.fire(new EventType.GameOverEvent(getEnemyTeam())); + }); +} +/** Please validate requestedWaves to ensure it is not huge */ +function skipWaves(requestedWaves, runIntermediateWaves) { + //Check if the current map has opted out of multi-wave skipping + if (Vars.state.rules.tags.get(config_1.noMultiVnwTag) == "true") { + requestedWaves = Math.min(requestedWaves, 1); + } + var winWave = Vars.state.rules.winWave; + if (winWave <= 0) + winWave = Infinity; + var wavesToSkip = Math.min(requestedWaves, winWave - Vars.state.wave); + if (runIntermediateWaves) { + for (var i = 0; i < wavesToSkip; i++) { + Vars.logic.skipWave(); + } + } + else { + Vars.state.wave += (wavesToSkip - 1); + Vars.logic.skipWave(); + } +} +function logHTrip(player, name, message) { + Log.warn("&yPlayer &b\"".concat(player.cleanedName, "\"&y (&b").concat(player.uuid, "&y/&b").concat(player.ip(), "&y) tripped &c").concat(name, "&y") + (message ? ": ".concat(message) : "")); + players_1.FishPlayer.messageStaff("[yellow]Player [blue]\"".concat(player.cleanedName, "\"[] tripped [cyan]").concat(name, "[]") + (message ? ": ".concat(message) : "")); + api.sendModerationMessage("Player `".concat(player.cleanedName, "` (`").concat(player.uuid, "`/`").concat(player.ip(), "`) tripped **").concat(name, "**").concat(message ? ": ".concat(message) : "", "\n**Server:** ").concat(config_1.Gamemode.name())); +} +function setType(input) { + //does not do any checking +} +function untilForever() { + return (globals_1.maxTime - Date.now() - 10000); +} +function colorNumber(number, getColor, side) { + if (side === void 0) { side = "client"; } + return getColor(number) + number.toString() + (side == "client" ? "[]" : "&fr"); +} +function formatRatekeeper(x) { + if (x.lastTime <= 1) + return "0"; + return "".concat(x.occurences, " / ").concat(formatTimeRelative(x.lastTime, true)); +} +function getAntiBotInfo(side) { + var color = side == "client" ? "[acid]" : "&ly"; + var True = side == "client" ? "[red]true[]" : "&lrtrue"; + var False = side == "client" ? "[green]false[]" : "&gfalse"; + return ("".concat(color, "Flag count: ").concat(formatRatekeeper(players_1.FishPlayer.autoflagRate), "\n").concat(color, "Autobanning flagged players: ").concat(players_1.FishPlayer.shouldWhackFlaggedPlayers() ? True : False, "\n").concat(color, "Kicking new players: ").concat(players_1.FishPlayer.shouldKickNewPlayers() ? True : False, "\n").concat(color, "Recent connect packets: ").concat(formatRatekeeper(players_1.FishPlayer.connectRate), "\n").concat(color, "Reason: ").concat(players_1.FishPlayer.lastAntibotReason)); +} +var failPrefix = "[scarlet]\u26A0 [yellow]"; +var successPrefix = "[#48e076]\uE800 "; +function outputFail(message, sender, ratelimit) { + var msg = failPrefix + (typeof message == "function" && "__partialFormatString" in message ? message("[yellow]") : message); + if (ratelimit) + sender.sendMessage(msg, ratelimit); + else + sender.sendMessage(msg); +} +function outputSuccess(message, sender) { + sender.sendMessage(successPrefix + (typeof message == "function" && "__partialFormatString" in message ? message("[#48e076]") : message)); +} +function outputMessage(message, sender) { + sender.sendMessage(((typeof message == "function" && "__partialFormatString" in message ? message(null) : message) + "").replace(/\t/g, " ".repeat(4))); +} +function outputConsole(message, channel) { + if (channel === void 0) { channel = Log.info; } + channel(typeof message == "function" && "__partialFormatString" in message ? message("") : message); +} +function updateBans(message) { + Groups.player.each(function (player) { + if (Vars.netServer.admins.isIDBanned(player.uuid())) { + player.con.kick(Packets.KickReason.banned); + if (message) + Call.sendMessage(message(player)); + } + }); +} +function processChat(player, message, effects) { + if (effects === void 0) { effects = false; } + var fishPlayer = players_1.FishPlayer.get(player); + var highlight = fishPlayer.highlight; + var filterTripText; + var suspicious = fishPlayer.suspicionLevel() == 3; + if ((!fishPlayer.hasPerm("bypassChatFilter") || fishPlayer.chatStrictness == "strict") + && (filterTripText = matchFilter(message, fishPlayer.chatStrictness, suspicious))) { + if (effects) { + if (suspicious && removeFoosChars(message).split(" ") + .map(function (w) { return w.replace(/[-_.^*,]/g, ""); }) + .some(function (w) { return config_1.bannedWords.autoWhack.includes(w); })) { + if (!fishPlayer.muted) { + logHTrip(fishPlayer, "bad words in chat", "message: `".concat(message, "`")); + fishPlayer.muted = true; + void fishPlayer.stop("automod", globals_1.maxTime, "Automatic stop due to suspicious activity", false); + } + } + Log.info("Censored message from player ".concat(player.name, ": \"").concat((0, funcs_1.escapeStringColorsServer)(message), "\"; contained \"").concat(filterTripText, "\"")); + players_1.FishPlayer.messageStaff("[yellow]Censored message from player ".concat(fishPlayer.cleanedName, ": \"").concat(message, "\" contained \"").concat(filterTripText, "\"")); + } + message = config_1.text.chatFilterReplacement.message(); + highlight !== null && highlight !== void 0 ? highlight : (highlight = config_1.text.chatFilterReplacement.highlight()); + } + if (message.startsWith("./")) + message = message.replace("./", "/"); + if (!fishPlayer.hasPerm("chat")) { + if (effects) { + players_1.FishPlayer.messageMuted(player.name, message); + Log.info("".concat(player.name, ": ").concat(message)); + } + return null; + } + return (highlight !== null && highlight !== void 0 ? highlight : "") + message; +} +var replacements = [ + //Serpulo units + ["dagger", "mace", "fortress", "scepter", "reign", "nova", "pulsar", "quasar", "vela", "corvus", "crawler", "atrax", "spiroct", "arkyid", "toxopid", "flare", "horizon", "zenith", "antumbra", "eclipse", "mono", "poly", "mega", "quad", "oct", "risso", "minke", "bryde", "sei", "omura", "retusa", "oxynoe", "cyerce", "aegires", "navanax", "fort", "toxo", "flarogus"], + //Erekir units + ["stell", "locus", "precept", "vanquish", "conquer", "merui", "cleroi", "anthicus", "tecta", "collaris", "elude", "avert", "obviate", "quell", "disrupt", "vanq", "crab", "anthi", "larry", "obvi"], + //Items, full form + ["copper", "lead", "metaglass", "graphite", "sand", "coal", "titanium", "thorium", "scrap", "silicon", "plastanium", "phase fabric", "surge alloy", "spore pod", "blast compound", "pyratite", "beryllium", "tungsten", "oxide", "carbide"], + //Items, short form + ["coppa", "meta", "graph", "tita", "titan", "thor", "scrap", "sili", "plast", "phase", "surge", "spore", "blast", "pyra", "beryl", "tung", "oxide", "carb"], + //Liquids + ["water", "slag", "oil", "cryo", "cryofluid"], + //Liquids/gases (erekir) + ["hydrogen", "ozone", "nitrogen", "cyanogen", "cyan", "nitro", "hydro", "arky", "arkycite", "neoplasm"], + //Gamemodes + ["attack", "sandbox", "pvp", "hexed", "survival"], + //teams + ["crux", "sharded", "malis", "neoplastic"], + //maps + ["rampant", "harbor war", "cave canal", "acheron", "wolframfestung", "avast", "fallen omura", "assault"], + //aquatic animals + ["fish", "shark", "whale", "dolphin", "salmon", "tuna", "squid", "jellyfish", "turtle"], + //antonym adjectives + ["fast", "slow"], ["big", "little"], ["hot", "cold"], ["hard", "easy", "difficult", "ez"], ["hello", "bye"], +].map(function (set) { return [set, new RegExp("\\b(?:".concat(set.join("|"), ")(e?s?(?:i?gone)?)\\b"), 'g')]; }); +var foolCounter = 0; +exports.foolifyChat = memoizeChatFilter(function foolifyChat(message) { + var e_5, _a; + var cleanedMessage = removeFoosChars(message); + setShuffle: { + if (foolCounter < 8) { + //Skip the next 5 messages no matter what + foolCounter++; + break setShuffle; + } + var replacedMessage = cleanedMessage; + var _loop_2 = function (set, regex) { + replacedMessage = replacedMessage.replace(regex, function (_, plural) { return (0, funcs_1.random)(set) + plural; }); + }; + try { + for (var replacements_1 = __values(replacements), replacements_1_1 = replacements_1.next(); !replacements_1_1.done; replacements_1_1 = replacements_1.next()) { + var _b = __read(replacements_1_1.value, 2), set = _b[0], regex = _b[1]; + _loop_2(set, regex); + } + } + catch (e_5_1) { e_5 = { error: e_5_1 }; } + finally { + try { + if (replacements_1_1 && !replacements_1_1.done && (_a = replacements_1.return)) _a.call(replacements_1); + } + finally { if (e_5) throw e_5.error; } + } + if (replacedMessage !== cleanedMessage) { + if (foolCounter < 11) { + //Skip the next 2 messages that would get altered + foolCounter++; + break setShuffle; + } + foolCounter = 0; + return replacedMessage; + } + else { + break setShuffle; + } + } + if (Math.random() < 0.01) { + return cleanedMessage.split("").reverse().join(""); + // eslint-disable-next-line no-dupe-else-if + } + else if (Math.random() < 0.01) { + return "[scarlet]I really hope everyone is having a fun time :} <3"; + } + else if (Math.random() < 0.005) { + return "[cyan]AMOGUS"; + } + else { + return message; + } +}); +exports.addToTileHistory = logErrors("Error while saving a tilelog entry", function (e) { + var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q, _r, _s, _t, _u, _v, _w, _x, _y, _z, _0, _1, _2, _3; + // eslint-disable-next-line prefer-const + var tile, uuid, action, type, time = Date.now(); + if (e instanceof EventType.BlockBuildBeginEvent) { + tile = e.tile; + uuid = (_e = (_c = (_b = (_a = e.unit) === null || _a === void 0 ? void 0 : _a.player) === null || _b === void 0 ? void 0 : _b.uuid()) !== null && _c !== void 0 ? _c : (_d = e.unit) === null || _d === void 0 ? void 0 : _d.type.name) !== null && _e !== void 0 ? _e : "unknown"; + if (e.breaking) { + action = "broke"; + type = (e.tile.build instanceof ConstructBlock.ConstructBuild) ? e.tile.build.previous.name : "unknown"; + if (((_g = (_f = e.unit) === null || _f === void 0 ? void 0 : _f.player) === null || _g === void 0 ? void 0 : _g.uuid()) && ((_h = e.tile.build) === null || _h === void 0 ? void 0 : _h.team) != Team.derelict) { + var fishP = players_1.FishPlayer.get(e.unit.player); + //TODO move this code + fishP.tstats.blocksBroken++; + fishP.tstats.blockInteractionsThisMap++; + fishP.updateStats(function (stats) { return stats.blocksBroken++; }); + } + } + else { + action = "built"; + type = (e.tile.build instanceof ConstructBlock.ConstructBuild) ? e.tile.build.current.name : "unknown"; + if ((_k = (_j = e.unit) === null || _j === void 0 ? void 0 : _j.player) === null || _k === void 0 ? void 0 : _k.uuid()) { + var fishP = players_1.FishPlayer.get(e.unit.player); + //TODO move this code + fishP.updateStats(function (stats) { return stats.blocksPlaced++; }); + fishP.tstats.blockInteractionsThisMap++; + } + } + } + else if (e instanceof EventType.ConfigEvent) { + tile = e.tile.tile; + uuid = (_m = (_l = e.player) === null || _l === void 0 ? void 0 : _l.uuid()) !== null && _m !== void 0 ? _m : "unknown"; + if (uuid != "unknown") { + var fishP = players_1.FishPlayer.getById(uuid); + if (fishP) + fishP.tstats.blockInteractionsThisMap++; + } + action = "configured"; + type = e.tile.block.name; + } + else if (e instanceof EventType.BuildRotateEvent) { + tile = e.build.tile; + uuid = (_s = (_q = (_p = (_o = e.unit) === null || _o === void 0 ? void 0 : _o.player) === null || _p === void 0 ? void 0 : _p.uuid()) !== null && _q !== void 0 ? _q : (_r = e.unit) === null || _r === void 0 ? void 0 : _r.type.name) !== null && _s !== void 0 ? _s : "unknown"; + if (uuid != "unknown") { + var fishP = players_1.FishPlayer.getById(uuid); + if (fishP) + fishP.tstats.blockInteractionsThisMap++; + } + action = "rotated"; + type = e.build.block.name; + } + else if (e instanceof EventType.UnitDestroyEvent) { + tile = e.unit.tileOn(); + if (!tile) + return; + if (!e.unit.type.playerControllable) + return; + uuid = e.unit.isPlayer() ? e.unit.getPlayer().uuid() : (_t = e.unit.lastCommanded) !== null && _t !== void 0 ? _t : "unknown"; + action = "killed"; + type = e.unit.type.name; + } + else if (e instanceof EventType.BlockDestroyEvent) { + if (config_1.Gamemode.attack() && ((_u = e.tile.build) === null || _u === void 0 ? void 0 : _u.team) != Vars.state.rules.defaultTeam) + return; //Don't log destruction of enemy blocks + tile = e.tile; + uuid = "[[something]"; + action = "killed"; + type = (_w = (_v = e.tile.block()) === null || _v === void 0 ? void 0 : _v.name) !== null && _w !== void 0 ? _w : "air"; + } + else if (e instanceof EventType.PayloadDropEvent) { + action = "pay-dropped"; + var controller = e.carrier.controller(); + uuid = (_z = (_y = (_x = e.carrier.player) === null || _x === void 0 ? void 0 : _x.uuid()) !== null && _y !== void 0 ? _y : (controller instanceof LogicAI && controller.controller ? + "".concat(e.carrier.type.name, " controlled by ").concat(controller.controller.block.name, " at ").concat(controller.controller.tileX(), ",").concat(controller.controller.tileY(), " last accessed by ").concat(e.carrier.getControllerName()) + : null)) !== null && _z !== void 0 ? _z : e.carrier.type.name; + if (e.build) { + tile = e.build.tile; + type = e.build.block.name; + } + else if (e.unit) { + tile = e.unit.tileOn(); + if (!tile) + return; + type = e.unit.type.name; + } + else + return; + } + else if (e instanceof EventType.PickupEvent) { + action = "picked up"; + if (e.carrier.isPlayer()) + return; //This event would have been handled by actionfilter + var controller = e.carrier.controller(); + if (!(controller instanceof LogicAI && controller.controller != null)) + return; + uuid = "".concat(e.carrier.type.name, " controlled by ").concat(controller.controller.block.name, " at ").concat(controller.controller.tileX(), ",").concat(controller.controller.tileY(), " last accessed by ").concat(e.carrier.getControllerName()); + if (e.build) { + tile = e.build.tile; + type = e.build.block.name; + } + else if (e.unit) { + tile = e.unit.tileOn(); + if (!tile) + return; + type = e.unit.type.name; + } + else + return; + } + else if (e instanceof EventType.UnitControlEvent) { + if (e.unit instanceof Packages.mindustry.gen.BlockUnitUnit) { + action = "controlled"; + tile = (_0 = e.unit) === null || _0 === void 0 ? void 0 : _0.tile().tile; + if (!tile) + return; + type = (_2 = (_1 = tile.block()) === null || _1 === void 0 ? void 0 : _1.name) !== null && _2 !== void 0 ? _2 : "air"; + uuid = e.player.uuid(); + } + else + return; + } + else if (e instanceof Object && "pos" in e && "uuid" in e && "action" in e && "type" in e) { + var pos = void 0; + (pos = e.pos, uuid = e.uuid, action = e.action, type = e.type); + tile = (_3 = Vars.world.tile(pos.split(",")[0], pos.split(",")[1])) !== null && _3 !== void 0 ? _3 : (0, funcs_1.crash)("Cannot log ".concat(action, " at ").concat(pos, ": Nonexistent tile")); + } + else + return; + if (tile == null) + return; + [tile, uuid, action, type, time]; + tile.getLinkedTiles(function (t) { + var pos = "".concat(t.x, ",").concat(t.y); + var existingData = globals_1.tileHistory[pos] ? funcs_1.StringIO.read(globals_1.tileHistory[pos], function (str) { return str.readArray(function (d) { return ({ + action: d.readString(2), + uuid: d.readString(3), + time: d.readNumber(16), + type: d.readString(2), + }); }, 1); }) : []; + existingData.push({ + action: action, + uuid: uuid, + time: time, + type: type + }); + existingData = existingData.slice(-9); + //Write + globals_1.tileHistory[t.x + ',' + t.y] = funcs_1.StringIO.write(existingData, function (str, data) { return str.writeArray(data, function (el) { + str.writeString(el.action, 2); + str.writeString(el.uuid, 3); + str.writeNumber(el.time, 16); + str.writeString(el.type, 2); + }, 1); }); + }); +}); +function getIPRange(input, error) { + if (globals_1.ipRangeCIDRPattern.test(input)) { + var _a = __read(input.split("/"), 2), ip = _a[0], maskLength = _a[1]; + switch (maskLength) { + case "24": + return ip.split(".").slice(0, 3).join(".") + "."; + case "16": + return ip.split(".").slice(0, 2).join(".") + "."; + default: + error === null || error === void 0 ? void 0 : error("Mindustry does not currently support netmasks other than /16 and /24"); + return null; + } + } + else if (globals_1.ipRangeWildcardPattern.test(input)) { + //1.2.3.* + //1.2.* + var _b = __read(input.split("."), 4), a = _b[0], b = _b[1], c = _b[2], d = _b[3]; + if (c !== "*") + return "".concat(a, ".").concat(b, ".").concat(c, "."); + return "".concat(a, ".").concat(b, "."); + } + else + return null; +} +//this brings me physical pain +function getHash(file, algorithm) { + if (algorithm === void 0) { algorithm = "SHA-1"; } + try { + var header = "blob ".concat(file.length(), "\0"); + var fileSHAHeader = Packages.java.nio.charset.StandardCharsets.UTF_8.encode(header); + var contents = file.readBytes(); + var buffer = Packages.java.nio.ByteBuffer.allocate(fileSHAHeader.remaining() + contents.length); + buffer.put(fileSHAHeader); + buffer.put(contents); + buffer.flip(); + var digest = Packages.java.security.MessageDigest.getInstance(algorithm); + digest.update(buffer); + return digest.digest().map(function (byte) { + return (byte & 0xFF).toString(16).padStart(2, "0"); + }).join(""); + } + catch (e) { + Log.err("Cannot generate ".concat(algorithm, ", ").concat(String(e))); + return undefined; + } +} +function match(value, clauses, defaultValue) { + return Object.prototype.hasOwnProperty.call(clauses, value) ? clauses[value] : defaultValue; +} +/** @throws CommandError */ +function fishCommandsRootDirPath() { + var commandsDir = Vars.modDirectory.child("fish-commands"); + if (!commandsDir.exists()) + (0, commands_1.fail)("Fish commands directory at path ".concat(commandsDir.absolutePath(), " does not exist!")); + var fishCommandsRootDirPath = Paths.get(commandsDir.file().path); + if (Packages.java.nio.file.Files.isSymbolicLink(fishCommandsRootDirPath)) { + //fish-commands is linked to the build directory of somewhere else + //resolve and get the parent directory of the build directory + fishCommandsRootDirPath = fishCommandsRootDirPath.toRealPath().getParent(); + } + return fishCommandsRootDirPath; +} +/** Fails if "mode" is invalid. */ +function applyEffectMode(mode, unit, ticks) { + var e_6, _a; + var _b; + var modes = { + fast: [StatusEffects.fast], + fast2: [StatusEffects.fast, StatusEffects.overdrive, StatusEffects.overclock], + boss: [StatusEffects.boss], + health: [StatusEffects.boss, StatusEffects.shielded], + slow: [StatusEffects.slow], + slow2: [ + StatusEffects.slow, + StatusEffects.freezing, + StatusEffects.wet, + StatusEffects.muddy, + StatusEffects.sapped, + StatusEffects.sporeSlowed, + StatusEffects.electrified, + StatusEffects.tarred, + ], + freeze: [StatusEffects.unmoving], + disarm: [StatusEffects.disarmed], + invincible: [StatusEffects.invincible], + boost: [ + StatusEffects.fast, + StatusEffects.overdrive, + StatusEffects.overclock, + StatusEffects.boss, + StatusEffects.shielded, + ], + damage: [ + StatusEffects.burning, + StatusEffects.freezing, + StatusEffects.wet, + StatusEffects.muddy, + StatusEffects.melting, + StatusEffects.sapped, + StatusEffects.tarred, + StatusEffects.shocked, + StatusEffects.blasted, + StatusEffects.corroded, + StatusEffects.sporeSlowed, + StatusEffects.electrified, + StatusEffects.fast, + ], + clear: function (unit) { + unit.clearStatuses(); + unit.maxHealth = unit.type.health; + }, + paper: function (unit) { + unit.health = 1; + unit.maxHealth = 1; + unit.apply(StatusEffects.disarmed, Number.MAX_VALUE / 2); + }, + heal: function (unit) { + unit.health = unit.maxHealth; + }, + overheal: function (unit) { + unit.maxHealth = unit.health = 1e15; + }, + shield: function (unit) { + unit.shield = 1e15; + } + }; + var effects = (_b = match(mode, modes, null)) !== null && _b !== void 0 ? _b : (0, commands_1.fail)("Invalid mode. Supported modes: ".concat(Object.keys(modes).join(", "))); + if (typeof effects === "function") { + effects(unit); + } + else { + try { + for (var effects_1 = __values(effects), effects_1_1 = effects_1.next(); !effects_1_1.done; effects_1_1 = effects_1.next()) { + var effect = effects_1_1.value; + unit.apply(effect, ticks); + } + } + catch (e_6_1) { e_6 = { error: e_6_1 }; } + finally { + try { + if (effects_1_1 && !effects_1_1.done && (_a = effects_1.return)) _a.call(effects_1); + } + finally { if (e_6) throw e_6.error; } + } + } +} +function handleError(err, sender, outputFail, context) { + if (err instanceof commands_1.CommandError) { + //If the error is a command error, then just outputFail + outputFail(err.data, sender); + } + else if (err === menus_1.Cancel) { + //Menu cancelled, do nothing + return; + } + else { + sender.sendMessage("[scarlet]\u274C An error occurred while executing the command!"); + if (sender.hasPerm("seeErrorMessages")) + sender.sendMessage((0, funcs_1.parseError)(err)); + Log.err(context ? + "Unhandled error in command execution: ".concat(context) + : "Unhandled error in command execution."); + Log.err(err); + if (typeof err == "object" && err != null && "stack" in err) + Log.err(err.stack); + } +} +function syncManual(player, rules, emptyMap) { + if (rules === void 0) { rules = Vars.state.rules; } + return new Promise(function (resolve) { + Threads.daemon(function () { + Call.worldDataBegin(player.con); + var os = new ByteArrayOutputStream(); + var stream = new DataOutputStream(new FastDeflaterOutputStream(os)); + stream.writeUTF(JsonIO.write(rules)); + stream.writeUTF(JsonIO.write(Vars.state.mapLocales)); + SaveIO.getSaveWriter().writeStringMap(stream, Vars.state.map.tags); + stream.writeInt(Vars.state.wave); + stream.writeFloat(Vars.state.wavetime); + stream.writeDouble(Vars.state.tick); + stream.writeLong(GlobalVars.rand.seed0); + stream.writeLong(GlobalVars.rand.seed1); + stream.writeInt(player.id); + player.write(new Writes(stream)); + SaveIO.getSaveWriter().writeContentHeader(stream); + SaveIO.getSaveWriter().writeContentPatches(stream); + if (emptyMap) { + //fake world, all the same tile + stream.writeShort(emptyMap.width); + stream.writeShort(emptyMap.height); + var area = emptyMap.width * emptyMap.height; + for (var i = 0; i < area;) { + stream.writeShort(emptyMap.floor.id); + stream.writeShort(emptyMap.overlay.id); + var needed = area - i - 1; + if (needed > 255) { + stream.writeByte(255); + i += 256; + } + else { + stream.writeByte(needed); + break; + } + } + for (var i = 0; i < area;) { + stream.writeShort(emptyMap.build.id); + stream.writeByte(0); + var needed = area - i - 1; + if (needed > 255) { + stream.writeByte(255); + i += 256; + } + else { + stream.writeByte(needed); + break; + } + } + } + else + SaveIO.getSaveWriter().writeMap(stream); + SaveIO.getSaveWriter().writeTeamBlocks(stream); + SaveIO.getSaveWriter().writeMarkers(stream); + SaveIO.getSaveWriter().writeCustomChunks(stream, true); + stream.close(); + var data = Object.assign(new Packets.WorldStream(), { + stream: new ByteArrayInputStream(os.toByteArray()) + }); + player.con.sendStream(data); + resolve(); + }); + }); +} +function crashClient(player) { + var planetBackground = Object.assign(new Packages.mindustry.graphics.g3d.PlanetParams(), { planet: null }); + if (Vars.state.rules.planetBackground) { + //There are already planet params, need to force sync + var rules = Object.assign(Vars.state.rules.copy(), { planetBackground: planetBackground }); + void syncManual(player, rules, { + width: 1, + height: 1, + floor: Blocks.space, + build: Blocks.air, + overlay: Blocks.air, + }); + return false; + } + else { + Call.setRule(player.con, "planetBackground", JsonIO.write(planetBackground)); + return true; + } +} +var sources = [ + Packages.mindustry.gen.UnitEntity, + Packages.mindustry.gen.MechUnit, + Packages.mindustry.gen.LegsUnit, + Packages.mindustry.gen.CrawlUnit, + Packages.mindustry.gen.UnitWaterMove, + Packages.mindustry.gen.BlockUnitUnit, + Packages.mindustry.gen.ElevationMoveUnit, + Packages.mindustry.gen.BuildingTetherPayloadUnit, + Packages.mindustry.gen.TimedKillUnit, + Packages.mindustry.gen.PayloadUnit, + Packages.mindustry.gen.TankUnit, +]; +function getStatuses(unit) { + var e_7, _a; + try { + for (var sources_1 = __values(sources), sources_1_1 = sources_1.next(); !sources_1_1.done; sources_1_1 = sources_1.next()) { + var clazz = sources_1_1.value; + if (unit instanceof clazz) + return ArcReflect.get(clazz, unit, "statuses"); + } + } + catch (e_7_1) { e_7 = { error: e_7_1 }; } + finally { + try { + if (sources_1_1 && !sources_1_1.done && (_a = sources_1.return)) _a.call(sources_1); + } + finally { if (e_7) throw e_7.error; } + } + return new Seq(); +} diff --git a/build/scripts/votes.js b/build/scripts/votes.js index 3765de2f..7c291217 100644 --- a/build/scripts/votes.js +++ b/build/scripts/votes.js @@ -1,218 +1,218 @@ -"use strict"; -/* -Copyright © BalaM314, 2026. All Rights Reserved. -This file contains the voting system. -Some contributions: @author Jurorno9 -*/ -var __extends = (this && this.__extends) || (function () { - var extendStatics = function (d, b) { - extendStatics = Object.setPrototypeOf || - ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || - function (d, b) { for (var p in b) if (Object.prototype.hasOwnProperty.call(b, p)) d[p] = b[p]; }; - return extendStatics(d, b); - }; - return function (d, b) { - if (typeof b !== "function" && b !== null) - throw new TypeError("Class extends value " + String(b) + " is not a constructor or null"); - extendStatics(d, b); - function __() { this.constructor = d; } - d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); - }; -})(); -var __values = (this && this.__values) || function(o) { - var s = typeof Symbol === "function" && Symbol.iterator, m = s && o[s], i = 0; - if (m) return m.call(o); - if (o && typeof o.length === "number") return { - next: function () { - if (o && i >= o.length) o = void 0; - return { value: o && o[i++], done: !o }; - } - }; - throw new TypeError(s ? "Object is not iterable." : "Symbol.iterator is not defined."); -}; -var __read = (this && this.__read) || function (o, n) { - var m = typeof Symbol === "function" && o[Symbol.iterator]; - if (!m) return o; - var i = m.call(o), r, ar = [], e; - try { - while ((n === void 0 || n-- > 0) && !(r = i.next()).done) ar.push(r.value); - } - catch (error) { e = { error: error }; } - finally { - try { - if (r && !r.done && (m = i["return"])) m.call(i); - } - finally { if (e) throw e.error; } - } - return ar; -}; -var __spreadArray = (this && this.__spreadArray) || function (to, from, pack) { - if (pack || arguments.length === 2) for (var i = 0, l = from.length, ar; i < l; i++) { - if (ar || !(i in from)) { - if (!ar) ar = Array.prototype.slice.call(from, 0, i); - ar[i] = from[i]; - } - } - return to.concat(ar || Array.prototype.slice.call(from)); -}; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.VoteManager = void 0; -var commands_1 = require("/frameworks/commands"); -var funcs_1 = require("/funcs"); -var globals_1 = require("/globals"); -var players_1 = require("/players"); -/** Manages a vote. */ -var VoteManager = /** @class */ (function (_super) { - __extends(VoteManager, _super); - function VoteManager(voteTime, goal, isEligible, isCounted) { - if (goal === void 0) { goal = ["fractionOfVoters", 0.50001]; } - if (isEligible === void 0) { isEligible = function () { return true; }; } - if (isCounted === void 0) { isCounted = function (fishP) { return !fishP.afk(); }; } - var _this = _super.call(this) || this; - _this.voteTime = voteTime; - _this.goal = goal; - _this.isEligible = isEligible; - _this.isCounted = isCounted; - /** The ongoing voting session, if there is one. */ - _this.session = null; - if (goal[0] == "fractionOfVoters") { - if (goal[1] < 0 || goal[1] > 1) - (0, funcs_1.crash)("Invalid goal: fractionOfVoters must be between 0 and 1 inclusive"); - } - else if (goal[0] == "absolute") { - if (goal[1] < 0) - (0, funcs_1.crash)("Invalid goal: absolute must be greater than 0"); - } - Events.on(EventType.PlayerLeave, function (_a) { - var player = _a.player; - //Run once the player has been removed, but resolve the player first in case the connection gets nulled - var fishP = players_1.FishPlayer.get(player); - Core.app.post(function () { return _this.unvote(fishP); }); - }); - Events.on(EventType.GameOverEvent, function () { return _this.resetVote(); }); - return _this; - } - /** @throws CommandError */ - VoteManager.prototype.start = function (player, newVote, data) { - var _this = this; - if (data === null) - (0, funcs_1.crash)("Cannot start vote: data not provided"); - if (!this.isEligible(player, data)) - (0, commands_1.fail)("You are not eligible for this vote."); - this.session = { - timer: Timer.schedule(function () { return _this._checkVote(false); }, this.voteTime / 1000), - votes: new Map(), - data: data, - }; - this.vote(player, newVote, data); - }; - /** @throws CommandError */ - VoteManager.prototype.vote = function (player, newVote, data) { - var _this = this; - if (!this.session) - return this.start(player, newVote, data); - if (!this.isEligible(player, this.session.data)) - (0, commands_1.fail)("You are not eligible for this vote."); - var oldVote = this.session.votes.get(player.uuid); - this.session.votes.set(player.uuid, newVote); - if (oldVote == null) - this.fire("player vote", [player, newVote]); - this.fire("player vote change", [player, oldVote !== null && oldVote !== void 0 ? oldVote : 0, newVote]); - if (Date.now() - globals_1.fishState.startTime < 3000) - Timer.schedule(function () { return _this._checkVote(false); }, 3); - else - this._checkVote(false); - }; - VoteManager.prototype.unvote = function (player) { - if (!this.session) - return; - var fishP = players_1.FishPlayer.resolve(player); - var vote = this.session.votes.get(fishP.uuid); - if (vote) { - this.session.votes.delete(fishP.uuid); - this.fire("player vote removed", [player, vote]); - this._checkVote(false); - } - }; - /** Does not fire the events used to display messages, please print one before calling this */ - VoteManager.prototype.forceVote = function (outcome) { - if (outcome) { - this.fire("success", [true]); - } - else { - this.fire("fail", [true]); - } - this.resetVote(); - }; - VoteManager.prototype.resetVote = function () { - if (this.session == null) - return; - this.session.timer.cancel(); - this.session = null; - }; - VoteManager.prototype.requiredVotes = function () { - var _this = this; - if (this.goal[0] == "absolute") { - return this.goal[1]; - } - else { - var numVoters = players_1.FishPlayer.getAllOnline().filter(function (p) { - return _this.isEligible(p, _this.session.data) && (_this.isCounted(p, _this.session.data) || _this.session.votes.has(p.uuid)); - }).length; - return Math.max(Math.ceil(this.goal[1] * numVoters), 1); - } - }; - VoteManager.prototype.currentVotes = function () { - var e_1, _a; - if (this.session) { - try { - for (var _b = __values(this.session.votes.keys()), _c = _b.next(); !_c.done; _c = _b.next()) { - var key = _c.value; - var fishP = players_1.FishPlayer.getById(key); - if (!this.isEligible(fishP, this.session.data)) - this.session.votes.delete(key); - } - } - catch (e_1_1) { e_1 = { error: e_1_1 }; } - finally { - try { - if (_c && !_c.done && (_a = _b.return)) _a.call(_b); - } - finally { if (e_1) throw e_1.error; } - } - return __spreadArray([], __read(this.session.votes), false).reduce(function (acc, _a) { - var _b = __read(_a, 2), k = _b[0], v = _b[1]; - return acc + v; - }, 0); - } - else - return 0; - }; - VoteManager.prototype.getEligibleVoters = function () { - var _this = this; - if (!this.session) - return []; - return players_1.FishPlayer.getAllOnline().filter(function (p) { - return _this.isEligible(p, _this.session.data); - }); - }; - VoteManager.prototype.messageEligibleVoters = function (message) { - this.getEligibleVoters().forEach(function (p) { return p.sendMessage(message); }); - }; - VoteManager.prototype._checkVote = function (end) { - var votes = this.currentVotes(); - var required = this.requiredVotes(); - if (votes >= required) { - this.fire("success", [false]); - this.fire("vote passed", [votes, required]); - this.resetVote(); - } - else if (end) { - this.fire("fail", [false]); - this.fire("vote failed", [votes, required]); - this.resetVote(); - } - }; - return VoteManager; -}(funcs_1.EventEmitter)); -exports.VoteManager = VoteManager; +"use strict"; +/* +Copyright © BalaM314, 2026. All Rights Reserved. +This file contains the voting system. +Some contributions: @author Jurorno9 +*/ +var __extends = (this && this.__extends) || (function () { + var extendStatics = function (d, b) { + extendStatics = Object.setPrototypeOf || + ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || + function (d, b) { for (var p in b) if (Object.prototype.hasOwnProperty.call(b, p)) d[p] = b[p]; }; + return extendStatics(d, b); + }; + return function (d, b) { + if (typeof b !== "function" && b !== null) + throw new TypeError("Class extends value " + String(b) + " is not a constructor or null"); + extendStatics(d, b); + function __() { this.constructor = d; } + d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); + }; +})(); +var __values = (this && this.__values) || function(o) { + var s = typeof Symbol === "function" && Symbol.iterator, m = s && o[s], i = 0; + if (m) return m.call(o); + if (o && typeof o.length === "number") return { + next: function () { + if (o && i >= o.length) o = void 0; + return { value: o && o[i++], done: !o }; + } + }; + throw new TypeError(s ? "Object is not iterable." : "Symbol.iterator is not defined."); +}; +var __read = (this && this.__read) || function (o, n) { + var m = typeof Symbol === "function" && o[Symbol.iterator]; + if (!m) return o; + var i = m.call(o), r, ar = [], e; + try { + while ((n === void 0 || n-- > 0) && !(r = i.next()).done) ar.push(r.value); + } + catch (error) { e = { error: error }; } + finally { + try { + if (r && !r.done && (m = i["return"])) m.call(i); + } + finally { if (e) throw e.error; } + } + return ar; +}; +var __spreadArray = (this && this.__spreadArray) || function (to, from, pack) { + if (pack || arguments.length === 2) for (var i = 0, l = from.length, ar; i < l; i++) { + if (ar || !(i in from)) { + if (!ar) ar = Array.prototype.slice.call(from, 0, i); + ar[i] = from[i]; + } + } + return to.concat(ar || Array.prototype.slice.call(from)); +}; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.VoteManager = void 0; +var commands_1 = require("/frameworks/commands"); +var funcs_1 = require("/funcs"); +var globals_1 = require("/globals"); +var players_1 = require("/players"); +/** Manages a vote. */ +var VoteManager = /** @class */ (function (_super) { + __extends(VoteManager, _super); + function VoteManager(voteTime, goal, isEligible, isCounted) { + if (goal === void 0) { goal = ["fractionOfVoters", 0.50001]; } + if (isEligible === void 0) { isEligible = function () { return true; }; } + if (isCounted === void 0) { isCounted = function (fishP) { return !fishP.afk(); }; } + var _this = _super.call(this) || this; + _this.voteTime = voteTime; + _this.goal = goal; + _this.isEligible = isEligible; + _this.isCounted = isCounted; + /** The ongoing voting session, if there is one. */ + _this.session = null; + if (goal[0] == "fractionOfVoters") { + if (goal[1] < 0 || goal[1] > 1) + (0, funcs_1.crash)("Invalid goal: fractionOfVoters must be between 0 and 1 inclusive"); + } + else if (goal[0] == "absolute") { + if (goal[1] < 0) + (0, funcs_1.crash)("Invalid goal: absolute must be greater than 0"); + } + Events.on(EventType.PlayerLeave, function (_a) { + var player = _a.player; + //Run once the player has been removed, but resolve the player first in case the connection gets nulled + var fishP = players_1.FishPlayer.get(player); + Core.app.post(function () { return _this.unvote(fishP); }); + }); + Events.on(EventType.GameOverEvent, function () { return _this.resetVote(); }); + return _this; + } + /** @throws CommandError */ + VoteManager.prototype.start = function (player, newVote, data) { + var _this = this; + if (data === null) + (0, funcs_1.crash)("Cannot start vote: data not provided"); + if (!this.isEligible(player, data)) + (0, commands_1.fail)("You are not eligible for this vote."); + this.session = { + timer: Timer.schedule(function () { return _this._checkVote(false); }, this.voteTime / 1000), + votes: new Map(), + data: data, + }; + this.vote(player, newVote, data); + }; + /** @throws CommandError */ + VoteManager.prototype.vote = function (player, newVote, data) { + var _this = this; + if (!this.session) + return this.start(player, newVote, data); + if (!this.isEligible(player, this.session.data)) + (0, commands_1.fail)("You are not eligible for this vote."); + var oldVote = this.session.votes.get(player.uuid); + this.session.votes.set(player.uuid, newVote); + if (oldVote == null) + this.fire("player vote", [player, newVote]); + this.fire("player vote change", [player, oldVote !== null && oldVote !== void 0 ? oldVote : 0, newVote]); + if (Date.now() - globals_1.fishState.startTime < 3000) + Timer.schedule(function () { return _this._checkVote(false); }, 3); + else + this._checkVote(false); + }; + VoteManager.prototype.unvote = function (player) { + if (!this.session) + return; + var fishP = players_1.FishPlayer.resolve(player); + var vote = this.session.votes.get(fishP.uuid); + if (vote) { + this.session.votes.delete(fishP.uuid); + this.fire("player vote removed", [player, vote]); + this._checkVote(false); + } + }; + /** Does not fire the events used to display messages, please print one before calling this */ + VoteManager.prototype.forceVote = function (outcome) { + if (outcome) { + this.fire("success", [true]); + } + else { + this.fire("fail", [true]); + } + this.resetVote(); + }; + VoteManager.prototype.resetVote = function () { + if (this.session == null) + return; + this.session.timer.cancel(); + this.session = null; + }; + VoteManager.prototype.requiredVotes = function () { + var _this = this; + if (this.goal[0] == "absolute") { + return this.goal[1]; + } + else { + var numVoters = players_1.FishPlayer.getAllOnline().filter(function (p) { + return _this.isEligible(p, _this.session.data) && (_this.isCounted(p, _this.session.data) || _this.session.votes.has(p.uuid)); + }).length; + return Math.max(Math.ceil(this.goal[1] * numVoters), 1); + } + }; + VoteManager.prototype.currentVotes = function () { + var e_1, _a; + if (this.session) { + try { + for (var _b = __values(this.session.votes.keys()), _c = _b.next(); !_c.done; _c = _b.next()) { + var key = _c.value; + var fishP = players_1.FishPlayer.getById(key); + if (!this.isEligible(fishP, this.session.data)) + this.session.votes.delete(key); + } + } + catch (e_1_1) { e_1 = { error: e_1_1 }; } + finally { + try { + if (_c && !_c.done && (_a = _b.return)) _a.call(_b); + } + finally { if (e_1) throw e_1.error; } + } + return __spreadArray([], __read(this.session.votes), false).reduce(function (acc, _a) { + var _b = __read(_a, 2), k = _b[0], v = _b[1]; + return acc + v; + }, 0); + } + else + return 0; + }; + VoteManager.prototype.getEligibleVoters = function () { + var _this = this; + if (!this.session) + return []; + return players_1.FishPlayer.getAllOnline().filter(function (p) { + return _this.isEligible(p, _this.session.data); + }); + }; + VoteManager.prototype.messageEligibleVoters = function (message) { + this.getEligibleVoters().forEach(function (p) { return p.sendMessage(message); }); + }; + VoteManager.prototype._checkVote = function (end) { + var votes = this.currentVotes(); + var required = this.requiredVotes(); + if (votes >= required) { + this.fire("success", [false]); + this.fire("vote passed", [votes, required]); + this.resetVote(); + } + else if (end) { + this.fire("fail", [false]); + this.fire("vote failed", [votes, required]); + this.resetVote(); + } + }; + return VoteManager; +}(funcs_1.EventEmitter)); +exports.VoteManager = VoteManager;