Files
adventureland/docker/patch-multi-account-limits.js
T
anima d589dd4d64
build container / build (push) Failing after 2s
fix rewards and bypass ip limits
2026-11-02 02:49:23 +01:00

166 lines
6.3 KiB
JavaScript

#!/usr/bin/env node
// Local patch: make the multi-account limits configurable.
//
// Upstream hard-codes:
// - one merchant per IP and per owner,
// - three non-merchant characters per IP and per owner,
// and only checks some of those values at login.
//
// This patch reads the limits from options.js in both places:
// - is_player_allowed() (login, node/server_functions.js)
// - enforce_limitations() (backend cron, adventure_functions.js)
//
// New options (defaults match upstream):
// merchant_ip_limit: 1
// merchant_owner_limit: 1
// ip_limit: 3
// character_limit: 3
//
// The patch is idempotent and fails loudly if an anchor is missing.
const fs = require("node:fs");
const path = require("node:path");
const app = path.resolve(process.argv[2] || "/app");
const serverFunctionsFile = path.join(app, "node", "server_functions.js");
const adventureFunctionsFile = path.join(app, "adventure_functions.js");
function fail(message) {
console.error("patch-multi-account-limits: " + message);
process.exit(1);
}
function replaceOnce(source, from, to, label) {
const count = source.split(from).length - 1;
if (count !== 1) {
fail(label + ": expected 1 anchor, found " + count);
}
return source.replace(from, to);
}
function patchServerFunctions(source) {
const marker = "function get_multi_account_identity(player) {";
const oldMerchant =
'\tif (player.type == "merchant") {\n' +
"\t\tfor (var id in players) {\n" +
"\t\t\tif ((players[id].stones && 0) || id == player.socket.id || is_free(player)) {\n" +
"\t\t\t\tcontinue;\n" +
"\t\t\t}\n" +
'\t\t\tif (players[id].owner == player.owner && players[id].type == "merchant") {\n' +
"\t\t\t\treturn false;\n" +
"\t\t\t}\n" +
"\t\t}\n" +
"\t\treturn true;\n" +
"\t}";
if (source.includes(marker)) {
if (source.includes(oldMerchant)) {
fail("server_functions.js has a partial merchant patch");
}
console.log("patch-multi-account-limits: server_functions.js already patched");
return source;
}
source = replaceOnce(
source,
'function get_limit_identity(player) {\n\treturn player && player.mainframe === true ? "mainframe:" + player.owner : get_ip_server(player);\n}',
'function get_limit_identity(player) {\n\treturn player && player.mainframe === true ? "mainframe:" + player.owner : get_ip_server(player);\n}\n\n' +
"// Multi-account identity; mirrors the auth/temp_auth handling in enforce_limitations().\n" +
"function get_multi_account_identity(player) {\n" +
'\tif (player.temp_auth) return "owner:" + player.owner;\n' +
'\tif (player.auth_id) return "auth:" + player.auth_id;\n' +
"\treturn get_limit_identity(player);\n" +
"}",
"get_limit_identity",
);
const newMerchant =
"\t// Multi-account limits: configured in options.js; the backend cron uses the same values.\n" +
'\tif (player.type == "merchant") {\n' +
"\t\tvar merchant_limit = options.merchant_owner_limit === undefined ? 1 : options.merchant_owner_limit;\n" +
"\t\tvar merchant_ip_limit = options.merchant_ip_limit === undefined ? 1 : options.merchant_ip_limit;\n" +
"\t\tvar merchants = 0;\n" +
"\t\tvar merchant_ips = 0;\n" +
"\t\tvar merchant_identity = get_multi_account_identity(player);\n" +
"\t\tfor (var id in players) {\n" +
'\t\t\tif (id == player.socket.id || is_free(players[id]) || players[id].type != "merchant") {\n' +
"\t\t\t\tcontinue;\n" +
"\t\t\t}\n" +
"\t\t\tif (players[id].owner == player.owner) {\n" +
"\t\t\t\tmerchants++;\n" +
"\t\t\t}\n" +
"\t\t\tif (get_multi_account_identity(players[id]) == merchant_identity) {\n" +
"\t\t\t\tmerchant_ips++;\n" +
"\t\t\t}\n" +
"\t\t}\n" +
"\t\tif (merchants >= merchant_limit || merchant_ips >= merchant_ip_limit) {\n" +
"\t\t\treturn false;\n" +
"\t\t}\n" +
"\t\treturn true;\n" +
"\t}";
source = replaceOnce(source, oldMerchant, newMerchant, "merchant branch");
return source;
}
function patchAdventureFunctions(source) {
const marker = "var merchant_ip_limit = options.merchant_ip_limit === undefined ? 1 : options.merchant_ip_limit;";
const oldMerchant =
'\t\t\t\tif (player.type === "merchant" && (mips[player.ip] > 1 || mowners[player.owner] > 1) && to_disconnect.indexOf(player.name) === -1) {';
const oldOther =
'\t\t\t\tif (player.type !== "merchant" && (ips[player.ip] > 3 * (ipx[player.ip] || 1) || owners[player.owner] > 3) && to_disconnect.indexOf(player.name) === -1) {';
if (source.includes(marker)) {
if (source.includes(oldMerchant) || source.includes(oldOther)) {
fail("adventure_functions.js has a partial limits patch");
}
console.log("patch-multi-account-limits: adventure_functions.js already patched");
return source;
}
source = replaceOnce(
source,
"\t\t\tvar to_disconnect = [];",
"\t\t\tvar to_disconnect = [];\n" +
"\t\t\tvar merchant_ip_limit = options.merchant_ip_limit === undefined ? 1 : options.merchant_ip_limit;\n" +
"\t\t\tvar merchant_owner_limit = options.merchant_owner_limit === undefined ? 1 : options.merchant_owner_limit;\n" +
"\t\t\tvar ip_limit = options.ip_limit === undefined ? 3 : options.ip_limit;\n" +
"\t\t\tvar character_limit = options.character_limit === undefined ? 3 : options.character_limit;",
"limit variables",
);
source = replaceOnce(
source,
oldMerchant,
"\t\t\t\tif (\n" +
'\t\t\t\t\tplayer.type === "merchant" &&\n' +
"\t\t\t\t\t(mips[player.ip] > merchant_ip_limit || mowners[player.owner] > merchant_owner_limit) &&\n" +
"\t\t\t\t\tto_disconnect.indexOf(player.name) === -1\n" +
"\t\t\t\t) {",
"merchant condition",
);
source = replaceOnce(
source,
oldOther,
"\t\t\t\tif (\n" +
'\t\t\t\t\tplayer.type !== "merchant" &&\n' +
"\t\t\t\t\t(ips[player.ip] > ip_limit * (ipx[player.ip] || 1) || owners[player.owner] > character_limit) &&\n" +
"\t\t\t\t\tto_disconnect.indexOf(player.name) === -1\n" +
"\t\t\t\t) {",
"non-merchant condition",
);
return source;
}
let serverFunctions = fs.readFileSync(serverFunctionsFile, "utf8");
serverFunctions = patchServerFunctions(serverFunctions);
fs.writeFileSync(serverFunctionsFile, serverFunctions);
let adventureFunctions = fs.readFileSync(adventureFunctionsFile, "utf8");
adventureFunctions = patchAdventureFunctions(adventureFunctions);
fs.writeFileSync(adventureFunctionsFile, adventureFunctions);
console.log("patch-multi-account-limits: wrote " + serverFunctionsFile + " and " + adventureFunctionsFile);