fix: Fixing the MP power bonus + seed maintenance

This commit is contained in:
gauvainboiche
2026-04-03 14:25:19 +02:00
parent b11446cf56
commit d345c025c0
11 changed files with 349 additions and 132 deletions
+63 -16
View File
@@ -7,21 +7,49 @@ const __dirname = path.dirname(fileURLToPath(import.meta.url));
const CONFIG_FILE_PATH =
process.env.CONFIG_FILE_PATH ?? path.join(__dirname, "..", "config", "game.settings.json");
/** @type {{ dailyActionQuota: number, teamActionQuota: number, databaseWipeoutIntervalSeconds: number, configReloadIntervalSeconds: number, resourceWorth: object, militaryPower: object }} */
const VALID_RESET_HOURS = new Set([1, 2, 3, 4, 6, 8, 12, 24]);
const EPOCH_FILE_PATH =
process.env.EPOCH_FILE_PATH ?? path.join(path.dirname(CONFIG_FILE_PATH), ".timing_epoch");
let cached = {
dailyActionQuota: 100,
teamActionQuota: 100,
actionsResetIntervalHours: 12,
databaseWipeoutIntervalSeconds: 21600,
actionsResetIntervalSeconds: 3600,
databaseWipeoutIntervalCycles: 6,
configReloadIntervalSeconds: 30,
elementWorth: {},
resourceWorth: { common: {}, rare: {} },
militaryPower: {},
};
let lastMtimeMs = 0;
/** Raw JSON content from the last successful read (for change detection). */
let lastRawContent = "";
/** Unix seconds marking the origin of all timing slot calculations. */
let timingEpochSec = 0;
/** Signature of timing-relevant values from the last successful load. */
let lastTimingSig = "";
function timingSignature() {
return `${cached.actionsResetIntervalSeconds}:${cached.databaseWipeoutIntervalCycles}`;
}
function loadTimingEpoch() {
try {
const raw = fs.readFileSync(EPOCH_FILE_PATH, "utf8").trim();
const v = Number(raw);
if (Number.isFinite(v) && v > 0) return v;
} catch { /* ignore */ }
return 0;
}
function saveTimingEpoch(sec) {
try {
fs.writeFileSync(EPOCH_FILE_PATH, String(sec), "utf8");
} catch (e) {
console.error("[config] Could not persist timing epoch:", e.message);
}
}
function parseBool(v) {
if (typeof v === "boolean") return v;
@@ -35,11 +63,13 @@ function parseBool(v) {
export function loadConfigFile() {
try {
const st = fs.statSync(CONFIG_FILE_PATH);
if (st.mtimeMs === lastMtimeMs) {
// Always read the file — mtime is unreliable on Docker bind mounts (Windows).
// The file is tiny and polled every ~30 s, so the cost is negligible.
const raw = fs.readFileSync(CONFIG_FILE_PATH, "utf8");
if (raw === lastRawContent) {
return cached;
}
const raw = fs.readFileSync(CONFIG_FILE_PATH, "utf8");
lastRawContent = raw;
const j = JSON.parse(raw);
if (typeof j.dailyActionQuota === "number" && j.dailyActionQuota >= 1) {
cached.dailyActionQuota = Math.floor(j.dailyActionQuota);
@@ -47,12 +77,11 @@ export function loadConfigFile() {
if (typeof j.teamActionQuota === "number" && j.teamActionQuota >= 1) {
cached.teamActionQuota = Math.floor(j.teamActionQuota);
}
if (typeof j.actionsResetIntervalHours === "number") {
const v = Math.floor(j.actionsResetIntervalHours);
if (VALID_RESET_HOURS.has(v)) cached.actionsResetIntervalHours = v;
if (typeof j.actionsResetIntervalSeconds === "number" && j.actionsResetIntervalSeconds >= 1) {
cached.actionsResetIntervalSeconds = Math.floor(j.actionsResetIntervalSeconds);
}
if (typeof j.databaseWipeoutIntervalSeconds === "number" && j.databaseWipeoutIntervalSeconds >= 60) {
cached.databaseWipeoutIntervalSeconds = j.databaseWipeoutIntervalSeconds;
if (typeof j.databaseWipeoutIntervalCycles === "number" && j.databaseWipeoutIntervalCycles >= 1) {
cached.databaseWipeoutIntervalCycles = Math.floor(j.databaseWipeoutIntervalCycles);
}
if (typeof j.configReloadIntervalSeconds === "number" && j.configReloadIntervalSeconds >= 5) {
cached.configReloadIntervalSeconds = j.configReloadIntervalSeconds;
@@ -64,10 +93,25 @@ export function loadConfigFile() {
cached.resourceWorth = j.resourceWorth;
} if (j.militaryPower && typeof j.militaryPower === 'object') {
cached.militaryPower = j.militaryPower;
} lastMtimeMs = st.mtimeMs;
}
// Detect timing changes and reset the epoch when they happen
const newSig = timingSignature();
if (!timingEpochSec) {
// First load: try to restore from disk
timingEpochSec = loadTimingEpoch();
}
if (!timingEpochSec || (lastTimingSig && newSig !== lastTimingSig)) {
// No persisted epoch, or timing values changed → new epoch
timingEpochSec = Math.floor(Date.now() / 1000);
saveTimingEpoch(timingEpochSec);
if (lastTimingSig) {
console.log(`[config] Timing values changed (${lastTimingSig}${newSig}), epoch reset to ${timingEpochSec}`);
}
}
lastTimingSig = newSig;
} catch (e) {
if (e.code === "ENOENT") {
lastMtimeMs = 0;
lastRawContent = "";
} else {
console.error("[config]", e.message);
}
@@ -76,7 +120,10 @@ export function loadConfigFile() {
}
export function getConfig() {
return { ...cached };
const c = { ...cached };
c.databaseWipeoutIntervalSeconds = c.actionsResetIntervalSeconds * c.databaseWipeoutIntervalCycles;
c.timingEpochSec = timingEpochSec;
return c;
}
export function getConfigFilePath() {