instead of restarting the whole container we now just restart css, much faster

This commit is contained in:
jenz 2026-06-10 16:08:25 +01:00
parent 435cd7f4ae
commit cf4c95fe2c
2 changed files with 61 additions and 51 deletions

View File

@ -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

View File

@ -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');
@ -98,11 +102,11 @@ async function applyController(entry) {
containerRestarting = true;
broadcastQueueState();
if (entry.visitorName === lastControllerName) {
// Same person returning — no restart needed
if (entry.visitorName === lastControllerName) {
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', () => {