/** 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. var reconnecting = false; /** * 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.')); } function reconnectServer(serverName) { $('#disconnectionModal').hide(); reconnecting = true; openServer(serverName); } /** * Prepare and show server to user */ function openServer(serverName){ //Hide welcome div if user is not in welcome page $("#welcomeContainer").hide(); $("#serverContainer").show(); $("#serverTitle").text(serverName); //Change server name and related info $("#consoleTextArea").css("font-size", persistenceManager.getSetting("consoleFontSize")+"px"); if (!reconnecting && typeof connectionManager.activeConnection === 'undefined') $("#consoleTextArea").text(""); $("#commandInput").prop("disabled", false); $("#sendCommandButton").prop("disabled", false); //New server, new variables: autoPasswordCompleted = false; commandHistoryIndex = -1; //Reset command history index connectionManager.loadConnection(serverName); //Create or retrieve connection //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) { if (reconnecting) reconnecting = false; else 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(); }, persistenceManager.getSetting("infoRefreshInterval")); } } /** * Write to console */ function writeToWebConsole(msg, time){ const textArea = document.getElementById("consoleTextArea") const isScrolledDown = (textArea.scrollHeight-textArea.scrollTop-40) <= ($("#consoleTextArea").height()+100); //Write to div, replacing < to < (to avoid XSS) and replacing new line to br. msg = msg.replace(/"); // LINUX color codes: msg = msg.replace(/\033\[30m/g, ""); //&0 msg = msg.replace(/\033\[90m/g, ""); //&0 msg = msg.replace(/\033\[34m/g, ""); //&1 msg = msg.replace(/\033\[32m/g, ""); //&2 was \/ msg = msg.replace(/\033\[92m/g, ""); //&2 was #00AA00 // msg = msg.replace(/\033\[36m/g, ""); //&3 msg = msg.replace(/\033\[96m/g, ""); //&3 was #00AAAA // msg = msg.replace(/\033\[31m/g, ""); //&4 msg = msg.replace(/\033\[91m/g, ""); //&4 was #AA0000 msg = msg.replace(/\033\[35m/g, ""); //&5 msg = msg.replace(/\033\[33m/g, ""); //&6 msg = msg.replace(/\033\[37m/g, ""); //&7 msg = msg.replace(/\033\[30m/g, ""); //&8 msg = msg.replace(/\033\[34m/g, ""); //&9 msg = msg.replace(/\033\[33m/g, ""); //&a was #55FF55 msg = msg.replace(/\033\[36m/g, ""); //&b was #55FFFF msg = msg.replace(/\033\[31m/g, ""); //&c was #FF5555 // msg = msg.replace(/\033\[35m/g, ""); //&d msg = msg.replace(/\033\[95m/g, ""); //&d // msg = msg.replace(/\033\[33m/g, ""); //&e msg = msg.replace(/\033\[93m/g, ""); //&e // msg = msg.replace(/\033\[37m/g, ""); //&f msg = msg.replace(/\033\[97m/g, ""); //&f msg = msg.replace(/\033\[1m/g, ""); //&l // msg = msg.replace(/\022\[2m/g, ""); //&m msg = msg.replace(/\033\[3m/g, ""); //&o msg = msg.replace(/\033\[4m/g, ""); //&n msg = msg.replace(/\033\[8m/g, ""); //crazy text msg = msg.replace(/\033\[0m/g, ""); //&r //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 codes. 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') {//if time is present if(time !== null) msg = "[" + time + "] " + msg; //and not null else msg = "["+new Date().toLocaleTimeString()+"] "+msg; // and is null } } $("#consoleTextArea").append(msg + "
"); if(isScrolledDown){ 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 + "%"); if(connectionManager.activeConnection){ $("#playerList").text(connectionManager.activeConnection.players.splice(",  ")); } else $("#playerList").text(""); } /** * 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 and 'Disconnect' button */ 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'); $("#disconnectionModalReconnectButton").click(function() { reconnectServer(serverName); }); } connectionManager.deleteConnection(serverName, false); clearInterval(statusCommandsInterval); // Stop gathering info from server statusCommandsInterval = -1; commandHistoryIndex = -1; //Reset command history index } /** * Shows welcome screen */ function backToHomepage(){ clearInterval(statusCommandsInterval); //Stop gathering info from server if(connectionManager.activeConnection === null) { $("#consoleTextArea").text(""); } statusCommandsInterval = -1; commandHistoryIndex = -1; //Reset command history index writePlayerInfo(0, 0); writeCpuInfo(0); writeRamInfo(0, 0, 0); //Clear all server indicators $("#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'); } }