/** Main JS file for WebConsole. https://github.com/mesacarlos 2019-2020 Carlos Mesa under MIT License. */ /** * Global variables */ const persistenceManager = new WebConsolePersistenceManager(); const connectionManager = new WebConsoleManager(); let lang; let autoPasswordCompleted = false; //When true, saved password was used. If a 401 is received, then saved password is not correct let statusCommandsInterval = -1; let commandHistoryIndex = -1; //Saves current command history index. -1 when not browsing history. /** * Load list of servers in file servers.json * and auto update in next request when file is changed */ function readServerList() { let hash = persistenceManager.getSetting('server:hash') /** * Hash code function used for compare version of file servers.json * https://stackoverflow.com/questions/7616461/generate-a-hash-from-string-in-javascript */ const hashCode = s => s.split('').reduce((a,b)=>{a=((a<<5)-a)+b.charCodeAt(0);return a&a},0); fetch('servers.json') .then(res => res.text()) .then(json => { if (hash !== hashCode(json)) { persistenceManager.setSetting('server:hash', hashCode(json)) JSON.parse(json).forEach(server => persistenceManager.saveServer(server)) } }) .then(updateServerList) .catch(() => console.info('Ignore load new list in file servers.json.')); } /** * Prepare and show server to user */ function openServer(serverName){ //Hide welcome div if user is not in welcome page $("#welcomeContainer").hide(); $("#serverContainer").show(); //Change server name and related info $("#serverTitle").text(serverName); $("#consoleTextArea").text(""); $("#commandInput").prop("disabled", false); $("#sendCommandButton").prop("disabled", false); //New server, new variables: autoPasswordCompleted = false; commandHistoryIndex = -1; //Reset command history index //Create or retrieve connection connectionManager.loadConnection(serverName); //Load saved messages let i; const messages = connectionManager.activeConnection.messages; for(i = 0; i < messages.length; i++){ if(messages[i].status !== 401){ onWebSocketsMessage(messages[i]); } } //Subscribe a function connectionManager.activeConnection.subscribe(onWebSocketsMessage); } function onWebSocketsMessage(message){ switch (message.status) { case 10: //Console Output writeToWebConsole(message.message, message.time); break; case 200: //LoggedIn writeToWebConsole(message.message); //Show user and permissions $("#loggedUsernameLabel").text(message.username); $("#loggedUserTypeLabel").text(message.as); //Disable command bar if user is viewer if(message.as.toLowerCase() === "viewer"){ $("#commandInput").prop("disabled", true); $("#sendCommandButton").prop("disabled", true); } //Read log file if enabled if(connectionManager.activeConnection.isLogged === false){ connectionManager.activeConnection.isLogged = true; if(persistenceManager.getSetting("retrieveLogFile") === true) connectionManager.askForLogs(); } break; case 400: //Unknown Command writeToWebConsole(message.message); break; case 401: //Waiting for login. Show password modal or retrieve password const savedPwd = persistenceManager.getServer(connectionManager.activeConnection.serverName).serverPassword; if(typeof savedPwd !== "undefined" && !autoPasswordCompleted){ connectionManager.sendPassword(savedPwd); autoPasswordCompleted = true; }else{ $('#passwordModal').modal('show'); } break; case 1000: //Players writePlayerInfo(message.connectedPlayers, message.maxPlayers); connectionManager.activeConnection.players = JSON.parse(message.players); break; case 1001: //Cpu Usage writeCpuInfo(message.usage); break; case 1002: //RAM Usage writeRamInfo(message.free, message.used, message.max); break; case 1003: //Server TPS writeTpsInfo(message.tps, 20); break; default: console.log('Unknown server response:'); } console.log(message); //Add interval for Players, CPU and RAM info, if not set if(statusCommandsInterval === -1 && message.status !== 401){ statusCommandsInterval = setInterval(function(){ connectionManager.askForInfo(); }, 2500); } } /** * Write to console */ function writeToWebConsole(msg, time){ const isScrolledDown = document.getElementById("consoleTextArea").scrollHeight - document.getElementById("consoleTextArea").scrollTop - 40 === $("#consoleTextArea").height(); //Write to div, replacing < to < (to avoid XSS) and replacing new line to br. msg = msg.replace(/"); //Color filter for Windows (thanks to SuperPykkon) msg = msg.replace(/\[0;30;22m/g, ""); //&0 msg = msg.replace(/\[0;34;22m/g, ""); //&1 msg = msg.replace(/\[0;32;22m/g, ""); //&2 msg = msg.replace(/\[0;36;22m/g, ""); //&3 msg = msg.replace(/\[0;31;22m/g, ""); //&4 msg = msg.replace(/\[0;35;22m/g, ""); //&5 msg = msg.replace(/\[0;33;22m/g, ""); //&6 msg = msg.replace(/\[0;37;22m/g, ""); //&7 msg = msg.replace(/\[0;30;1m/g, ""); //&8 msg = msg.replace(/\[0;34;1m/g, ""); //&9 msg = msg.replace(/\[0;32;1m/g, ""); //&a msg = msg.replace(/\[0;36;1m/g, ""); //&b msg = msg.replace(/\[0;31;1m/g, ""); //&c msg = msg.replace(/\[0;35;1m/g, ""); //&d msg = msg.replace(/\[0;33;1m/g, ""); //&e msg = msg.replace(/\[0;37;1m/g, ""); //&f msg = msg.replace(/\[m/g, ""); //&f //Color filter for UNIX (This is easier!) //span may not be closed every time but browsers will do for ourselves msg = msg.replace(/§0/g, ""); //&0 msg = msg.replace(/§1/g, ""); //&1 msg = msg.replace(/§2/g, ""); //&2 msg = msg.replace(/§3/g, ""); //&3 msg = msg.replace(/§4/g, ""); //&4 msg = msg.replace(/§5/g, ""); //&5 msg = msg.replace(/§6/g, ""); //&6 msg = msg.replace(/§7/g, ""); //&7 msg = msg.replace(/§8/g, ""); //&8 msg = msg.replace(/§9/g, ""); //&9 msg = msg.replace(/§a/g, ""); //&a msg = msg.replace(/§b/g, ""); //&b msg = msg.replace(/§c/g, ""); //&c msg = msg.replace(/§d/g, ""); //&d msg = msg.replace(/§e/g, ""); //&e msg = msg.replace(/§f/g, ""); //&f msg = msg.replace(/§l/g, ""); //&l msg = msg.replace(/§m/g, ""); //&m msg = msg.replace(/§n/g, ""); //&n msg = msg.replace(/§o/g, ""); //&o msg = msg.replace(/§r/g, ""); //&r //Color filter for MC 1.18 (Also easy :D) //span may not be closed every time but browsers will do for ourselves msg = msg.replace(/0/g, ""); //&0 msg = msg.replace(/1/g, ""); //&1 msg = msg.replace(/2/g, ""); //&2 msg = msg.replace(/3/g, ""); //&3 msg = msg.replace(/4/g, ""); //&4 msg = msg.replace(/5/g, ""); //&5 msg = msg.replace(/6/g, ""); //&6 msg = msg.replace(/7/g, ""); //&7 msg = msg.replace(/8/g, ""); //&8 msg = msg.replace(/9/g, ""); //&9 msg = msg.replace(/a/g, ""); //&a msg = msg.replace(/b/g, ""); //&b msg = msg.replace(/c/g, ""); //&c msg = msg.replace(/d/g, ""); //&d msg = msg.replace(/e/g, ""); //&e msg = msg.replace(/f/g, ""); //&f msg = msg.replace(/l/g, ""); //&l msg = msg.replace(/m/g, ""); //&m msg = msg.replace(/n/g, ""); //&n msg = msg.replace(/o/g, ""); //&o msg = msg.replace(/r/g, ""); //&r //Append datetime if enabled if(persistenceManager.getSetting("dateTimePrefix")){ if(typeof time !== 'undefined' && time !== null) //if time is present and not null msg = "[" + time + "] " + msg; else if(typeof time !== 'undefined' && time === null) //if time is present and null ; //no time (is already printed) else msg = "[" + new Date().toLocaleTimeString() + "] " + msg; } $("#consoleTextArea").append(msg + "
"); if(isScrolledDown){ const textarea = document.getElementById('consoleTextArea'); textarea.scrollTop = textarea.scrollHeight; } } /** * Fill connected players card */ function writePlayerInfo(connected, maximum){ $("#connectedPlayers").text(connected); $("#maxPlayers").text(maximum); const percent = (connected / maximum) * 100; $("#playerProgressBar").width(percent + "%"); } /** * Fill CPU info card */ function writeCpuInfo(usage){ $("#cpuInfo").text(usage + "%"); $("#CpuProgressBar").width(usage + "%"); } /** * Fill RAM info card */ function writeRamInfo(free, used, total){ $("#usedRam").text(used); $("#totalRam").text(total); const percent = (used / total) * 100; $("#RamProgressBar").width(percent + "%"); } /** * Fill TPS info card */ function writeTpsInfo(tps, max){ if(tps > 20) { tps = 20; } $("#tps").text(tps); $("#maxTps").text(max); const percent = (tps / max) * 100; $("#TpsProgressBar").width(percent + "%"); } /** * Called from WebConsoleConnector only. */ function closedConnection(serverName){ if(connectionManager.activeConnection.serverName === serverName){ //Disable command input and button $("#commandInput").prop("disabled", true); $("#sendCommandButton").prop("disabled", true); //Inform user $('#disconnectionModal').modal('show'); } connectionManager.deleteConnection(serverName, true); } /** * Shows welcome screen */ function backToHomepage(){ //Stop gathering info from server clearInterval(statusCommandsInterval); statusCommandsInterval = -1; //Reset command history index commandHistoryIndex = -1; //Clear all server indicators writePlayerInfo(0, 0); writeCpuInfo(0); writeRamInfo(0, 0, 0); $("#welcomeContainer").show(); $("#serverContainer").hide(); } /** * Update dropdown with saved server list */ function updateServerList(){ //Delete all servers in dropdown $('.servermenuitem').remove(); //Add all servers const servers = persistenceManager.getAllServers(); for(let i = 0; i < servers.length; i++){ $('#ServerListDropDown').append('' + servers[i].serverName.replace(//g,">").replace(/'/g,"").replace(/"/g,"") + ''); } //Show a "no servers" message when no servers are added if(servers.length === 0){ $('#ServerListDropDown').append('No servers added'); } }