From f38fd340f4e807ae903b8966454cea91ff0f4395 Mon Sep 17 00:00:00 2001 From: Daniel Xie Date: Thu, 24 Nov 2016 16:30:33 -0600 Subject: [PATCH] Implement Script Editor (mostly) --- css/menupages.css | 68 +++++++++++++++++++++++++++++++++++ css/styles.css | 9 +---- index.html | 39 +++++++++++--------- src/Script.js | 86 +++++++++++++++++++++++++++++++++++++++++--- src/Server.js | 1 - src/Terminal.js | 92 ++++++++++++++++++++++++++++++++++++++++------- src/engine.js | 86 +++++++++++++++++++++++++++++++++++--------- 7 files changed, 322 insertions(+), 59 deletions(-) create mode 100644 css/menupages.css diff --git a/css/menupages.css b/css/menupages.css new file mode 100644 index 000000000..263b176c9 --- /dev/null +++ b/css/menupages.css @@ -0,0 +1,68 @@ +/* CSS for different main menu pages, such as character info, script editor, etc (but excluding + terminal which has its own page) */ + +/* Character Info */ +#character-container { + position: fixed; + padding-top: 10px; + padding-left: 10px; + margin-left: 10%; + width: 99%; +} + + +/* Script Editor */ +/* This temp element is used for auto adjusting filename field */ +.tmp-element { + visibility: hidden; + white-space: pre; +} + +#script-editor-container { + position: fixed; + padding-top: 10px; + padding-left: 10px; + height: 100%; + margin-left: 10%; + width: 99%; + color: #66ff33; + +} + +#script-editor-filename-row-div { + color: #66ff33; +} + +#script-editor-filename-tag { + float: left; +} + +#script-editor-filename { + float: left; + resize: none; + color: #66ff33; + width: 100%; + + border: none; + outline: none; + -webkit-box-shadow: none; + -moz-box-shadow: none; + box-shadow: none; +} + + +#script-editor-status { + float: left; + color: #ffffff; +} + +#script-editor-text { + color: #66ff33; + width: 90%; + height: 100%; + + outline: none; + -webkit-box-shadow: none; + -moz-box-shadow: none; + box-shadow: none; +} \ No newline at end of file diff --git a/css/styles.css b/css/styles.css index 3bcc21127..caaa1f123 100644 --- a/css/styles.css +++ b/css/styles.css @@ -13,6 +13,7 @@ p { color: #66ff33; } + .mainmenu { list-style-type: none; margin: 0; @@ -46,14 +47,6 @@ p { color: white; } -#character-container { - position: fixed; - padding-top: 10px; - padding-left: 10px; - margin-left: 10%; - width: 99%; -} - /* Style all the buttons */ input[type=button] { width: 100px; diff --git a/index.html b/index.html index 3a009168f..4b12668de 100644 --- a/index.html +++ b/index.html @@ -5,6 +5,7 @@ Netburner + @@ -22,10 +23,10 @@ - - + + +
- -
$
-
+ + +

-
+ + + +
+
+

Script name (edit below):

+ +
+

+
+

