Update Healer Man server

This commit is contained in:
phenom committed 2026-09-19 21:01:51 -04:00
1 parent f8314e7de1
commit 1fedb9368d
76 files changed
+20556 -2523

No files matched your search

+32 -2
View File
@@ -27,6 +27,8 @@ const ARTIFACTS = new Map([
["party_configurations.csv", "text/csv; charset=utf-8"],
["class_specs_across_parties.csv", "text/csv; charset=utf-8"],
["class_abilities_across_parties.csv", "text/csv; charset=utf-8"],
["npc_class_specs_across_parties.csv", "text/csv; charset=utf-8"],
["npc_class_abilities_across_parties.csv", "text/csv; charset=utf-8"],
["class_spec_by_boss.csv", "text/csv; charset=utf-8"],
["bosses_across_parties.csv", "text/csv; charset=utf-8"],
["boss_by_party.csv", "text/csv; charset=utf-8"],
@@ -55,6 +57,9 @@ export function normalizeBalanceLabJobSpec(input) {
if (!SPEC_SELECTIONS.has(specSelection)) {
throw new GameDatabaseError("Class/spec selection must be random, all, or specific.", 400, "invalid_balance_lab_job");
}
if (input?.includeBetaSpecs !== undefined && typeof input.includeBetaSpecs !== "boolean") {
throw new GameDatabaseError("Include Beta Specs must be boolean.", 400, "invalid_balance_lab_job");
}
const rawBossKeys = Array.isArray(input?.bossKeys) ? input.bossKeys : [];
if (rawBossKeys.length > 500) {
throw new GameDatabaseError("Select no more than 500 bosses.", 400, "invalid_balance_lab_job");
@@ -85,6 +90,8 @@ export function normalizeBalanceLabJobSpec(input) {
partySeed: integer(input?.partySeed ?? 1, "Party seed", -2_147_483_648, 2_147_483_647),
bossCategory,
bossKeys: Object.freeze(bossKeys),
seatSweep: input?.seatSweep === true,
includeBetaSpecs: input?.includeBetaSpecs === true,
});
const sourceCount = spec.source === "all" ? 3 : 1;
const bossEstimate = Math.max(1, spec.bossKeys.length || (spec.bossCategory === "all" ? 500 : 300));
@@ -93,7 +100,7 @@ export function normalizeBalanceLabJobSpec(input) {
: spec.specSelection === "specific"
? Math.max(spec.partyConfigurations, spec.selectedBuildIds.length)
: Math.max(spec.partyConfigurations, spec.source === "all" ? 100 : 50);
const estimatedRuns = partyEstimate * spec.simulationsPerParty * bossEstimate;
const estimatedRuns = partyEstimate * (spec.seatSweep ? 5 : 1) * spec.simulationsPerParty * bossEstimate;
if (estimatedRuns > 250_000) {
throw new GameDatabaseError(
"That job is too large for one server request. Reduce parties, simulations, or selected bosses.",
@@ -136,6 +143,10 @@ export function balanceLabCommand(spec, outputDir, options = {}) {
];
for (const buildId of spec.selectedBuildIds) args.push("--spec", buildId);
for (const bossKey of spec.bossKeys) args.push("--boss", bossKey);
if (spec.seatSweep) args.push("--seat-sweep");
if (spec.includeBetaSpecs) args.push("--include-beta-specs");
if (options.cooldownPolicyPath) args.push("--cooldown-policy", options.cooldownPolicyPath);
if (options.readinessPolicyPath) args.push("--companion-readiness-policy", options.readinessPolicyPath);
return { executable, args, cwd: projectRoot };
}
@@ -198,6 +209,8 @@ function reportSummary(outputDir) {
runs: Number(summary.runs) || 0,
classSpecs: Number(summary.classSpecs) || 0,
classAbilities: Number(summary.classAbilities) || 0,
npcClassSpecs: Number(summary.npcClassSpecs) || 0,
npcClassAbilities: Number(summary.npcClassAbilities) || 0,
bossPressureMedianDtps: Number(summary.bossPressureMedianDtps) || 0,
};
} catch {
@@ -206,9 +219,10 @@ function reportSummary(outputDir) {
}
function expectedPartyConfigurations(spec) {
return spec.specSelection === "random"
const parties = spec.specSelection === "random"
? spec.partyConfigurations * (spec.source === "all" ? 3 : 1)
: spec.partyConfigurations;
return parties * (spec.seatSweep ? 5 : 1);
}
function lineCollector(onLine) {
@@ -249,6 +263,8 @@ export function createBalanceLabJobService(options = {}) {
? Math.min(200, Math.floor(configuredQueueLimit))
: 100;
const launch = options.spawnProcess ?? spawn;
const cooldownPolicyProvider = options.cooldownPolicyProvider;
const readinessPolicyProvider = options.readinessPolicyProvider;
const pythonExecutable = String(options.pythonExecutable
?? process.env.BALANCE_LAB_PYTHON
?? (process.platform === "win32" ? "python" : "python3"));
@@ -390,6 +406,8 @@ export function createBalanceLabJobService(options = {}) {
const command = balanceLabCommand(job.spec, job.outputDir, {
projectRoot,
pythonExecutable,
cooldownPolicyPath: job.cooldownPolicyPath,
readinessPolicyPath: job.readinessPolicyPath,
});
const child = launch(command.executable, command.args, {
cwd: command.cwd,
@@ -467,6 +485,16 @@ export function createBalanceLabJobService(options = {}) {
const id = randomUUID();
const outputDir = path.join(jobsDir, id);
mkdirSync(outputDir, { recursive: true });
const cooldownPolicyPath = path.join(outputDir, "companion-cooldown-policy.json");
if (cooldownPolicyProvider) {
const published = cooldownPolicyProvider();
writeFileSync(cooldownPolicyPath, `${JSON.stringify(published.config ?? published, null, 2)}\n`, "utf8");
}
const readinessPolicyPath = path.join(outputDir, "companion-spec-readiness.json");
if (readinessPolicyProvider) {
const published = readinessPolicyProvider();
writeFileSync(readinessPolicyPath, `${JSON.stringify(published, null, 2)}\n`, "utf8");
}
const totalParties = expectedPartyConfigurations(spec);
const job = {
id,
@@ -488,6 +516,8 @@ export function createBalanceLabJobService(options = {}) {
logTail: [],
summary: null,
outputDir,
cooldownPolicyPath: cooldownPolicyProvider ? cooldownPolicyPath : null,
readinessPolicyPath: readinessPolicyProvider ? readinessPolicyPath : null,
};
jobs.set(id, job);
persistJob(job);