Count occupied restarts as failed missions; sync published Workshop version
continuous-integration/drone/push Build is passing

This commit is contained in:
2026-11-07 01:05:43 +01:00
parent 161100cf08
commit e421dcfc15
14 changed files with 640 additions and 50 deletions
+3
View File
@@ -287,6 +287,9 @@ snapshots only. Spectators and temporary connection IDs are excluded. A mission
victory does not imply a personal escape. Startup failures, empty restarts and
interrupted sessions do not count as gameplay wins/losses or player attempts.
An interrupted mission can still have raw combat data in its detail snapshot.
New clients report occupied restarts as `defeat`, including recovery from the
last durable occupied checkpoint after a forced stop. Those explicit final
events count as failures and participant attempts; restart links alone do not.
Legacy missing counters remain null, with `missionsWithKills`,
`missionsWithFriendlyKills`, `missionsWithDeaths`, `missionsWithDowned` and
@@ -110,6 +110,32 @@ test('published fractional-duration defeats are accepted, counted and retry idem
assert.equal((await ingest(event({connectedPlayers: 0.5}))).status, 400);
});
test('occupied restart recovery counts one failure and retains the old mission participants', async () => {
const terrain = 'Restart recovery fixture';
const player = {...event().players[0], escaped: false};
const recovered = event({terrain, phase: 6, outcome: 'defeat', players: [player],
reason: 'Mission interrupted by a restart while players were connected.'});
const snapshot = {...recovered, eventId: crypto.randomUUID(), type: 'heartbeat', phase: 3, outcome: ''};
assert.equal((await ingest(snapshot)).status, 201);
const first = await ingest(recovered);
assert.equal(first.status, 201, await first.clone().text());
assert.equal((await ingest(recovered)).status, 200);
// A delayed pre-restart snapshot must not reopen the completed mission.
assert.equal((await ingest({...snapshot, eventId: crypto.randomUUID(), elapsedSeconds: 150})).status, 201);
const next = event({terrain, type: 'mission_restarted', outcome: '', phase: 0,
previousMissionId: recovered.missionId, previousOutcome: 'defeat', players: [], connectedPlayers: 0});
assert.equal((await ingest(next)).status, 201);
const summary = (await (await ingest(recovered)).json()).community;
assert.equal(summary.allServers.losses, 1);
assert.equal(summary.allServers.completed, 1);
const old = await detail(recovered);
assert.equal(old.outcome, 'defeat');
assert.equal(old.players[0].id, player.id);
assert.equal(old.players[0].participated, true);
assert.equal(old.players[0].escaped, false);
assert.equal((await detail(next)).outcome, '');
});
test('out-of-order snapshots and second terminal reports cannot reopen or double count a mission', async () => {
const e = event({players: []});
const heartbeat = {...e, type: 'heartbeat', phase: 4, outcome: '', elapsedSeconds: 100};