+ +
+ - - diff --git a/src/Script.js b/src/Script.js index 6e5e00c86..11a95ef44 100644 --- a/src/Script.js +++ b/src/Script.js @@ -1,14 +1,77 @@ /* Script.js * Script object */ + +//Define commands in script editor (ctrl x to close, etc.) +$(document).keydown(function(e) { + if (Engine.currentPage == Engine.Page.ScriptEditor) { + if (e.keyCode == 88 && e.ctrlKey) { + var filename = document.getElementById("script-editor-filename").value; + + if (checkValidFilename(filename) == false) { + postScriptEditorStatus("Script filename can contain only alphanumerics, hyphens, and underscores"); + return; + } + + filename += ".script"; + + //If the current script matches one thats currently running, throw an error + for (var i = 0; i < Player.currentServer.runningScripts.length; i++) { + if (filename == Player.currentServer.runningScripts[i].filename) { + postScriptEditorStatus("Cannot write to script that is currently running!"); + return; + } + } + + //If the current script already exists on the server, overwrite it + for (var i = 0; i < Player.currentServer.scripts.length; i++) { + if (filename == Player.currentServer.scripts[i].filename) { + Player.currentServer.scripts[i].saveScript(); + Engine.loadTerminalContent(); + return; + } + } + + //If the current script does NOT exist, create a new one + var script = new Script(); + script.saveScript(); + Player.currentServer.scripts.push(script); + Engine.loadTerminalContent(); + } + } +}); + +//Checks that the string contains only valid characters for a filename, which are alphanumeric, +// underscores and hyphens +function checkValidFilename(filename) { + var regex = /^[a-zA-Z0-9_-]+$/; + + if (filename.match(regex)) { + return true; + } + return false; +} + +var ScriptEditorLastStatus = null; +function postScriptEditorStatus(text) { + document.getElementById("script-editor-status").innerHTML = text; + + clearTimeout(ScriptEditorLastStatus); + ScriptEditorLastStatus = setTimeout(function() { + document.getElementById("script-editor-status").innerHTML = ""; + }, 3000); +} + function Script() { //Function queue that holds the next functions to be //executed in this script. A function from this queue //is executed every second (this may change) this.functionQueue = []; + this.filename = ""; this.code = ""; this.ramUsage = 0; + this.server = null; //Which server this script is on /* Properties to calculate offline progress. Only applies for infinitely looping scripts */ @@ -22,18 +85,31 @@ function Script() { //Which servers are hacked in one iteration of the script. May contain duplicates this.serversHacked = []; -} +}; //Execute the next function in the Script's function queue -Script.prototype.executeNext() { +Script.prototype.executeNext = function() { if (this.functionQueue.length <= 0) {return;} //Shift the next element off ths function queue and then execute it (this.functionQueue.shift())(); } -Script.prototype.setCode(code) { - this.code = code; +//Get the script data from the Script Editor and save it to the object +Script.prototype.saveScript = function() { + if (Engine.currentPage == Engine.Page.ScriptEditor) { + //Update code and filename + var code = document.getElementById("script-editor-text").value; + this.code = code; + + var filename = document.getElementById("script-editor-filename").value + ".script"; + this.filename = filename; + + //Server + this.server = Player.currentServer; + + //TODO Calculate/update number of instructions, ram usage, execution time, etc. + } } /* Wrapper object that wraps a function with its arguments. @@ -51,7 +127,7 @@ Script.prototype.setCode(code) { * fooObj(); * */ -function functionObject = function(fn, context, params) { +function functionObject(fn, context, params) { return function() { fn.apply(context, params); } diff --git a/src/Server.js b/src/Server.js index c7d2ec07d..38846bb4c 100644 --- a/src/Server.js +++ b/src/Server.js @@ -19,7 +19,6 @@ function Server() { this.scripts = []; this.runningScripts = []; //Scripts currently being run - this.scriptEnvs = []; //The environment for all of the running scripts. Matched by index number this.programs = []; /* Hacking information (only valid for "foreign" aka non-purchased servers) */ diff --git a/src/Terminal.js b/src/Terminal.js index 56b932995..e3a12ee70 100644 --- a/src/Terminal.js +++ b/src/Terminal.js @@ -19,20 +19,35 @@ var postNetburnerText = function() { post("Netburner v1.0"); } -//command is checked on enter key press +//Defines what happens when enter is pressed (keycode 13) $(document).keyup(function(event) { - if (event.keyCode == 13) { - var command = $('input[class=terminal-input]').val(); - if (command.length > 0) { - post("> " + command); - - //TODO Do i have to switch the order of these two? - Terminal.executeCommand(command); - $('input[class=terminal-input]').val(""); + //Terminal + if (Engine.currentPage == Engine.Page.Terminal) { + if (event.keyCode == 13) { + var command = $('input[class=terminal-input]').val(); + if (command.length > 0) { + post("> " + command); + + //TODO Do i have to switch the order of these two? + Terminal.executeCommand(command); + $('input[class=terminal-input]').val(""); + } } } }); +//Keep terminal in focus +$(document).ready(function() { + if (Engine.currentPage == Engine.Page.Terminal) { + $('.terminal-input').focus(); + } +}); +$(document).keydown(function() { + if (Engine.currentPage == Engine.Page.Terminal) { + $('.terminal-input').focus(); + } +}) + var Terminal = { //Flags to determine whether the player is currently running a hack or an analyze hackFlag: false, @@ -136,6 +151,9 @@ var Terminal = { switch (commandArray[0]) { case "analyze": + if (commandArray.length != 1) { + post("Incorrect usage of analyze command. Usage: analyze"); return; + } //Analyze the current server for information console.log("analyze terminal command called"); Terminal.analyzeFlag = true; @@ -151,6 +169,9 @@ var Terminal = { break; case "clear": case "cls": + if (commandArray.length != 1) { + post("Incorrect usage of clear/cls command. Usage: clear/cls"); return; + } console.log("cls/clear terminal command called"); $("#terminal tr:not(:last)").remove(); postNetburnerText(); @@ -177,12 +198,18 @@ var Terminal = { post("Host not found"); break; case "df": + if (commandArray.length != 1) { + post("Incorrect usage of df command. Usage: df"); return; + } console.log("df terminal command called"); post("Total: " + Player.currentServer.maxRam.toString() + " GB"); post("Used: " + Player.currentServer.ramUsed.toString() + " GB"); post("Available: " + (Player.currentServer.maxRam - Player.currentServer.ramUsed).toString() + " GB"); break; case "hack": + if (commandArray.length != 1) { + post("Incorrect usage of hack command. Usage: hack"); return; + } //Hack the current PC (usually for money) //You can't hack your home pc or servers you purchased if (Player.currentServer.purchasedByPlayer) { @@ -207,10 +234,16 @@ var Terminal = { //TODO break; case "hostname": + if (commandArray.length != 1) { + post("Incorrect usage of hostname command. Usage: hostname"); return; + } //Print the hostname of current system post(Player.currentServer.hostname); break; case "ifconfig": + if (commandArray.length != 1) { + post("Incorrect usage of ifconfig command. Usage: ifconfig"); return; + } //Print the IP address of the current system post(Player.currentServer.ip); break; @@ -218,6 +251,10 @@ var Terminal = { //TODO break; case "ls": + if (commandArray.length != 1) { + post("Incorrect usage of ls command. Usage: ls"); return; + } + //Display all programs and scripts var allFiles = []; @@ -226,7 +263,7 @@ var Terminal = { allFiles.push(Player.currentServer.programs[i]); } for (var i = 0; i < Player.currentServer.scripts.length; i++) { - allFiles.push(Player.currentServer.scripts[i]); + allFiles.push(Player.currentServer.scripts[i].filename); } //Sort the files alphabetically then print each @@ -236,11 +273,41 @@ var Terminal = { post(allFiles[i]); } break; + case "nano": + if (commandArray.length != 2) { + post("Incorrect usage of nano command. Usage: nano [scriptname]"); return; + } + + var filename = commandArray[1]; + + //Can only edit script files + if (filename.endsWith(".script") == false) { + post("Error: Only .script files are editable with nano (filename must end with .scrip)"); return; + } + + //Script name is the filename without the .script at the end + var scriptname = filename.substr(0, filename.indexOf(".script")); + + //Cannot edit scripts that are currently running + for (var i = 0; i < Player.currentServer.runningScripts.length; i++) { + if (filename == Player.currentServer.runningScripts[i].filename) { + post("Cannot open/edit scripts that are currently running!"); return; + } + } + + //Check if the script already exists + for (var i = 0; i < Player.currentServer.scripts.length; i++) { + if (filename == Player.currentServer.scripts[i].filename) { + Engine.loadScriptEditorContent(scriptname, Player.currentServer.scripts[i].code); + return; + } + } + Engine.loadScriptEditorContent(scriptname, ""); + break; case "netstat": case "scan": if (commandArray.length != 1) { - post("Incorrect usage of netstat/scan command. Usage: netstat/scan"); - return; + post("Incorrect usage of netstat/scan command. Usage: netstat/scan"); return; } //Displays available network connections using TCP console.log("netstat/scan terminal command called"); @@ -268,6 +335,7 @@ var Terminal = { entry += hasRoot; post(entry); } + break; case "ps": //TODO break; diff --git a/src/engine.js b/src/engine.js index e71eb00e2..4bd170730 100644 --- a/src/engine.js +++ b/src/engine.js @@ -17,35 +17,52 @@ var Engine = { //Main menu buttons terminalMainMenuButton: null, characterMainMenuButton: null, + scriptEditorMainMenuButton: null, }, //Display objects Display: { //Progress bar - progress: null, + progress: null, //Display for status text (such as "Saved" or "Loaded") - statusText: null, - - hacking_skill: null, + statusText: null, + hacking_skill: null, + //Main menu content - terminalContent: null, - characterContent: null, + terminalContent: null, + characterContent: null, + scriptEditorContent: null, //Character info - characterInfo: null, + characterInfo: null, + + //Script editor text + scriptEditorText: null, }, + //Current page status + Page: { + Terminal: "Terminal", + CharacterInfo: "CharacterInfo", + ScriptEditor: "ScriptEditor", + }, + currentPage: null, + + //Time variables (milliseconds unix epoch time) _lastUpdate: new Date().getTime(), _idleSpeed: 200, //Speed (in ms) at which the main loop is updated //Save function - saveFile: function() { - var tempSaveFile = JSON.stringify(Player); + saveGame: function() { + var PlayerSave = JSON.stringify(Player); + var ForeignServersSave = JSON.stringify(ForeignServers); + //TODO Add factions + companies here when they're done - window.localStorage.setItem("netburnerSave", tempSaveFile); + window.localStorage.setItem("netburnerPlayerSave", PlayerSave); + window.localStorage.setItem("netburnerForeignServersSave", ForeignServersSave) Engine.displayStatusText("Saved!"); }, @@ -53,11 +70,16 @@ var Engine = { //Load saved game function loadSave: function() { //Check to see if file exists - if (!window.localStorage.getItem("netburnerSave")) { - Engine.displayStatusText("No save file present for load!"); - } else { - var tempSaveFile = window.localStorage.getItem("netburnerSave"); - Player = JSON.parse(tempSaveFile); + if (!window.localStorage.getItem("netburnerPlayerSave")) { + //TODO Add this displayStatusText function + Engine.displayStatusText("No Player save file present for load!"); + } else if (!window.localStorage.getItem("netburnerForeignServersSave")) { + Engine.displayStatusText("No Foreign Serverssave file present for load!"); + } else { + var PlayerSave = window.localStorage.getItem("netburnerPlayerSave"); + var ForeignServersSave = window.localStorage.getItem("netburnerForeignServersSave"); + Player = JSON.parse(PlayerSave); + ForeignServers = JSON.parse(ForeignServersSave); Engine.displayStatusText("Loaded successfully!"); } }, @@ -76,18 +98,33 @@ var Engine = { loadTerminalContent: function() { Engine.hideAllContent(); Engine.Display.terminalContent.style.visibility = "visible"; + Engine.currentPage = Engine.Page.Terminal; }, loadCharacterContent: function() { Engine.hideAllContent(); Engine.Display.characterContent.style.visibility = "visible"; Engine.displayCharacterInfo(); + Engine.currentPage = Engine.Page.CharacterInfo; }, + + loadScriptEditorContent: function(filename = "", code = "") { + Engine.hideAllContent(); + Engine.Display.scriptEditorContent.style.visibility = "visible"; + if (filename == "") { + document.getElementById("script-editor-filename").value = "untitled"; + } else { + document.getElementById("script-editor-filename").value = filename; + } + document.getElementById("script-editor-text").value = code; + Engine.currentPage = Engine.Page.ScriptEditor; + }, //Helper function that hides all content hideAllContent: function() { Engine.Display.terminalContent.style.visibility = "hidden"; Engine.Display.characterContent.style.visibility = "hidden"; + Engine.Display.scriptEditorContent.style.visibility = "hidden"; }, /* Display character info */ @@ -178,6 +215,9 @@ var Engine = { /* Initialization */ init: function() { //Initialization functions + //TODO Might need to move these into a new "BeginGame()" function. init() is called + //every time the window is opened and we don't want to do these init() functions every + //time Player.init(); ForeignServers.init(); @@ -216,14 +256,26 @@ var Engine = { Engine.loadCharacterContent(); return false; }); + + Engine.Clickables.scriptEditorMainMenuButton = document.getElementById("create-script-menu-link"); + Engine.Clickables.scriptEditorMainMenuButton.addEventListener("click", function() { + Engine.loadScriptEditorContent(); + return false; + }); Engine.Display.terminalContent = document.getElementById("terminal-container"); + Engine.currentPage = Engine.Page.Terminal; Engine.Display.characterContent = document.getElementById("character-container"); - Engine.Display.characterContent.style.visibility = "hidden"; + Engine.Display.characterContent.style.visibility = "hidden"; + Engine.Display.scriptEditorContent = document.getElementById("script-editor-container"); + Engine.Display.scriptEditorContent.style.visibility = "hidden"; //Character info Engine.Display.characterInfo = document.getElementById("character-info"); - Engine.displayCharacterInfo(); + //Engine.displayCharacterInfo(); - Don't think I need this + + //Script editor + Engine.Display.scriptEditorText = document.getElementById("script-editor-text"); //Message at the top of terminal postNetburnerText();