Remove Dead Ops Arcade support
This commit is contained in:
@@ -4,6 +4,8 @@ Open **Fleet → Autoscaling** to keep empty lobbies available for new groups. P
|
|||||||
|
|
||||||
Autoscaling is opt-in. Installing the feature creates no policies and changes no lobby counts. Use a two-week dry run before enabling live actions.
|
Autoscaling is opt-in. Installing the feature creates no policies and changes no lobby counts. Use a two-week dry run before enabling live actions.
|
||||||
|
|
||||||
|
T5 game-wide policies cover the ten standard Zombies maps. Dead Ops Arcade is not supported.
|
||||||
|
|
||||||
## Configure a policy
|
## Configure a policy
|
||||||
|
|
||||||
Choose **Add policy**, select the game and scope, and enter an explicit maximum. The initial editor selects Dry run, with a minimum of one running lobby, one empty spare, a 20-minute empty interval, and a five-minute action cooldown. Counts must be between 1 and 20; the minimum and spare target cannot exceed the maximum.
|
Choose **Add policy**, select the game and scope, and enter an explicit maximum. The initial editor selects Dry run, with a minimum of one running lobby, one empty spare, a 20-minute empty interval, and a five-minute action cooldown. Counts must be between 1 and 20; the minimum and spare target cannot exceed the maximum.
|
||||||
|
|||||||
@@ -27,7 +27,6 @@ export const T5_ZOMBIE_MAPS: readonly T5MapEntry[] = Object.freeze([
|
|||||||
{ friendly: 'Shangri-La', slug: 'temple', canonical: 'zombie_temple' },
|
{ friendly: 'Shangri-La', slug: 'temple', canonical: 'zombie_temple' },
|
||||||
{ friendly: 'Moon', slug: 'moon', canonical: 'zombie_moon' },
|
{ friendly: 'Moon', slug: 'moon', canonical: 'zombie_moon' },
|
||||||
{ friendly: 'Five', slug: 'pentagon', canonical: 'zombie_pentagon' },
|
{ friendly: 'Five', slug: 'pentagon', canonical: 'zombie_pentagon' },
|
||||||
{ friendly: 'Dead Ops Arcade', slug: 'zombietron', canonical: 'zombietron' },
|
|
||||||
{ friendly: 'Nacht Der Untoten', slug: 'prototype', canonical: 'zombie_cod5_prototype' },
|
{ friendly: 'Nacht Der Untoten', slug: 'prototype', canonical: 'zombie_cod5_prototype' },
|
||||||
{ friendly: 'Verrückt', slug: 'asylum', canonical: 'zombie_cod5_asylum' },
|
{ friendly: 'Verrückt', slug: 'asylum', canonical: 'zombie_cod5_asylum' },
|
||||||
{ friendly: 'Shi No Numa', slug: 'sumpf', canonical: 'zombie_cod5_sumpf' },
|
{ friendly: 'Shi No Numa', slug: 'sumpf', canonical: 'zombie_cod5_sumpf' },
|
||||||
|
|||||||
@@ -10,6 +10,18 @@ beforeEach(async () => { vi.resetModules(); fixture = await newFixture([]); });
|
|||||||
afterEach(async () => { await fixture.cleanup(); });
|
afterEach(async () => { await fixture.cleanup(); });
|
||||||
const settings = { game: 't6', mapSlug: '*', maximum: 2, mode: 'create-only' };
|
const settings = { game: 't6', mapSlug: '*', maximum: 2, mode: 'create-only' };
|
||||||
const maps = zombieMaps('t6');
|
const maps = zombieMaps('t6');
|
||||||
|
|
||||||
|
it('satisfies a T5 game-wide policy with the ten standard Zombies maps', () => {
|
||||||
|
const now = 2_000_000;
|
||||||
|
const policy: ScalingPolicy = { ...scalingSettings({ ...settings, game: 't5' }),
|
||||||
|
id: 'policy', revision: 1, nextActionAt: 0, pausedReason: null, updatedAt: '' };
|
||||||
|
const members: ScalingMember[] = zombieMaps('t5').map(map => ({ name: `t5_zombie_${map.slug}_1`, mapSlug: map.slug,
|
||||||
|
identity: map.slug, state: 'empty', players: 0, observedAt: now, source: 'rcon', emptySince: 1, blocker: null, operationId: null }));
|
||||||
|
const snapshot = { members, reservedNames: members.map(m => m.name), keyPool: { total: 20, available: 10 }, blockers: [] };
|
||||||
|
expect(planScaling(policy, snapshot, now)).toMatchObject({ action: 'wait', server: null, desired: 10, deficit: 0, empty: 10 });
|
||||||
|
expect(() => scalingSettings({ ...settings, game: 't5', mapSlug: 'zombietron' })).toThrow('supported game and map');
|
||||||
|
});
|
||||||
|
|
||||||
async function setup() {
|
async function setup() {
|
||||||
await prepareNativeHost();
|
await prepareNativeHost();
|
||||||
const { db } = await import('../../src/lib/server/db/index.js');
|
const { db } = await import('../../src/lib/server/db/index.js');
|
||||||
|
|||||||
@@ -89,7 +89,7 @@ describe('matching a native collection', () => {
|
|||||||
const { saveBlueprint } = await import('../../src/lib/server/fleet/launch-recipes.js');
|
const { saveBlueprint } = await import('../../src/lib/server/fleet/launch-recipes.js');
|
||||||
const { previewCollection, queueCollection, getCollection } = await import('../../src/lib/server/fleet/collections.js');
|
const { previewCollection, queueCollection, getCollection } = await import('../../src/lib/server/fleet/collections.js');
|
||||||
const { T5_ZOMBIE_MAPS } = await import('../../src/lib/map-aliases.js');
|
const { T5_ZOMBIE_MAPS } = await import('../../src/lib/map-aliases.js');
|
||||||
const blueprint = saveBlueprint({ name: 'Replace nine with ten', entries: T5_ZOMBIE_MAPS.filter(map => map.slug !== 'zombietron')
|
const blueprint = saveBlueprint({ name: 'Replace nine with ten', entries: T5_ZOMBIE_MAPS
|
||||||
.map(map => ({ game: 't5', mapSlug: map.slug, count: 1 })) });
|
.map(map => ({ game: 't5', mapSlug: map.slug, count: 1 })) });
|
||||||
const preview = previewCollection(blueprint.id, 'exact');
|
const preview = previewCollection(blueprint.id, 'exact');
|
||||||
const queued = queueCollection({ previewId: preview.id,
|
const queued = queueCollection({ previewId: preview.id,
|
||||||
@@ -137,7 +137,7 @@ describe('matching a native collection', () => {
|
|||||||
const { GET: fleetResponse } = await import('../../src/routes/api/servers/+server.js');
|
const { GET: fleetResponse } = await import('../../src/routes/api/servers/+server.js');
|
||||||
const { POST: agentHeartbeat } = await import('../../src/routes/api/agent/heartbeat/+server.js');
|
const { POST: agentHeartbeat } = await import('../../src/routes/api/agent/heartbeat/+server.js');
|
||||||
const { T5_ZOMBIE_MAPS } = await import('../../src/lib/map-aliases.js');
|
const { T5_ZOMBIE_MAPS } = await import('../../src/lib/map-aliases.js');
|
||||||
const blueprint = saveBlueprint({ name: 'Match ten T5 lobbies', entries: T5_ZOMBIE_MAPS.filter(map => map.slug !== 'zombietron')
|
const blueprint = saveBlueprint({ name: 'Match ten T5 lobbies', entries: T5_ZOMBIE_MAPS
|
||||||
.map(map => ({ game: 't5', mapSlug: map.slug, count: 1 })) });
|
.map(map => ({ game: 't5', mapSlug: map.slug, count: 1 })) });
|
||||||
const preview = previewCollection(blueprint.id, 'exact');
|
const preview = previewCollection(blueprint.id, 'exact');
|
||||||
expect(preview).toMatchObject({ ready: true, mode: 'exact' });
|
expect(preview).toMatchObject({ ready: true, mode: 'exact' });
|
||||||
|
|||||||
@@ -106,8 +106,8 @@ describe('automatic map selection', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('appends the rotation when absent and preserves CRLF', () => {
|
it('appends the rotation when absent and preserves CRLF', () => {
|
||||||
expect(composeServerCfg('set sv_voice "1"\r\n', [], 't5', 'zombietron'))
|
expect(composeServerCfg('set sv_voice "1"\r\n', [], 't5', 'zombie_moon'))
|
||||||
.toBe('set sv_voice "1"\r\n// Map selected by Electric Cherry\r\nset sv_maprotation "map zombietron"\r\n');
|
.toBe('set sv_voice "1"\r\n// Map selected by Electric Cherry\r\nset sv_maprotation "map zombie_moon"\r\n');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('removes conflicting duplicate assignments regardless of case or command form', () => {
|
it('removes conflicting duplicate assignments regardless of case or command form', () => {
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import { describe, expect, it } from 'vitest';
|
import { describe, expect, it } from 'vitest';
|
||||||
import {
|
import {
|
||||||
parseServerName,
|
parseServerName,
|
||||||
|
selectedZombieMap,
|
||||||
t5ZombieMapBySlug,
|
t5ZombieMapBySlug,
|
||||||
T5_ZOMBIE_MAPS,
|
T5_ZOMBIE_MAPS,
|
||||||
T6_ZOMBIE_MAPS
|
T6_ZOMBIE_MAPS
|
||||||
@@ -98,6 +99,12 @@ describe('parseServerName (new naming: t5_<mode>_<slug>_<n>)', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
describe('t5ZombieMapBySlug', () => {
|
describe('t5ZombieMapBySlug', () => {
|
||||||
|
it('excludes Dead Ops Arcade from supported map selection', () => {
|
||||||
|
expect(t5ZombieMapBySlug('zombietron')).toBeUndefined();
|
||||||
|
expect(selectedZombieMap('t5', 'zombietron')).toBeUndefined();
|
||||||
|
expect(parseServerName('t5_zombie_zombietron_1')?.mapCanonical).toBeNull();
|
||||||
|
});
|
||||||
|
|
||||||
it('resolves each catalog slug to its canonical + friendly', () => {
|
it('resolves each catalog slug to its canonical + friendly', () => {
|
||||||
for (const entry of T5_ZOMBIE_MAPS) {
|
for (const entry of T5_ZOMBIE_MAPS) {
|
||||||
const found = t5ZombieMapBySlug(entry.slug);
|
const found = t5ZombieMapBySlug(entry.slug);
|
||||||
|
|||||||
Reference in New Issue
Block a user