hopefully does not lose keyboard control any more for users
This commit is contained in:
parent
a7b4f04118
commit
d3baa7e050
@ -35,7 +35,7 @@ from gi.repository import Gst, GstWebRTC, GstSdp, GLib
|
|||||||
import requests
|
import requests
|
||||||
import websockets
|
import websockets
|
||||||
|
|
||||||
logging.basicConfig(level=logging.INFO, format='[gst_agent] %(message)s', stream=sys.stderr)
|
logging.basicConfig(level=logging.INFO, format='%(asctime)s [gst_agent] %(message)s', stream=sys.stderr)
|
||||||
log = logging.getLogger(__name__)
|
log = logging.getLogger(__name__)
|
||||||
|
|
||||||
# ── Configuration ─────────────────────────────────────────────────────────────
|
# ── Configuration ─────────────────────────────────────────────────────────────
|
||||||
@ -345,16 +345,33 @@ class CSSStreamAgent:
|
|||||||
def css_watchdog():
|
def css_watchdog():
|
||||||
# If cstrike_win64.exe crashes (e.g. Wine c0000409 stack overrun), the
|
# If cstrike_win64.exe crashes (e.g. Wine c0000409 stack overrun), the
|
||||||
# stream keeps running but shows a black screen forever with no recovery.
|
# stream keeps running but shows a black screen forever with no recovery.
|
||||||
# This watchdog detects the crash and relaunches CSS directly via revLoader,
|
# This watchdog detects the crash and triggers a restart.
|
||||||
# the same way start.sh does on initial boot. gst_agent keeps running and
|
#
|
||||||
# will simply start capturing the relaunched game once it renders.
|
# IMPORTANT: this must NOT relaunch CSS directly itself. Doing so
|
||||||
|
# (a bare `wine revLoader.exe` Popen call) created a second, uncoordinated
|
||||||
|
# restart authority alongside the signaling server's own kill+relaunch
|
||||||
|
# logic (manual button / scheduled 5h restart). If both fired close
|
||||||
|
# together, two separate Wine processes could end up racing to launch
|
||||||
|
# CSS, producing duplicate/orphaned windows — xdotool's window search
|
||||||
|
# would then target an arbitrary one, causing keyboard input to land on
|
||||||
|
# a stale/backgrounded window while video (which captures the whole X
|
||||||
|
# display, not a specific window) kept working fine. That mismatch is
|
||||||
|
# exactly the "mouse/video fine, keyboard dead" symptom that was
|
||||||
|
# reported after a watchdog relaunch overlapped with a manual restart.
|
||||||
|
#
|
||||||
|
# So instead we just notify the signaling server, which has the single
|
||||||
|
# authoritative kill+relaunch implementation (performCssRestart) shared
|
||||||
|
# by the manual restart button, the scheduled 5-hour restart, and this
|
||||||
|
# watchdog — never more than one restart in flight at a time.
|
||||||
#
|
#
|
||||||
# We only start watching once CSS has been seen alive at least once, to
|
# We only start watching once CSS has been seen alive at least once, to
|
||||||
# avoid racing its initial startup. Require several consecutive misses to
|
# avoid racing its initial startup. Require several consecutive misses to
|
||||||
# avoid false positives during a normal controller-triggered relaunch
|
# avoid false positives during a normal controller-triggered handoff.
|
||||||
# (the signaling server kills+relaunches CSS on controller change).
|
restart_url = SIG_URL.replace('ws://', 'http://').replace('wss://', 'https://')
|
||||||
home = os.path.expanduser('~')
|
if not restart_url.endswith('/'):
|
||||||
revloader = os.path.join(home, 'Counter-Strike Source', 'revLoader.exe')
|
restart_url += '/'
|
||||||
|
restart_url += 'webclient-force-restart'
|
||||||
|
|
||||||
seen_alive = False
|
seen_alive = False
|
||||||
missing_count = 0
|
missing_count = 0
|
||||||
while True:
|
while True:
|
||||||
@ -381,17 +398,14 @@ def css_watchdog():
|
|||||||
# 4 consecutive misses (~20s) — long enough to not collide with the
|
# 4 consecutive misses (~20s) — long enough to not collide with the
|
||||||
# signaling server's own kill+relaunch cycle on controller change.
|
# signaling server's own kill+relaunch cycle on controller change.
|
||||||
if missing_count >= 4:
|
if missing_count >= 4:
|
||||||
log.error('CSS appears crashed — relaunching revLoader')
|
log.error('CSS appears crashed — requesting restart from signaling server')
|
||||||
try:
|
try:
|
||||||
env = dict(os.environ, DISPLAY=DISPLAY)
|
resp = requests.post(restart_url, timeout=5)
|
||||||
subprocess.Popen(['wine', revloader], env=env,
|
log.info(f'restart request sent to {restart_url}, status={resp.status_code}')
|
||||||
stdout=subprocess.DEVNULL,
|
|
||||||
stderr=subprocess.DEVNULL)
|
|
||||||
log.info('revLoader relaunched by watchdog')
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
log.error(f'watchdog relaunch failed: {e}')
|
log.error(f'watchdog restart request failed: {e}')
|
||||||
# Reset so we give the relaunched game time to come up before
|
# Reset so we give the restart time to complete before counting
|
||||||
# counting misses again.
|
# misses again.
|
||||||
seen_alive = False
|
seen_alive = False
|
||||||
missing_count = 0
|
missing_count = 0
|
||||||
|
|
||||||
|
|||||||
@ -10,6 +10,14 @@
|
|||||||
|
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
|
// Prefix every console.log / console.error line with an ISO timestamp.
|
||||||
|
// Wrapping here means every existing log call site automatically gets a
|
||||||
|
// timestamp without needing to be touched individually.
|
||||||
|
const _origLog = console.log;
|
||||||
|
const _origError = console.error;
|
||||||
|
console.log = (...args) => _origLog(`[${new Date().toISOString()}]`, ...args);
|
||||||
|
console.error = (...args) => _origError(`[${new Date().toISOString()}]`, ...args);
|
||||||
|
|
||||||
const express = require('express');
|
const express = require('express');
|
||||||
const http = require('http');
|
const http = require('http');
|
||||||
const WebSocket = require('ws');
|
const WebSocket = require('ws');
|
||||||
@ -263,6 +271,25 @@ function performCssRestart(reason) {
|
|||||||
}
|
}
|
||||||
containerRestarting = false;
|
containerRestarting = false;
|
||||||
broadcastQueueState();
|
broadcastQueueState();
|
||||||
|
|
||||||
|
// Explicitly give the newly-created CSS window keyboard focus once it
|
||||||
|
// has had time to map. Mouse clicks/movement always work after a
|
||||||
|
// restart because they target whatever window is physically under the
|
||||||
|
// cursor — but keyboard input goes to whichever window holds X11 input
|
||||||
|
// focus, which is separate state that does NOT automatically transfer
|
||||||
|
// to a freshly-relaunched window after a kill+relaunch (as opposed to a
|
||||||
|
// genuinely fresh X session on container boot). Without this, keyboard
|
||||||
|
// input can silently keep targeting a stale/dead window reference while
|
||||||
|
// everything else on the new window works fine — this was very likely
|
||||||
|
// the actual cause of "keyboard stuck, mouse/video fine" after restarts.
|
||||||
|
setTimeout(() => {
|
||||||
|
try {
|
||||||
|
execSync(`docker exec ${CONTAINER} bash -c "DISPLAY=:99 WIN=\$(xdotool search --name '' | head -1) && xdotool windowfocus --sync \$WIN" || true`);
|
||||||
|
console.log('[sig] CSS restart: focused new window');
|
||||||
|
} catch (e) {
|
||||||
|
console.error('[sig] CSS restart: failed to focus new window:', e.message);
|
||||||
|
}
|
||||||
|
}, 8000);
|
||||||
}, 2000);
|
}, 2000);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user