added option for mouse acceleration and button to manually restart the game if stuck

This commit is contained in:
2026-07-24 10:57:53 +01:00
parent 19e7ff4f14
commit 62e74b740f
2 changed files with 151 additions and 38 deletions
+84 -2
View File
@@ -81,6 +81,27 @@
accent-color: #4fc;
cursor: pointer;
}
#sensitivity-control {
position: fixed;
bottom: 10px;
left: 10px;
background: rgba(0,0,0,0.7);
padding: 8px 12px;
border-radius: 4px;
font-size: 12px;
z-index: 10;
display: flex;
align-items: center;
gap: 8px;
color: #fff;
font-family: monospace;
}
#sensitivity-control input[type=range] {
width: 80px;
accent-color: #4fc;
cursor: pointer;
}
#sound-toggle {
position: fixed;
top: 10px;
@@ -97,6 +118,22 @@
}
#sound-toggle:hover { background: rgba(40,40,40,0.85); }
#sound-toggle.unmuted { color: #4fc; border-color: #4fc; }
#restart-game-btn {
position: fixed;
top: 36px;
left: 10px;
background: rgba(120,20,20,0.75);
color: #fff;
border: 1px solid #a44;
padding: 6px 12px;
border-radius: 4px;
font-size: 13px;
font-family: monospace;
cursor: pointer;
z-index: 10;
}
#restart-game-btn:hover { background: rgba(160,30,30,0.9); }
#gameCanvas {
width: 1280px;
height: 720px;
@@ -110,7 +147,7 @@
#queue-panel {
position: fixed;
top: 40px;
top: 68px;
left: 10px;
background: rgba(0,0,0,0.75);
padding: 10px 14px;
@@ -165,9 +202,13 @@
<body>
<div id="status">Connecting...</div>
<button id="sound-toggle">🔇 Unmute</button>
<button id="restart-game-btn" style="display:none;">🔄 Restart Game</button>
<div id="brightness-control">
☀️ <input type="range" id="brightness-slider" min="50" max="200" value="100" step="5"> <span id="brightness-value">100%</span>
</div>
<div id="sensitivity-control">
🖱️ <input type="range" id="sensitivity-slider" min="1" max="6" value="3" step="0.5"> <span id="sensitivity-value">1.0x</span>
</div>
<div id="idle-warning">Idle — control will be released soon. Move your mouse to stay in control.</div>
@@ -263,8 +304,12 @@
const loadingEl = document.getElementById('loading-overlay');
const idleWarnEl = document.getElementById('idle-warning');
const soundToggleEl = document.getElementById('sound-toggle');
const restartGameBtn = document.getElementById('restart-game-btn');
const brightnessSlider = document.getElementById('brightness-slider');
const brightnessValue = document.getElementById('brightness-value');
const sensitivitySlider = document.getElementById('sensitivity-slider');
const sensitivityValue = document.getElementById('sensitivity-value');
let mouseSensitivity = 1;
let sigWs = null;
let pc = null;
@@ -273,6 +318,7 @@
let isControlling = false;
let isInQueue = false;
let isWaitingForRestart = false;
let waitingRetryTimer = null;
let idleWarnTimer = null;
function setStatus(msg) { statusEl.textContent = msg; }
@@ -311,12 +357,29 @@
setMuted(!canvas.muted);
});
restartGameBtn.addEventListener('click', () => {
if (!isControlling) return;
const confirmed = confirm(
'Restart the game? This kills and relaunches Counter-Strike Source. ' +
'Use this only if the game appears stuck/frozen. Takes a few seconds.'
);
if (!confirmed) return;
if (sigWs && sigWs.readyState === WebSocket.OPEN) {
sigWs.send(JSON.stringify({ type: 'restart_css' }));
}
});
brightnessSlider.addEventListener('input', () => {
const val = brightnessSlider.value;
canvas.style.filter = `brightness(${val}%)`;
brightnessValue.textContent = `${val}%`;
});
sensitivitySlider.addEventListener('input', () => {
mouseSensitivity = parseFloat(sensitivitySlider.value);
sensitivityValue.textContent = `${mouseSensitivity.toFixed(1)}x`;
});
function updateQueuePanel(controller, queue, restarting) {
let html = '';
if (restarting) {
@@ -405,6 +468,7 @@
canvas.requestPointerLock();
hintEl.textContent = 'Mouse captured — ESC to release';
resetIdleWarning();
restartGameBtn.style.display = 'block';
}
function exitControl() {
@@ -416,6 +480,7 @@
stopMic();
if (idleWarnTimer) clearTimeout(idleWarnTimer);
idleWarnEl.classList.remove('visible');
restartGameBtn.style.display = 'none';
if (document.pointerLockElement === canvas) document.exitPointerLock();
}
@@ -459,6 +524,7 @@
const msg = JSON.parse(e.data);
if (msg.type === 'session_created') {
if (waitingRetryTimer) { clearInterval(waitingRetryTimer); waitingRetryTimer = null; }
sessionId = msg.sessionId;
await pc.setRemoteDescription(new RTCSessionDescription(msg.sdpAnswer));
await waitForIce();
@@ -490,6 +556,21 @@
}
else if (msg.type === 'waiting') {
setStatus('Waiting for game instance...');
// Previously this just set the status text and did nothing further —
// if gst_agent had crashed (e.g. ICE failure) and hadn't finished
// restarting/re-registering yet at the exact moment this browser's
// browser_connect request landed, the browser was stuck showing
// this message forever with no way to recover short of a manual
// page refresh, even after gst_agent came back online moments later.
// Now retry browser_connect periodically until session_created
// arrives (cleared there) or the socket closes.
if (!waitingRetryTimer) {
waitingRetryTimer = setInterval(() => {
if (sigWs && sigWs.readyState === WebSocket.OPEN && pc && pc.localDescription) {
sigWs.send(JSON.stringify({ type: 'browser_connect', sdp: pc.localDescription.sdp }));
}
}, 3000);
}
}
else if (msg.type === 'error') {
setStatus('Error: ' + msg.message);
@@ -500,6 +581,7 @@
sigWs.onclose = () => {
stopMic();
exitControl();
if (waitingRetryTimer) { clearInterval(waitingRetryTimer); waitingRetryTimer = null; }
setStatus('Disconnected');
};
}
@@ -566,7 +648,7 @@
const now = performance.now();
if (now - lastMouseMove < 16) return;
lastMouseMove = now;
sendInput({ type: 'mouse_move', dx: e.movementX, dy: e.movementY });
sendInput({ type: 'mouse_move', dx: e.movementX * mouseSensitivity, dy: e.movementY * mouseSensitivity });
pingActive();
});