mirror of
https://github.com/logsol/chuck.js.git
synced 2026-05-11 18:47:35 +00:00
Socket.IO (TCP) holds back later packets while it retransmits a lost one, which stalls worldUpdate delivery on lossy long-distance links — exactly the pattern game state suffers worst from. WebRTC DataChannels in unreliable mode (ordered:false, maxRetransmits:0) drop late packets instead of queueing them, which is what we want for high-frequency state sync. Adds a per-user WebRTCTransport on top of the existing Socket.IO connection. Socket.IO stays in charge of bootstrap, signaling (SDP/ICE exchange), and control messages — only gameCommand payloads get routed onto the unreliable channel once it's open. If WebRTC fails to negotiate, gameCommand transparently falls back to Socket.IO, so the game keeps working unchanged. A new StatsLogger writes per-session JSONL events (session_start, webrtc_ready with negotiation time, per-second stats with transport, RTT samples, recv/send rates, seq gaps) so we can compare real-world runs (e.g. Germany server <-> Korea client) instead of guessing. URL flag ?webrtc=0 forces fallback for A/B testing. scripts/webrtc-browser-test.js spins up a headless Chromium against a freshly-started server and asserts the unreliable channel opens and gameCommand traffic actually rides it.
218 lines
No EOL
7.1 KiB
JavaScript
Executable file
218 lines
No EOL
7.1 KiB
JavaScript
Executable file
define([
|
|
"Game/Config/Settings",
|
|
"Lib/Utilities/NotificationCenter",
|
|
"Lib/Vendor/Screenfull",
|
|
"Game/Client/View/Graph",
|
|
"Game/Client/PointerLockManager"
|
|
],
|
|
|
|
function (Settings, nc, Screenfull, Graph, pointerLockManager) {
|
|
|
|
"use strict";
|
|
|
|
function DomController() {
|
|
this.canvas = null;
|
|
this.stats = null;
|
|
this.ping = null;
|
|
this.nickContainer = null;
|
|
this.fpsContainer = null;
|
|
this.devToolsContainer = null;
|
|
this.frames = 0;
|
|
|
|
this.canvas = document.getElementById("canvas");
|
|
|
|
this.initDevTools();
|
|
}
|
|
|
|
DomController.prototype.initDevTools = function() {
|
|
var self = this;
|
|
var li, button, label;
|
|
|
|
this.devToolsContainer = document.getElementById("menuBar");
|
|
|
|
// create back to menu button
|
|
li = document.createElement("li");
|
|
li.id = "back-to-menu";
|
|
button = document.createElement("button");
|
|
button.innerHTML = "Menu";
|
|
button.onclick = function() {
|
|
window.location.href="/";
|
|
};
|
|
li.appendChild(button);
|
|
this.devToolsContainer.appendChild(li);
|
|
|
|
// create user name
|
|
li = document.createElement("li");
|
|
label = document.createElement("label");
|
|
label.appendChild(document.createTextNode("?"));
|
|
li.appendChild(label);
|
|
this.devToolsContainer.appendChild(li);
|
|
this.nickContainer = label;
|
|
|
|
|
|
// create fps label with updater
|
|
li = document.createElement("li");
|
|
label = document.createElement("label");
|
|
label.id = "label-fps";
|
|
li.appendChild(label);
|
|
this.devToolsContainer.appendChild(li);
|
|
this.fpsContainer = label;
|
|
|
|
/*
|
|
// create new fps meter
|
|
li = document.createElement("li");
|
|
var fpsCanvas = document.createElement("canvas");
|
|
fpsCanvas.id = "graph-fps";
|
|
fpsCanvas.width = "100";
|
|
fpsCanvas.height = "27";
|
|
li.appendChild(fpsCanvas);
|
|
this.devToolsContainer.appendChild(li);
|
|
|
|
this.fpsGraph = new Graph(fpsCanvas.getContext("2d"), true);
|
|
|
|
this.fpsGraph.onUpdate(function(value){
|
|
self.fpsContainer.innerHTML = "FPS:" + value;
|
|
|
|
var color,
|
|
alpha = 0.8;
|
|
|
|
if (value >= 50) {
|
|
color = "rgba(136, 209, 018, " + alpha + ")";
|
|
} else if (value > 25) {
|
|
color = "rgba(204, 114, 018, " + alpha + ")";
|
|
} else {
|
|
color = "rgba(224, 018, 018, " + 1 + ")";
|
|
}
|
|
|
|
return color;
|
|
});
|
|
|
|
// create new ping meter
|
|
li = document.createElement("li");
|
|
var pingCanvas = document.createElement("canvas");
|
|
pingCanvas.id = "graph-fps";
|
|
pingCanvas.width = "100";
|
|
pingCanvas.height = "27";
|
|
li.appendChild(pingCanvas);
|
|
this.devToolsContainer.appendChild(li);
|
|
|
|
this.pingGraph = new Graph(pingCanvas.getContext("2d"), false, {
|
|
scaleOverride: false,
|
|
scaleStartValue: 0,
|
|
scaleStepWidth: 0,
|
|
scaleSteps: 0
|
|
});
|
|
*/
|
|
|
|
setInterval(function() {
|
|
self.fpsContainer.innerHTML = "FPS:" + self.frames;
|
|
self.frames = 0;
|
|
}, 1000);
|
|
|
|
// create Ping: container
|
|
li = document.createElement("li");
|
|
this.ping = document.createElement("label");
|
|
li.appendChild(this.ping);
|
|
this.devToolsContainer.appendChild(li);
|
|
|
|
// create Transport: container
|
|
li = document.createElement("li");
|
|
this.transport = document.createElement("label");
|
|
this.transport.innerHTML = "transport:?";
|
|
li.appendChild(this.transport);
|
|
this.devToolsContainer.appendChild(li);
|
|
|
|
|
|
// create debug mode
|
|
li = document.createElement("li");
|
|
label = document.createElement("label");
|
|
var checkbox = document.createElement("input");
|
|
checkbox.type = "checkbox";
|
|
checkbox.onclick = function(e) {
|
|
nc.trigger(nc.ns.client.view.debugMode.toggle, e.target.checked);
|
|
};
|
|
label.appendChild(checkbox);
|
|
label.appendChild(document.createTextNode("Debug"));
|
|
li.appendChild(label);
|
|
this.devToolsContainer.appendChild(li);
|
|
|
|
|
|
// create Fullscreen
|
|
li = document.createElement("li");
|
|
li.id = "fullscreen";
|
|
button = document.createElement("button");
|
|
button.innerHTML = "Fullscreen";
|
|
button.onclick = function() {
|
|
if(Screenfull.enabled) {
|
|
pointerLockManager.request();
|
|
Screenfull.request(self.canvas);
|
|
}
|
|
};
|
|
li.appendChild(button);
|
|
this.devToolsContainer.appendChild(li);
|
|
|
|
|
|
// FIXME : isn't this a weird place for this?
|
|
window.onresize = function() {
|
|
nc.trigger(nc.ns.client.view.display.change);
|
|
};
|
|
};
|
|
|
|
DomController.prototype.setNick = function (nick) {
|
|
this.nickContainer.innerHTML = nick;
|
|
};
|
|
|
|
DomController.prototype.fpsStep = function() {
|
|
this.frames++;
|
|
// this.fpsGraph.step();
|
|
};
|
|
|
|
DomController.prototype.setPing = function(ping) {
|
|
this.ping.innerHTML = "Ping:" + ping;
|
|
// this.pingGraph.addValue(ping);
|
|
};
|
|
|
|
DomController.prototype.setTransport = function(name) {
|
|
if (this.transport) this.transport.innerHTML = "transport:" + name;
|
|
};
|
|
|
|
DomController.prototype.getCanvasContainer = function () {
|
|
var container = document.getElementById(Settings.CANVAS_DOM_ID);
|
|
|
|
if(container) {
|
|
return container;
|
|
} else {
|
|
throw 'Canvas Container missing: #' + Settings.CANVAS_DOM_ID;
|
|
}
|
|
};
|
|
|
|
DomController.prototype.getCanvas = function () {
|
|
return this.canvas;
|
|
};
|
|
|
|
DomController.prototype.initCanvas = function (canvas) {
|
|
nc.trigger(nc.ns.client.view.display.change, Screenfull.isFullscreen);
|
|
};
|
|
|
|
DomController.prototype.setConnected = function(connected) {
|
|
if(connected) {
|
|
document.body.style.backgroundColor = '';
|
|
} else {
|
|
document.body.style.backgroundColor = '#aaaaaa';
|
|
this.ping.innerHTML = "Disconnected. ".replace(/ /g, ' ');
|
|
this.ping.style.color = "#ff0000";
|
|
/*
|
|
self = this;
|
|
setTimeout(function(){self.ping.innerHTML = "Reload Page...".replace(/ /g, ' ');}, 3000);
|
|
setTimeout(function(){self.ping.innerHTML = "Reload in 3...".replace(/ /g, ' ');}, 6000);
|
|
setTimeout(function(){self.ping.innerHTML = "Reload in 2...".replace(/ /g, ' ');}, 7000);
|
|
setTimeout(function(){self.ping.innerHTML = "Reload in 1...".replace(/ /g, ' ');}, 8000);
|
|
setTimeout(function(){self.ping.innerHTML = "Reload now. ".replace(/ /g, ' '); location.reload(); }, 9000);
|
|
*/
|
|
}
|
|
};
|
|
|
|
|
|
return new DomController();
|
|
|
|
}); |