From cf4c95fe2c31f6861e366d8d7c551bcfae3ded4d Mon Sep 17 00:00:00 2001 From: jenz Date: Wed, 10 Jun 2026 16:08:25 +0100 Subject: [PATCH] instead of restarting the whole container we now just restart css, much faster --- start-signaling.sh | 2 +- streaming-agent/signaling-server.js | 110 +++++++++++++++------------- 2 files changed, 61 insertions(+), 51 deletions(-) diff --git a/start-signaling.sh b/start-signaling.sh index 31d738b..058a38f 100755 --- a/start-signaling.sh +++ b/start-signaling.sh @@ -1,6 +1,6 @@ #!/bin/bash cd ~/webclient_css_ze/streaming-agent -CF_APP_ID= CF_APP_TOKEN= NAME_SALT= node signaling-server.js &> ../signaling.log & +CF_APP_ID= CF_APP_TOKEN= NAME_SALT= CONTAINER_NAME=css-test node signaling-server.js &> ../signaling.log & echo "Signaling server started (PID $!), logs at webclient_css_ze/signaling.log" #pkill -f signaling-server.js diff --git a/streaming-agent/signaling-server.js b/streaming-agent/signaling-server.js index ced583c..082f9c4 100644 --- a/streaming-agent/signaling-server.js +++ b/streaming-agent/signaling-server.js @@ -2,10 +2,10 @@ * Signaling Server * * Handles two tracks: video and game audio. - * Also manages controller queue and container restart for name changes. + * Also manages controller queue and CSS process restart for name changes. * All Cloudflare API calls happen server-side. * - * Run: CF_APP_ID=xxx CF_APP_TOKEN=xxx node signaling-server.js + * Run: CF_APP_ID=xxx CF_APP_TOKEN=xxx NAME_SALT=xxx CONTAINER_NAME=css-test node signaling-server.js */ 'use strict'; @@ -23,6 +23,10 @@ const CF_APP_TOKEN = process.env.CF_APP_TOKEN || ''; const CF_BASE = `https://rtc.live.cloudflare.com/v1/apps/${CF_APP_ID}`; const CONTAINER = process.env.CONTAINER_NAME || 'css-test'; const IDLE_TIMEOUT = 10000; // 10 seconds +const NAME_SALT = process.env.NAME_SALT || 'change-this-secret'; + +// How long to wait after relaunching CSS before granting control +const CSS_LAUNCH_WAIT = 15000; if (!CF_APP_ID || !CF_APP_TOKEN) { console.error('[sig] CF_APP_ID and CF_APP_TOKEN must be set'); @@ -67,7 +71,7 @@ const pacatProcesses = new Map(); // ── Controller queue ────────────────────────────────────────────────────────── let currentController = null; -let lastControllerName = null; +let lastControllerName = null; let idleTimer = null; const controlQueue = []; let containerRestarting = false; @@ -98,11 +102,11 @@ async function applyController(entry) { containerRestarting = true; broadcastQueueState(); + // Same person returning — no restart needed if (entry.visitorName === lastControllerName) { - // Same person returning — no restart needed + console.log(`[sig] same controller returning, skipping restart`); containerRestarting = false; currentController = entry; - lastControllerName = entry.visitorName; resetIdleTimer(entry); broadcastQueueState(); if (entry.ws.readyState === WebSocket.OPEN) { @@ -111,9 +115,11 @@ async function applyController(entry) { return; } - // Different person — update rev.ini and restart + // Different person — update rev.ini and relaunch CSS only lastControllerName = entry.visitorName; + console.log(`[sig] applying controller: ${entry.visitorName}`); + // Update rev.ini try { const safeName = entry.visitorName.replace(/'/g, "'\\''"); execSync(`docker exec ${CONTAINER} sed -i "s/PlayerName=.*/PlayerName=${safeName}/" "/home/ubuntu/Counter-Strike Source/rev.ini"`); @@ -122,23 +128,30 @@ async function applyController(entry) { console.error('[sig] failed to update rev.ini:', e.message); } + // Kill CSS and revLoader (leave Xvfb, PulseAudio, gst_agent running) try { - execSync(`docker restart ${CONTAINER}`); - console.log('[sig] container restarted'); + execSync(`docker exec ${CONTAINER} pkill -f "cstrike_win64.exe" || true`); + execSync(`docker exec ${CONTAINER} pkill -f "revLoader.exe" || true`); + execSync(`docker exec ${CONTAINER} pkill -f "wineserver" || true`); + execSync(`docker exec ${CONTAINER} pkill -f "winedevice.exe" || true`); + console.log('[sig] CSS processes killed'); +} catch (e) { + console.error('[sig] failed to kill CSS:', e.message); +} + + // Wait for Wine to clean up + await new Promise(r => setTimeout(r, 2000)); + + // Relaunch CSS + try { + execSync(`docker exec -d ${CONTAINER} bash -c "DISPLAY=:99 wine '/home/ubuntu/Counter-Strike Source/revLoader.exe'"`); + console.log('[sig] CSS relaunched'); } catch (e) { - console.error('[sig] failed to restart container:', e.message); + console.error('[sig] failed to relaunch CSS:', e.message); } - // Wait for agent to re-register (up to 30 seconds) - await new Promise(resolve => { - const start = Date.now(); - const check = setInterval(() => { - if (agentSessions.size > 0 || Date.now() - start > 30000) { - clearInterval(check); - resolve(); - } - }, 500); - }); + // Wait for CSS to launch and connect to server + await new Promise(r => setTimeout(r, CSS_LAUNCH_WAIT)); containerRestarting = false; currentController = entry; @@ -178,6 +191,28 @@ function removeFromQueue(ws) { } } +// ── IP → Name (server side, salt never exposed to browser) ─────────────────── +function hashString(str) { + let hash = 5381; + for (let i = 0; i < str.length; i++) { + hash = ((hash << 5) + hash) + str.charCodeAt(i); + hash = hash & 0x7fffffff; + } + return hash; +} + +function ipToName(ip) { + const adjectives = ['Fast','Slow','Red','Blue','Green','Dark','Bright','Wild', + 'Cool','Hot','Big','Small','Sharp','Brave','Swift','Bold']; + const nouns = ['Wolf','Eagle','Tiger','Bear','Fox','Lion','Hawk','Shark', + 'Snake','Raven','Storm','Rock','Fire','Ice','Wind','Steel']; + const salted = ip + NAME_SALT; + const h1 = hashString(salted); + const h2 = hashString(salted + '1'); + const h3 = hashString(salted + '2'); + return `${adjectives[h1 % adjectives.length]} ${nouns[h2 % nouns.length]} ${h3 % 9999}`; +} + // ── pacat for mic audio ─────────────────────────────────────────────────────── const PACAT_ARGS = [ '--playback', @@ -204,29 +239,6 @@ function stopPacat(browserSessId) { if (pacat) { pacat.kill(); pacatProcesses.delete(browserSessId); } } -const NAME_SALT = process.env.NAME_SALT || 'change-this-secret'; - -function hashString(str) { - let hash = 5381; - for (let i = 0; i < str.length; i++) { - hash = ((hash << 5) + hash) + str.charCodeAt(i); - hash = hash & 0x7fffffff; - } - return hash; -} - -function ipToName(ip) { - const adjectives = ['Fast','Slow','Red','Blue','Green','Dark','Bright','Wild', - 'Cool','Hot','Big','Small','Sharp','Brave','Swift','Bold']; - const nouns = ['Wolf','Eagle','Tiger','Bear','Fox','Lion','Hawk','Shark', - 'Snake','Raven','Storm','Rock','Fire','Ice','Wind','Steel']; - const salted = ip + NAME_SALT; - const h1 = hashString(salted); - const h2 = hashString(salted + '1'); - const h3 = hashString(salted + '2'); - return `${adjectives[h1 % adjectives.length]} ${nouns[h2 % nouns.length]} ${h3 % 9999}`; -} - // ── WebSocket handler ───────────────────────────────────────────────────────── wss.on('connection', ws => { let role = null; @@ -262,16 +274,13 @@ wss.on('connection', ws => { else if (msg.type === 'request_control') { visitorName = msg.visitorName; - // Reject during restart if (containerRestarting) return; - // Already controlling — just reset idle timer if (currentController && currentController.ws === ws) { resetIdleTimer(currentController); return; } - // Already in queue if (controlQueue.find(e => e.ws === ws)) return; const entry = { ws, visitorName, browserSessId }; @@ -295,6 +304,12 @@ wss.on('connection', ws => { if (browserSessId) stopPacat(browserSessId); } + else if (msg.type === 'get_ip') { + const ip = ws._socket.remoteAddress.replace('::ffff:', ''); + const name = ipToName(ip); + ws.send(JSON.stringify({ type: 'your_ip', ip: name })); + } + else if (msg.type === 'browser_connect') { role = 'browser'; const entry = [...agentSessions.values()][0]; @@ -358,11 +373,6 @@ wss.on('connection', ws => { entry.agentWs.send(JSON.stringify(msg.event)); } } - else if (msg.type === 'get_ip') { - const ip = ws._socket.remoteAddress.replace('::ffff:', ''); - const name = ipToName(ip); - ws.send(JSON.stringify({ type: 'your_ip', ip: name })); - } }); ws.on('close', () => {