prettyify code
This commit is contained in:
@ -1,176 +1,205 @@
|
||||
export class SocketManager {
|
||||
constructor (app) {
|
||||
this._app = app
|
||||
this._hasRequestedHistoryGraph = false
|
||||
this._reconnectDelayBase = 0
|
||||
constructor(app) {
|
||||
this._app = app;
|
||||
this._hasRequestedHistoryGraph = false;
|
||||
this._reconnectDelayBase = 0;
|
||||
}
|
||||
|
||||
reset () {
|
||||
this._hasRequestedHistoryGraph = false
|
||||
reset() {
|
||||
this._hasRequestedHistoryGraph = false;
|
||||
}
|
||||
|
||||
createWebSocket () {
|
||||
let webSocketProtocol = 'ws:'
|
||||
if (location.protocol === 'https:') {
|
||||
webSocketProtocol = 'wss:'
|
||||
createWebSocket() {
|
||||
let webSocketProtocol = "ws:";
|
||||
if (location.protocol === "https:") {
|
||||
webSocketProtocol = "wss:";
|
||||
}
|
||||
|
||||
this._webSocket = new WebSocket(`${webSocketProtocol}//${location.host}`)
|
||||
this._webSocket = new WebSocket(`${webSocketProtocol}//${location.host}`);
|
||||
|
||||
// The backend will automatically push data once connected
|
||||
this._webSocket.onopen = () => {
|
||||
this._app.caption.set('Loading...')
|
||||
this._app.caption.set("Loading...");
|
||||
|
||||
// Reset reconnection scheduling since the WebSocket has been established
|
||||
this._reconnectDelayBase = 0
|
||||
}
|
||||
this._reconnectDelayBase = 0;
|
||||
};
|
||||
|
||||
this._webSocket.onclose = (event) => {
|
||||
this._app.handleDisconnect()
|
||||
this._app.handleDisconnect();
|
||||
|
||||
// Modify page state to display loading overlay
|
||||
// Code 1006 denotes "Abnormal closure", most likely from the server or client losing connection
|
||||
// See https://developer.mozilla.org/en-US/docs/Web/API/CloseEvent
|
||||
// Treat other codes as active errors (besides connectivity errors) when displaying the message
|
||||
if (event.code === 1006) {
|
||||
this._app.caption.set('Lost connection!')
|
||||
this._app.caption.set("Lost connection!");
|
||||
} else {
|
||||
this._app.caption.set('Disconnected due to error.')
|
||||
this._app.caption.set("Disconnected due to error.");
|
||||
}
|
||||
|
||||
// Schedule socket reconnection attempt
|
||||
this.scheduleReconnect()
|
||||
}
|
||||
this.scheduleReconnect();
|
||||
};
|
||||
|
||||
this._webSocket.onmessage = (message) => {
|
||||
const payload = JSON.parse(message.data)
|
||||
const payload = JSON.parse(message.data);
|
||||
|
||||
switch (payload.message) {
|
||||
case 'init':
|
||||
this._app.setPublicConfig(payload.config)
|
||||
case "init":
|
||||
this._app.setPublicConfig(payload.config);
|
||||
|
||||
// Display the main page component
|
||||
// Called here instead of syncComplete so the DOM can be drawn prior to the graphs being drawn
|
||||
this._app.setPageReady(true)
|
||||
this._app.setPageReady(true);
|
||||
|
||||
// Allow the graphDisplayManager to control whether or not the historical graph is loaded
|
||||
// Defer to isGraphVisible from the publicConfig to understand if the frontend will ever receive a graph payload
|
||||
if (this._app.publicConfig.isGraphVisible) {
|
||||
this.sendHistoryGraphRequest()
|
||||
this.sendHistoryGraphRequest();
|
||||
}
|
||||
|
||||
payload.servers.forEach((serverPayload, serverId) => {
|
||||
this._app.addServer(serverId, serverPayload, payload.timestampPoints)
|
||||
})
|
||||
this._app.addServer(
|
||||
serverId,
|
||||
serverPayload,
|
||||
payload.timestampPoints
|
||||
);
|
||||
});
|
||||
|
||||
// Init payload contains all data needed to render the page
|
||||
// Alert the app it is ready
|
||||
this._app.handleSyncComplete()
|
||||
this._app.handleSyncComplete();
|
||||
|
||||
break
|
||||
break;
|
||||
|
||||
case 'updateServers': {
|
||||
for (let serverId = 0; serverId < payload.updates.length; serverId++) {
|
||||
case "updateServers": {
|
||||
for (
|
||||
let serverId = 0;
|
||||
serverId < payload.updates.length;
|
||||
serverId++
|
||||
) {
|
||||
// The backend may send "update" events prior to receiving all "add" events
|
||||
// A server has only been added once it's ServerRegistration is defined
|
||||
// Checking undefined protects from this race condition
|
||||
const serverRegistration = this._app.serverRegistry.getServerRegistration(serverId)
|
||||
const serverUpdate = payload.updates[serverId]
|
||||
const serverRegistration =
|
||||
this._app.serverRegistry.getServerRegistration(serverId);
|
||||
const serverUpdate = payload.updates[serverId];
|
||||
|
||||
if (serverRegistration) {
|
||||
serverRegistration.handlePing(serverUpdate, payload.timestamp)
|
||||
serverRegistration.updateServerStatus(serverUpdate, this._app.publicConfig.minecraftVersions)
|
||||
serverRegistration.handlePing(serverUpdate, payload.timestamp);
|
||||
serverRegistration.updateServerStatus(
|
||||
serverUpdate,
|
||||
this._app.publicConfig.minecraftVersions
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// Bulk add playerCounts into graph during #updateHistoryGraph
|
||||
if (payload.updateHistoryGraph) {
|
||||
this._app.graphDisplayManager.addGraphPoint(payload.timestamp, Object.values(payload.updates).map(update => update.playerCount))
|
||||
this._app.graphDisplayManager.addGraphPoint(
|
||||
payload.timestamp,
|
||||
Object.values(payload.updates).map((update) => update.playerCount)
|
||||
);
|
||||
|
||||
// Run redraw tasks after handling bulk updates
|
||||
this._app.graphDisplayManager.redraw()
|
||||
this._app.graphDisplayManager.redraw();
|
||||
}
|
||||
|
||||
this._app.percentageBar.redraw()
|
||||
this._app.updateGlobalStats()
|
||||
this._app.percentageBar.redraw();
|
||||
this._app.updateGlobalStats();
|
||||
|
||||
break
|
||||
break;
|
||||
}
|
||||
|
||||
case 'historyGraph': {
|
||||
this._app.graphDisplayManager.buildPlotInstance(payload.timestamps, payload.graphData)
|
||||
case "historyGraph": {
|
||||
this._app.graphDisplayManager.buildPlotInstance(
|
||||
payload.timestamps,
|
||||
payload.graphData
|
||||
);
|
||||
|
||||
// Build checkbox elements for graph controls
|
||||
let lastRowCounter = 0
|
||||
let controlsHTML = ''
|
||||
let lastRowCounter = 0;
|
||||
let controlsHTML = "";
|
||||
|
||||
this._app.serverRegistry.getServerRegistrations()
|
||||
.map(serverRegistration => serverRegistration.data.name)
|
||||
this._app.serverRegistry
|
||||
.getServerRegistrations()
|
||||
.map((serverRegistration) => serverRegistration.data.name)
|
||||
.sort()
|
||||
.forEach(serverName => {
|
||||
const serverRegistration = this._app.serverRegistry.getServerRegistration(serverName)
|
||||
.forEach((serverName) => {
|
||||
const serverRegistration =
|
||||
this._app.serverRegistry.getServerRegistration(serverName);
|
||||
|
||||
controlsHTML += `<td><label>
|
||||
<input type="checkbox" class="graph-control" minetrack-server-id="${serverRegistration.serverId}" ${serverRegistration.isVisible ? 'checked' : ''}>
|
||||
<input type="checkbox" class="graph-control" minetrack-server-id="${
|
||||
serverRegistration.serverId
|
||||
}" ${serverRegistration.isVisible ? "checked" : ""}>
|
||||
${serverName}
|
||||
</label></td>`
|
||||
</label></td>`;
|
||||
|
||||
// Occasionally break table rows using a magic number
|
||||
if (++lastRowCounter % 6 === 0) {
|
||||
controlsHTML += '</tr><tr>'
|
||||
controlsHTML += "</tr><tr>";
|
||||
}
|
||||
})
|
||||
});
|
||||
|
||||
// Apply generated HTML and show controls
|
||||
document.getElementById('big-graph-checkboxes').innerHTML = `<table><tr>${controlsHTML}</tr></table>`
|
||||
document.getElementById('big-graph-controls').style.display = 'block'
|
||||
document.getElementById(
|
||||
"big-graph-checkboxes"
|
||||
).innerHTML = `<table><tr>${controlsHTML}</tr></table>`;
|
||||
document.getElementById("big-graph-controls").style.display = "block";
|
||||
|
||||
// Bind click event for updating graph data
|
||||
this._app.graphDisplayManager.initEventListeners()
|
||||
break
|
||||
this._app.graphDisplayManager.initEventListeners();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
scheduleReconnect () {
|
||||
scheduleReconnect() {
|
||||
// Release any active WebSocket references
|
||||
this._webSocket = undefined
|
||||
this._webSocket = undefined;
|
||||
|
||||
this._reconnectDelayBase++
|
||||
this._reconnectDelayBase++;
|
||||
|
||||
// Exponential backoff for reconnection attempts
|
||||
// Clamp ceiling value to 30 seconds
|
||||
this._reconnectDelaySeconds = Math.min((this._reconnectDelayBase * this._reconnectDelayBase), 30)
|
||||
this._reconnectDelaySeconds = Math.min(
|
||||
this._reconnectDelayBase * this._reconnectDelayBase,
|
||||
30
|
||||
);
|
||||
|
||||
const reconnectInterval = setInterval(() => {
|
||||
this._reconnectDelaySeconds--
|
||||
this._reconnectDelaySeconds--;
|
||||
|
||||
if (this._reconnectDelaySeconds === 0) {
|
||||
// Explicitly clear interval, this avoids race conditions
|
||||
// #clearInterval first to avoid potential errors causing pre-mature returns
|
||||
clearInterval(reconnectInterval)
|
||||
clearInterval(reconnectInterval);
|
||||
|
||||
// Update displayed text
|
||||
this._app.caption.set('Reconnecting...')
|
||||
this._app.caption.set("Reconnecting...");
|
||||
|
||||
// Attempt reconnection
|
||||
// Only attempt when reconnectDelaySeconds === 0 and not <= 0, otherwise multiple attempts may be started
|
||||
this.createWebSocket()
|
||||
this.createWebSocket();
|
||||
} else if (this._reconnectDelaySeconds > 0) {
|
||||
// Update displayed text
|
||||
this._app.caption.set(`Reconnecting in ${this._reconnectDelaySeconds}s...`)
|
||||
this._app.caption.set(
|
||||
`Reconnecting in ${this._reconnectDelaySeconds}s...`
|
||||
);
|
||||
}
|
||||
}, 1000)
|
||||
}, 1000);
|
||||
}
|
||||
|
||||
sendHistoryGraphRequest () {
|
||||
sendHistoryGraphRequest() {
|
||||
if (!this._hasRequestedHistoryGraph) {
|
||||
this._hasRequestedHistoryGraph = true
|
||||
this._hasRequestedHistoryGraph = true;
|
||||
|
||||
// Send request as a plain text string to avoid the server needing to parse JSON
|
||||
// This is mostly to simplify the backend server's need for error handling
|
||||
this._webSocket.send('requestHistoryGraph')
|
||||
this._webSocket.send("requestHistoryGraph");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user