Server im LAN-Reiter des Steam-Serverbrowsers: LAN-Ports, Host-Netz, Teamnamen, CSTV +100
This commit is contained in:
1 parent
4521cdae72
commit
0a40688945
14 files changed
+269
-37
No files matched your search
@@ -39,7 +39,7 @@ test('Ohne GSLT startet der Container im LAN-Modus, mit GSLT mit Token', async (
|
||||
assert.equal(env.CS2_MAXPLAYERS, '6', '4 Spieler + CSTV + Reserve');
|
||||
assert.equal(env.CS2_ADDITIONAL_ARGS, '-allow_round_backup_files', 'Rundensicherung für Rollback');
|
||||
assert.equal(env.CS2_PORT, '27100');
|
||||
assert.equal(env.TV_PORT, '27101');
|
||||
assert.equal(env.TV_PORT, '27200');
|
||||
assert.ok(runArgs.includes('cs2-mapveto-gamefiles:/home/steam/cs2-dedicated'));
|
||||
assert.equal(r.host, '192.168.178.111');
|
||||
|
||||
@@ -74,7 +74,7 @@ test('Zweiter Container startet erst, wenn der erste seinen Update-Check abgesch
|
||||
d.finishUpdate(first.handle.container);
|
||||
const r2 = await second;
|
||||
assert.equal(d.calls.filter((c) => c[0] === 'run').length, 2);
|
||||
assert.equal(r2.port, 27102, 'zweiter Container bekommt die nächsten Ports');
|
||||
assert.equal(r2.port, 27101, 'zweiter Container bekommt den nächsten Port');
|
||||
d.finishUpdate(r2.handle.container);
|
||||
});
|
||||
|
||||
|
||||
@@ -81,11 +81,11 @@ test('Alte Handles ohne Host-Namen laufen auf dem ersten Host', () => {
|
||||
assert.equal(p.adapterFor({ container: 'x' }), p.adapters.get('a'));
|
||||
});
|
||||
|
||||
test('Auslastung pro Host: Port-Paare insgesamt und belegt', () => {
|
||||
test('Auslastung pro Host: Serverplätze insgesamt und belegt', () => {
|
||||
const { pool: p } = pool([hostA, hostB], { '10.0.0.1': [27100] });
|
||||
assert.deepEqual(p.hostStats(), [
|
||||
{ name: 'a', publicHost: '10.0.0.1', total: 2, used: 1 },
|
||||
{ name: 'b', publicHost: '10.0.0.2', total: 20, used: 0 },
|
||||
{ name: 'a', publicHost: '10.0.0.1', total: 4, used: 1 },
|
||||
{ name: 'b', publicHost: '10.0.0.2', total: 40, used: 0 },
|
||||
]);
|
||||
});
|
||||
test('df-Ausgabe wird in Gesamt- und freien Speicher zerlegt', async () => {
|
||||
|
||||
@@ -0,0 +1,95 @@
|
||||
import { test } from 'node:test';
|
||||
import assert from 'node:assert/strict';
|
||||
import { formatPortList, parsePortList } from '../src/config.js';
|
||||
import { countSlots, DockerHostPool, planPorts } from '../src/servers/dockerAdapter.js';
|
||||
|
||||
const host = { portRangeStart: 27100, portRangeEnd: 27105, lanPorts: [27015, 27016] };
|
||||
const secrets = { rconPassword: 'Rcon123', serverPassword: 'Pw123', tvPassword: 'Tv123' };
|
||||
|
||||
test('Portlisten werden gelesen und kompakt geschrieben', () => {
|
||||
assert.deepEqual(parsePortList('27015-27017, 27020'), [27015, 27016, 27017, 27020]);
|
||||
assert.deepEqual(parsePortList(''), []);
|
||||
assert.throws(() => parsePortList('abc'), /kein Port/);
|
||||
assert.throws(() => parsePortList('80-90'), /ungültig/);
|
||||
assert.equal(formatPortList([27020, 27015, 27016, 27017]), '27015-27017,27020');
|
||||
});
|
||||
|
||||
test('Spielport kommt zuerst aus den LAN-Ports, CSTV liegt immer 100 darüber', () => {
|
||||
assert.deepEqual(planPorts(host, new Set()), { port: 27015, tvPort: 27115 });
|
||||
assert.deepEqual(planPorts(host, new Set([27015, 27115])), { port: 27016, tvPort: 27116 });
|
||||
// LAN-Ports voll: weiter im normalen Bereich, CSTV ebenfalls +100
|
||||
assert.deepEqual(planPorts(host, new Set([27015, 27016, 27115, 27116])), { port: 27100, tvPort: 27200 });
|
||||
// Spielport frei, aber sein CSTV-Port belegt: nächster Spielport
|
||||
assert.deepEqual(planPorts({ ...host, lanPorts: [] }, new Set([27200])), { port: 27101, tvPort: 27201 });
|
||||
assert.equal(planPorts({ ...host, lanPorts: [] }, new Set([27100, 27101, 27102, 27103, 27104, 27105])), null);
|
||||
});
|
||||
|
||||
test('Abstand zwischen Spiel- und CSTV-Port ist einstellbar', () => {
|
||||
assert.deepEqual(planPorts({ ...host, tvPortOffset: 5 }, new Set()), { port: 27015, tvPort: 27020 });
|
||||
});
|
||||
|
||||
test('Plätze pro Host: 2 LAN-Ports plus 6 Spielports im normalen Bereich', () => {
|
||||
assert.equal(countSlots(host, new Set()), 8);
|
||||
assert.equal(countSlots({ ...host, lanPorts: [] }, new Set()), 6);
|
||||
});
|
||||
|
||||
function fakePool({ busy = [] } = {}) {
|
||||
const calls = [];
|
||||
const runDocker = async (args) => {
|
||||
calls.push(args);
|
||||
if (args[0] === 'run') {
|
||||
const ports = args.filter((a, i) => args[i - 1] === '-p').map((a) => Number(a.split(':')[0]));
|
||||
const portEnv = args.find((a) => a.startsWith('CS2_PORT='));
|
||||
if (args.includes('--network') && portEnv) ports.push(Number(portEnv.split('=')[1]));
|
||||
if (ports.some((p) => busy.includes(p))) {
|
||||
const err = new Error('Command failed');
|
||||
err.stderr = 'Bind for 0.0.0.0:27015 failed: port is already allocated';
|
||||
throw err;
|
||||
}
|
||||
}
|
||||
if (args[0] === 'inspect') return 'running';
|
||||
return '';
|
||||
};
|
||||
const p = new DockerHostPool({
|
||||
bin: 'docker', image: 'joedwards32/cs2:latest', host: '10.0.0.9', rconHost: '10.0.0.9',
|
||||
portRangeStart: 27100, portRangeEnd: 27139, gslt: '', volume: 'v', extraArgs: '',
|
||||
startTimeoutMinutes: 1, lanPorts: [27015, 27016], serverNamePrefix: 'MaxLAN',
|
||||
hosts: [{ name: 'a', publicHost: '10.0.0.1' }],
|
||||
}, { runDocker });
|
||||
for (const a of p.adapters.values()) a.waitForUpdateCheck = async () => {};
|
||||
return { pool: p, calls };
|
||||
}
|
||||
|
||||
test('Servername zeigt die Teams, Spielport ist ein LAN-Port', async () => {
|
||||
const { pool: p, calls } = fakePool();
|
||||
const r = await p.start({ matchId: 'm1', mode: 'wingman', secrets, teamNames: { A: 'Rote "Füchse"', B: 'Blau' } });
|
||||
assert.equal(r.port, 27015);
|
||||
assert.equal(r.tvPort, 27115);
|
||||
const run = calls.find((c) => c[0] === 'run');
|
||||
assert.ok(run.includes('CS2_SERVERNAME=MaxLAN | Rote Füchse vs. Blau'));
|
||||
// Auf LAN-Ports im Host-Netz, damit Broadcasts des Serverbrowsers ankommen; keine Port-Weiterleitung
|
||||
assert.equal(run[run.indexOf('--network') + 1], 'host');
|
||||
assert.ok(!run.includes('-p'));
|
||||
});
|
||||
|
||||
test('Server außerhalb der LAN-Ports laufen weiter mit Port-Weiterleitung', async () => {
|
||||
const { pool: p, calls } = fakePool();
|
||||
await p.start({ matchId: 'x1', mode: 'wingman', secrets, teamNames: { A: 'A', B: 'B' } });
|
||||
await p.start({ matchId: 'x2', mode: 'wingman', secrets, teamNames: { A: 'A', B: 'B' } });
|
||||
calls.length = 0;
|
||||
// Die App kennt ihre Ports aus der Datenbank; hier sind beide LAN-Ports belegt
|
||||
const adapter = p.adapters.get('a');
|
||||
adapter.getUsedPorts = () => new Set([27015, 27016, 27115, 27116]);
|
||||
await adapter.launch({ matchId: 'x3', mode: 'wingman', secrets });
|
||||
const run = calls.find((c) => c[0] === 'run');
|
||||
assert.ok(!run.includes('--network'));
|
||||
assert.ok(run.includes('27100:27100/udp'));
|
||||
assert.ok(run.includes('27200:27200/udp'));
|
||||
});
|
||||
|
||||
test('Ist ein LAN-Port von einem fremden Dienst belegt, nimmt der Start den nächsten', async () => {
|
||||
const { pool: p, calls } = fakePool({ busy: [27015] });
|
||||
const r = await p.start({ matchId: 'm2', mode: 'competitive', secrets, teamNames: { A: 'A', B: 'B' } });
|
||||
assert.equal(r.port, 27016);
|
||||
assert.equal(calls.filter((c) => c[0] === 'run').length, 2);
|
||||
});
|
||||
@@ -52,9 +52,9 @@ test('Docker-Hosts: Prüfung der Liste und Übernahme in den Pool, Startzeit wir
|
||||
{ name: 'a', publicHost: '10.0.0.1', dockerHost: 'ssh://server@10.0.0.1', portStart: 27100, portEnd: 27139 },
|
||||
{ name: 'b', publicHost: '10.0.0.2', dockerHost: 'ssh://b', portStart: 27100, portEnd: 27119 },
|
||||
]);
|
||||
const pool = new DockerHostPool({ ...config.docker, startTimeoutMinutes: 20, hosts: null });
|
||||
const pool = new DockerHostPool({ ...config.docker, startTimeoutMinutes: 20, lanPorts: [], hosts: null });
|
||||
pool.setHosts(hosts);
|
||||
assert.deepEqual(pool.hostStats().map((h) => [h.name, h.total]), [['a', 20], ['b', 10]]);
|
||||
assert.deepEqual(pool.hostStats().map((h) => [h.name, h.total]), [['a', 40], ['b', 20]]);
|
||||
|
||||
pool.options.startTimeoutMinutes = 45;
|
||||
assert.equal(pool.adapters.get('b').options.startTimeoutMinutes, 45);
|
||||
|
||||
Reference in new issue
Block a user