Servername aus dem Spiel und ein Schloss fuer Passwort oder Whitelist
Deploy / check (push) Has been cancelled
Deploy / deploy (push) Has been cancelled

Der Name im Panel ist der, den DU vergibst — wie der Server sich im Spiel
nennt, stand nirgends. Steht jetzt darunter, aber nur wenn es etwas anderes
ist als der eingetragene Name; sonst haette dieselbe Zeile zweimal dagestanden.

Beim Schloss war die Frage, wie viel die Abfrage ueberhaupt hergibt. Nachgesehen
statt geraten: 31 der 358 gamedig-Protokolle melden ein Passwort. valve
gehoert dazu, deckt also CS2, Rust, ARK, Squad, 7DTD, Zomboid, Gmod und DayZ
ab. Minecraft nicht, Farming Simulator auch nicht — im LS-XML kommt das Wort
Passwort kein einziges Mal vor. Und eine Whitelist meldet grundsaetzlich kein
Spiel, die steht in einer Datei auf dem Server.

Also beides: automatisch, wo es geht, und eine Einstellung, die es schlaegt.
Wer "Whitelist" eintraegt, bekommt sie angezeigt, auch wenn der Server
schweigt.

Wichtig dabei ist der dritte Zustand. "Sagt nichts" ist nicht "offen" —
deshalb `null` und kein `false`. Ein Schloss, das bei jedem schweigenden
Server fehlt, waere eine Aussage, die wir nicht belegen koennen; umgekehrt
waere ein Schloss ohne Grundlage eine Luege. Bei unbekannt steht schlicht
nichts da.

Geprueft: Einstellung schlaegt Messung in allen vier Faellen, automatisch mit
true/false/null, fehlender Server, unbekannter Wert faellt auf automatisch
zurueck statt zu sperren, Schloss im Embed-Titel nur wenn belegt, Spielname
nur bei echtem Unterschied (auch mit Leerzeichen drumherum). Panel und
/server-Seite im laufenden Frontend angesehen.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
2026-08-10 14:12:33 +02:00
co-authored by Claude Opus 5
parent 4ce3c10d76
commit ea523b76b3
7 changed files with 107 additions and 14 deletions
+49 -2
View File
@@ -76,6 +76,9 @@ export async function queryServer(server) {
const base = {
...server, online: false, players: null, max: null, map: null,
playerNames: [], ping: null, version: null, mods: null,
// Wie der Server sich selbst nennt, und ob er ein Passwort verlangt.
// `null` heisst „sagt er nicht" — das ist etwas anderes als „nein".
spielname: null, passwort: null,
};
const started = Date.now();
try {
@@ -91,6 +94,7 @@ export async function queryServer(server) {
players: Number(data.clients) || 0,
max: Number(data.sv_maxclients) || 0,
map: data.mapname ?? null,
spielname: data.hostname ?? null,
};
}
if (server.type === 'http') {
@@ -118,6 +122,11 @@ export async function queryServer(server) {
max: state.maxplayers ?? null,
map: state.map || null,
version: state.version || null,
spielname: state.name || null,
// 31 von 358 Protokollen melden das — valve (CS2, Rust, ARK,
// Squad, Zomboid …) gehoert dazu, Minecraft und Farming Simulator
// nicht. Wo nichts kommt, bleibt es null statt false.
passwort: typeof state.password === 'boolean' ? state.password : null,
// Nur Farming Simulator liefert die Modliste mit; anderswo bleibt
// das Feld leer statt auf 0 zu stehen und eine Aussage zu machen.
mods: Array.isArray(state.raw?.mods) ? state.raw.mods.length : null,
@@ -128,6 +137,34 @@ export async function queryServer(server) {
}
}
/**
* Wer darf auf den Server?
*
* Nur 31 der 358 Protokolle melden überhaupt ein Passwort, und eine Whitelist
* meldet gar keins — die steckt in einer Datei auf dem Server, nicht in der
* Abfrage. Deshalb schlägt die Einstellung die Messung: wer „Whitelist"
* einträgt, bekommt sie angezeigt, auch wenn der Server schweigt.
*
* `null` heisst „unbekannt" und ist etwas anderes als „offen". Ein Schloss,
* das bei jedem schweigenden Server fehlt, wäre eine Aussage, die wir nicht
* belegen können.
*
* @returns {{ zu: boolean|null, grund: string|null }}
*/
export function zugangStand(server, ergebnis = {}) {
switch (server?.zugang) {
case 'passwort': return { zu: true, grund: 'Passwort' };
case 'whitelist': return { zu: true, grund: 'Whitelist' };
case 'offen': return { zu: false, grund: null };
default: {
const p = ergebnis.passwort;
if (p === true) return { zu: true, grund: 'Passwort' };
if (p === false) return { zu: false, grund: null };
return { zu: null, grund: null };
}
}
}
/** Nur http(s) darf in einen Link-Knopf — Discord weist alles andere ab */
export function istWebLink(url) {
return /^https?:\/\/\S+$/i.test(String(url ?? '').trim());
@@ -182,10 +219,15 @@ export function buildServerNachricht(r) {
/** GSM-Style-Embed für ein Query-Ergebnis */
export function buildServerEmbed(r) {
const icon = GAME_ICONS[r.type] ?? '🎮';
const zugang = zugangStand(r, r);
const embed = new EmbedBuilder()
.setColor(r.online ? GREEN : RED)
.setTitle(`${icon} ${r.name}`)
.addFields({ name: 'Status', value: r.online ? '🟢 **Online**' : '🔴 **Offline**', inline: true })
.setTitle(`${icon} ${r.name}${zugang.zu ? ' 🔒' : ''}`)
.addFields({
name: 'Status',
value: `${r.online ? '🟢 **Online**' : '🔴 **Offline**'}${zugang.grund ? `\n🔒 ${zugang.grund}` : ''}`,
inline: true,
})
// Intervall aus den Werten lesen, nicht festschreiben — sonst behauptet
// das Embed weiter „alle 2 min", nachdem im Panel etwas anderes steht.
.setFooter({
@@ -194,6 +236,11 @@ export function buildServerEmbed(r) {
})
.setTimestamp();
if (r.image_url) embed.setThumbnail(r.image_url);
// Wie der Server sich im Spiel nennt. Nur wenn es etwas anderes ist als
// der Name im Panel — sonst stünde dieselbe Zeile zweimal da.
if (r.spielname && r.spielname.trim() && r.spielname.trim() !== r.name?.trim()) {
embed.setDescription(`*${r.spielname.trim().slice(0, 200)}*`);
}
if (r.online && r.players != null && r.max) {
const pct = Math.min(1, r.players / r.max);