Messerrunde auf dem eigenen Match-Server, danach derselbe Server fürs Match

Bei Seitenwahl per Messerrunde richtet die App den Match-Server in der Lobby als
Messer-Server ein (Passwort des Matches, Aufwärmphase ohne Ende). Spieler sehen die
Verbindungsdaten in der Lobby, der Organisator startet bzw. wiederholt die Runde.
Nach dem Veto wird derselbe Server fürs Match eingerichtet, die Messer-Einstellungen
werden zurückgenommen.
This commit is contained in:
mregosar committed 2026-11-01 18:25:07 +01:00
1 parent 2c57b8c170
commit 13320313ec
10 files changed
+220 -20

No files matched your search

+3 -2
View File
@@ -61,13 +61,14 @@ export default function ServerPanel({ state, isOrg, act, busy }) {
)}
{!server && <p className="muted small">Für dieses Match wurde noch kein Server angefordert.</p>}
{server && ['starting', 'ready'].includes(server.status) && state.status !== 'server_setup' && (
{server?.knife && <p className="muted small">Vor dem Veto läuft hier die Messerrunde, die Verbindungsdaten stehen in der Lobby. Nach dem Veto wird derselbe Server für das Match eingerichtet.</p>}
{server && !server.knife && ['starting', 'ready'].includes(server.status) && state.status !== 'server_setup' && (
<p className="muted small">
{sim ? 'Der Start wird während des Vetos simuliert.' : 'Der Server fährt während des Vetos hoch.'} Die Verbindungsdaten erscheinen nach dem Veto.
</p>
)}
{server?.connect && (
{server?.connect && !server.knife && (
<div className="connect">
<h3>{sim ? 'Spieler-Verbindung (Demo-Beispiel, nicht verbindbar)' : 'Verbindung für Spieler'}</h3>
<CopyField label="In die CS2-Konsole einfügen" value={server.connect.command} secret />
+46 -1
View File
@@ -13,6 +13,7 @@ import EventLog from '../components/EventLog.jsx';
import MapImage from '../components/MapImage.jsx';
import SelfJoin from '../components/SelfJoin.jsx';
import ChatBox from '../components/ChatBox.jsx';
import CopyField from '../components/CopyField.jsx';
export default function MatchPage() {
const { id } = useParams();
@@ -172,7 +173,7 @@ function Stage(ctx) {
{missing.map((t) => <li key={t.key}><strong className={`team-${t.key.toLowerCase()}`}>{t.name}:</strong> {readinessGap(t, state.requireFullTeams)}</li>)}
</ul>
)}
{state.vetoStart === 'knife' && <KnifeRound state={state} />}
{state.vetoStart === 'knife' && (state.server?.knife ? <OwnKnifeServer state={state} isOrg={isOrg} act={act} busy={busy} /> : <KnifeRound state={state} />)}
{isOrg && state.vetoStart === 'knife' ? (
<div className="knife-decide">
<p>Wer hat die Messerrunde gewonnen? Dieses Team beginnt das Veto.</p>
@@ -222,6 +223,50 @@ function Stage(ctx) {
* Seitenwahl per Messerrunde: freie Messer-Server vorschlagen, passende Teamgröße zuerst.
* Frei = niemand drauf (oder Spielerzahl unbekannt).
*/
/** Messerrunde auf dem eigenen Match-Server: derselbe Server wird nach dem Veto für das Match eingerichtet. */
function OwnKnifeServer({ state, isOrg, act, busy }) {
const { server } = state;
const { knife, connect } = server;
const live = knife.phase === 'live';
return (
<div className="knife-round">
<h3>Seitenwahl per Messerrunde</h3>
<p className="small">
Für euer Match läuft ein eigener Server. Beide Teams verbinden sich, spielen dort eine Runde nur mit Messer, danach legt der
Organisator fest, welches Team das Veto beginnt. Nach dem Veto geht es auf demselben Server weiter.
</p>
{!knife.ready ? (
<p className="small tone-pending">
<span className="status-dot tone-pending pulse" /> Der Server fährt hoch, die Verbindungsdaten erscheinen gleich hier.
{server.message && <span className="muted"> ({server.message})</span>}
</p>
) : (
<>
<p className={`small ${live ? 'tone-ok' : 'tone-ok'}`}>
<span className={`status-dot ${live ? 'tone-ok' : 'tone-ok'}`} />
{live ? ' Messerrunde läuft' : ' Aufwärmphase: verbinden und warten, bis der Organisator die Runde startet'}
{server.playerCount != null && <span className="muted"> · {server.playerCount} auf dem Server</span>}
</p>
{connect && (
<div className="knife-connect">
<CopyField label="In die CS2-Konsole einfügen" value={connect.command} secret />
<a className="btn btn-primary" href={connect.steamUrl}>In CS2 verbinden</a>
</div>
)}
{isOrg && (
<div className="row knife-actions">
{!live && <button className="btn btn-sm btn-primary" disabled={!!busy} onClick={() => act('server/knife-start', null, 'Messerrunde gestartet')}>Messerrunde starten</button>}
<button className="btn btn-sm" disabled={!!busy} onClick={() => act('server/knife-reset', null, 'Zurück in der Aufwärmphase')}>
{live ? 'Runde wiederholen (Aufwärmphase)' : 'Aufwärmphase neu starten'}
</button>
</div>
)}
</>
)}
</div>
);
}
function KnifeRound({ state }) {
const [servers, setServers] = useState(null);
useEffect(() => {
+3
View File
@@ -769,3 +769,6 @@ input:focus, select:focus { border-color: var(--link); outline: none; }
.console-backdrop { padding: 0; }
.console-dialog { height: 100%; border-radius: 0; }
}
.knife-connect { display: flex; flex-wrap: wrap; align-items: end; gap: 8px 12px; margin-top: 10px; }
.knife-connect > :first-child { flex: 1 1 260px; }
.knife-actions { margin-top: 10px; gap: 8px; flex-wrap: wrap; }