Fix: Starts meiden hochfahrende Hosts, Messerrunde zählt nicht als Map-Ende
- Neue Server meiden Hosts, auf denen gerade ein Container per SteamCMD lädt (Warteschlange im Prozess und laufende Starts laut Datenbank). Vorher warteten alle Starts hinter einem langen Download, obwohl andere Hosts frei waren. - Vor dem Einrichten des Matches wird eine laufende Messerrunde in die Aufwärmphase gesetzt, und Logzeilen zählen erst, wenn die Map eingerichtet ist. Vorher wurde das Ende der Messerrunde als Ende von Map 1 gewertet und die nächste Map automatisch geladen.
This commit is contained in:
1 parent
148cd99856
commit
d36dcacd39
6 files changed
+83
-8
No files matched your search
@@ -141,6 +141,8 @@ export class DockerServerAdapter {
|
||||
this.runDocker = runDocker; // nur für Tests
|
||||
this.lanMode = !options.gslt;
|
||||
this.startQueue = Promise.resolve();
|
||||
// Starts, die gerade warten oder deren Container noch SteamCMD laufen lässt
|
||||
this.pendingStarts = 0;
|
||||
if (!options.host) throw new Error('SERVER_ADAPTER=docker benötigt CS2_PUBLIC_HOST');
|
||||
}
|
||||
|
||||
@@ -169,8 +171,13 @@ export class DockerServerAdapter {
|
||||
async start(args) {
|
||||
// In die Warteschlange: erst starten, wenn der vorige Container seinen Update-Check hinter sich hat.
|
||||
const previous = this.startQueue;
|
||||
let release;
|
||||
this.startQueue = new Promise((resolve) => { release = resolve; });
|
||||
let releaseQueue;
|
||||
this.startQueue = new Promise((resolve) => { releaseQueue = resolve; });
|
||||
this.pendingStarts += 1;
|
||||
const release = () => {
|
||||
this.pendingStarts -= 1;
|
||||
releaseQueue();
|
||||
};
|
||||
await previous;
|
||||
let result;
|
||||
try {
|
||||
@@ -368,10 +375,11 @@ export class DockerServerAdapter {
|
||||
* immer an denselben Host. Ohne CS2_DOCKER_HOSTS gibt es genau einen Host: den bisherigen.
|
||||
*/
|
||||
export class DockerHostPool {
|
||||
constructor(options, { getUsedPorts, runDocker } = {}) {
|
||||
constructor(options, { getUsedPorts, getStarting, runDocker } = {}) {
|
||||
this.name = 'docker';
|
||||
this.simulated = false;
|
||||
this.depsGetUsedPorts = getUsedPorts;
|
||||
this.depsGetStarting = getStarting;
|
||||
this.runDocker = runDocker;
|
||||
this.options = options;
|
||||
this.diskCache = new Map();
|
||||
@@ -594,19 +602,31 @@ export class DockerHostPool {
|
||||
return new Set(this.files.running ? this.files.results.filter((r) => r.state === 'running').map((r) => r.name) : []);
|
||||
}
|
||||
|
||||
pickHost() {
|
||||
/**
|
||||
* Wie viele Server auf einem Host gerade hochfahren: Starts in der Warteschlange dieses Prozesses, und
|
||||
* (nach einem Neustart der App) Server, die laut Datenbank noch starten. Ein Container lädt beim Start
|
||||
* per SteamCMD, nach einem großen CS2-Update dauert das lange; weitere Starts dort müssten so lange warten.
|
||||
*/
|
||||
startingOn(host, exclude) {
|
||||
const queued = this.adapters.get(host.name)?.pendingStarts ?? 0;
|
||||
const stored = this.depsGetStarting?.(host.publicHost, exclude) ?? 0;
|
||||
return Math.max(queued, stored);
|
||||
}
|
||||
|
||||
pickHost(exclude) {
|
||||
const updating = this.updatingHosts();
|
||||
const best = this.hosts
|
||||
.filter((h) => !updating.has(h.name))
|
||||
.map((h) => ({ host: h, free: this.freeSlots(h) }))
|
||||
.map((h) => ({ host: h, free: this.freeSlots(h), starting: this.startingOn(h, exclude) }))
|
||||
.filter((x) => x.free > 0)
|
||||
.sort((a, b) => b.free - a.free)[0];
|
||||
// Zuerst Hosts, auf denen nichts hochfährt, dann die mit den meisten freien Plätzen
|
||||
.sort((a, b) => a.starting - b.starting || b.free - a.free)[0];
|
||||
if (!best) throw new Error(updating.size ? 'Kein Docker-Host frei, auf den anderen werden gerade die Spieldateien aktualisiert' : 'Kein Docker-Host mit freien Ports');
|
||||
return best.host;
|
||||
}
|
||||
|
||||
async start(args) {
|
||||
return this.adapters.get(this.pickHost().name).start(args);
|
||||
return this.adapters.get(this.pickHost(args?.matchId).name).start(args);
|
||||
}
|
||||
|
||||
adapterFor(handle) {
|
||||
|
||||
@@ -258,6 +258,14 @@ export class ServerManager {
|
||||
return result;
|
||||
}
|
||||
|
||||
/** Server (Matches und Messer-Server), die auf einem Host gerade hochfahren. `exclude`: der, der gerade startet. */
|
||||
startingOn(host, exclude = null) {
|
||||
return this.db.prepare(`
|
||||
SELECT (SELECT COUNT(*) FROM servers WHERE host = @host AND status = 'starting' AND handle IS NOT NULL AND match_id IS NOT @exclude)
|
||||
+ (SELECT COUNT(*) FROM knife_servers WHERE host = @host AND status = 'starting' AND handle IS NOT NULL AND id IS NOT @exclude) AS n
|
||||
`).get({ host, exclude }).n;
|
||||
}
|
||||
|
||||
/** Ports, die auf einem bestimmten Docker-Host belegt sind. */
|
||||
usedPortsOn(host) {
|
||||
const used = new Set();
|
||||
@@ -500,7 +508,12 @@ export class ServerManager {
|
||||
this.simulator.stop(matchId);
|
||||
this.service.resetLiveScore(matchId, seriesMap.number);
|
||||
// Lief vorher die Messerrunde auf diesem Server, deren Einstellungen zurücknehmen
|
||||
if (row.knife_at) plan.afterMapChange = [...KNIFE_UNDO_COMMANDS, ...plan.afterMapChange];
|
||||
if (row.knife_at) {
|
||||
// Eine noch laufende Messerrunde zuerst anhalten: sonst endet sie (mp_maxrounds 1) mitten im Einrichten,
|
||||
// und ihr Ergebnis würde als Ende der ersten Map gezählt
|
||||
plan.beforeMapChange = ['mp_warmup_pausetimer 1', 'mp_warmup_start', ...plan.beforeMapChange];
|
||||
plan.afterMapChange = [...KNIFE_UNDO_COMMANDS, ...plan.afterMapChange];
|
||||
}
|
||||
try {
|
||||
await this.adapter.configure(JSON.parse(row.handle), { map: seriesMap.map, ...plan });
|
||||
} catch (err) {
|
||||
|
||||
Reference in new issue
Block a user