From e1b8a23f1e0a40d187e3016fe77b3a17d5842e62 Mon Sep 17 00:00:00 2001 From: danielyxie Date: Sat, 2 Mar 2019 19:15:10 -0800 Subject: [PATCH] Started server code refactor --- src/Message/Message.ts | 32 ++ src/{Message.js => Message/MessageHelpers.js} | 41 +- src/Script/RunningScript.ts | 155 ++++++ src/Script/Script.ts | 104 ++++ src/{Script.js => Script/ScriptHelpers.js} | 187 +------ src/Server.js | 468 ------------------ src/Server/AllServers.ts | 111 +++++ src/Server/Server.ts | 303 ++++++++++++ src/Server/ServerHelpers.js | 126 +++++ src/{ => Server}/ServerPurchases.js | 0 src/{ => Server}/SpecialServerIps.js | 0 utils/IPAddress.js | 33 -- utils/IPAddress.ts | 25 + 13 files changed, 868 insertions(+), 717 deletions(-) create mode 100644 src/Message/Message.ts rename src/{Message.js => Message/MessageHelpers.js} (86%) create mode 100644 src/Script/RunningScript.ts create mode 100644 src/Script/Script.ts rename src/{Script.js => Script/ScriptHelpers.js} (85%) mode change 100755 => 100644 delete mode 100644 src/Server.js create mode 100644 src/Server/AllServers.ts create mode 100644 src/Server/Server.ts create mode 100644 src/Server/ServerHelpers.js rename src/{ => Server}/ServerPurchases.js (100%) rename src/{ => Server}/SpecialServerIps.js (100%) delete mode 100644 utils/IPAddress.js create mode 100644 utils/IPAddress.ts diff --git a/src/Message/Message.ts b/src/Message/Message.ts new file mode 100644 index 000000000..1c56ecd8b --- /dev/null +++ b/src/Message/Message.ts @@ -0,0 +1,32 @@ +import { Reviver, + Generic_toJSON, + Generic_fromJSON } from "../../utils/JSONReviver"; + +export class Message { + // Initializes a Message Object from a JSON save state + static fromJSON(value: any): Message { + return Generic_fromJSON(Message, value.data); + } + + // Name of Message file + filename: string = ""; + + // The text contains in the Message + msg: string = "": + + // Flag indicating whether this Message has been received by the player + recvd: boolean = false; + + constructor(filename="", msg="") { + this.filename = filename; + this.msg = msg; + this.recvd = false; + } + + // Serialize the current object to a JSON save state + toJSON(): any { + return Generic_toJSON("Message", this); + } +} + +Reviver.constructors.Message = Message; diff --git a/src/Message.js b/src/Message/MessageHelpers.js similarity index 86% rename from src/Message.js rename to src/Message/MessageHelpers.js index 7ebee10fa..8fc056fb9 100644 --- a/src/Message.js +++ b/src/Message/MessageHelpers.js @@ -1,34 +1,15 @@ -import { Augmentatation } from "./Augmentation/Augmentation"; -import { Augmentations } from "./Augmentation/Augmentations"; -import { AugmentationNames } from "./Augmentation/data/AugmentationNames"; -import { Programs } from "./Programs/Programs"; -import { inMission } from "./Missions"; -import { Player } from "./Player"; -import { redPillFlag } from "./RedPill"; -import { GetServerByHostname } from "./Server"; -import { Settings } from "./Settings/Settings"; +import { Message } from "./Message"; +import { Augmentatation } from "../Augmentation/Augmentation"; +import { Augmentations } from "../Augmentation/Augmentations"; +import { AugmentationNames } from "../Augmentation/data/AugmentationNames"; +import { Programs } from "../Programs/Programs"; +import { inMission } from "../Missions"; +import { Player } from "../Player"; +import { redPillFlag } from "../RedPill"; +import { GetServerByHostname } from "../Server"; +import { Settings } from "../Settings/Settings"; import { dialogBoxCreate, - dialogBoxOpened} from "../utils/DialogBox"; -import {Reviver, Generic_toJSON, - Generic_fromJSON} from "../utils/JSONReviver"; - -/* Message.js */ -function Message(filename="", msg="") { - this.filename = filename; - this.msg = msg; - this.recvd = false; -} - -Message.prototype.toJSON = function() { - return Generic_toJSON("Message", this); -} - - -Message.fromJSON = function(value) { - return Generic_fromJSON(Message, value.data); -} - -Reviver.constructors.Message = Message; + dialogBoxOpened} from "../../utils/DialogBox"; //Sends message to player, including a pop up function sendMessage(msg, forced=false) { diff --git a/src/Script/RunningScript.ts b/src/Script/RunningScript.ts new file mode 100644 index 000000000..c20e3e5f5 --- /dev/null +++ b/src/Script/RunningScript.ts @@ -0,0 +1,155 @@ +// Class representing a Script instance that is actively running. +// A Script can have multiple active instances +import { Script } from "./Script"; +import { IMap } from "../types"; +import { Generic_fromJSON, + Generic_toJSON, + Reviver } from "../../utils/JSONReviver"; + +export class RunningScript { + // Initializes a RunningScript Object from a JSON save state + static fromJSON(value: any): RunningScript { + return Generic_fromJSON(RunningScript, value.data); + } + + // Script arguments + args: any[] = []; + + // Holds a map of servers hacked, where server = key and the value for each + // server is an array of four numbers. The four numbers represent: + // [MoneyStolen, NumTimesHacked, NumTimesGrown, NumTimesWeaken] + // This data is used for offline progress + dataMap: IMap = {}; + + // Script filename + filename: string = ""; + + // This script's logs. An array of log entries + logs: string[] = []; + + // Flag indicating whether the logs have been updated since + // the last time the UI was updated + logUpd: boolean = false; + + // Total amount of hacking experience earned from this script when offline + offlineExpGained: number = 0; + + // Total amount of money made by this script when offline + offlineMoneyMade: number = 0; + + // Number of seconds that the script has been running offline + offlineRunningTime: number = 0.01; + + // Total amount of hacking experience earned from this script when online + onlineExpGained: number = 0; + + // Total amount of money made by this script when online + onlineMoneyMade: number = 0; + + // Number of seconds that this script has been running online + onlineRunningTime: number = 0.01; + + // How much RAM this script uses for ONE thread + ramUsage: number = 0; + + // IP of the server on which this script is running + server: string = ""; + + // Number of threads that this script is running with + threads: number = 1; + + constructor(script: Script | null = null, args: any[] = []) { + if (script == null) { return; } + this.filename = script.filename; + this.args = args; + + this.server = script.server; //IP Address only + this.ramUsage = script.ramUsage; + } + + RunningScript.prototype.getCode = function() { + const server = AllServers[this.server]; + if (server == null) { return ""; } + for (let i = 0; i < server.scripts.length; ++i) { + if (server.scripts[i].filename === this.filename) { + return server.scripts[i].code; + } + } + + return ""; + } + + RunningScript.prototype.getRamUsage = function() { + if (this.ramUsage != null && this.ramUsage > 0) { return this.ramUsage; } // Use cached value + + const server = AllServers[this.server]; + if (server == null) { return 0; } + for (let i = 0; i < server.scripts.length; ++i) { + if (server.scripts[i].filename === this.filename) { + // Cache the ram usage for the next call + this.ramUsage = server.scripts[i].ramUsage; + return this.ramUsage; + } + } + + + return 0; + } + + RunningScript.prototype.log = function(txt) { + if (this.logs.length > Settings.MaxLogCapacity) { + //Delete first element and add new log entry to the end. + //TODO Eventually it might be better to replace this with circular array + //to improve performance + this.logs.shift(); + } + let logEntry = txt; + if (FconfSettings.ENABLE_TIMESTAMPS) { + logEntry = "[" + getTimestamp() + "] " + logEntry; + } + this.logs.push(logEntry); + this.logUpd = true; + } + + RunningScript.prototype.displayLog = function() { + for (var i = 0; i < this.logs.length; ++i) { + post(this.logs[i]); + } + } + + RunningScript.prototype.clearLog = function() { + this.logs.length = 0; + } + + //Update the moneyStolen and numTimesHack maps when hacking + RunningScript.prototype.recordHack = function(serverIp, moneyGained, n=1) { + if (this.dataMap[serverIp] == null || this.dataMap[serverIp].constructor !== Array) { + this.dataMap[serverIp] = [0, 0, 0, 0]; + } + this.dataMap[serverIp][0] += moneyGained; + this.dataMap[serverIp][1] += n; + } + + //Update the grow map when calling grow() + RunningScript.prototype.recordGrow = function(serverIp, n=1) { + if (this.dataMap[serverIp] == null || this.dataMap[serverIp].constructor !== Array) { + this.dataMap[serverIp] = [0, 0, 0, 0]; + } + this.dataMap[serverIp][2] += n; + } + + //Update the weaken map when calling weaken() { + RunningScript.prototype.recordWeaken = function(serverIp, n=1) { + if (this.dataMap[serverIp] == null || this.dataMap[serverIp].constructor !== Array) { + this.dataMap[serverIp] = [0, 0, 0, 0]; + } + this.dataMap[serverIp][3] += n; + } + + // Serialize the current object to a JSON save state + toJSON(): any { + return Generic_toJSON("RunningScript", this); + } +} + +Reviver.constructors.RunningScript = RunningScript; diff --git a/src/Script/Script.ts b/src/Script/Script.ts new file mode 100644 index 000000000..fa64ed0d8 --- /dev/null +++ b/src/Script/Script.ts @@ -0,0 +1,104 @@ +// Class representing a script file +// This does NOT represent a script that is actively running and +// being evaluated. See RunningScript for that +import { Page, + routing } from "../ui/navigationTracking"; +import { setTimeoutRef } from "../utils/SetTimeoutRef"; +import { Generic_fromJSON, + Generic_toJSON, + Reviver } from "../../utils/JSONReviver"; +import { roundToTwo } from "../../utils/helpers/roundToTwo"; + +export class Script { + // Initializes a Script Object from a JSON save state + static fromJSON(value: any): Script { + return Generic_fromJSON(Script, value.data); + } + + // Code for this script + code: string = ""; + + // Filename for the script file + filename: string = ""; + + // The dynamic module generated for this script when it is run. + // This is only applicable for NetscriptJS + module: any = ""; + + // Amount of RAM this Script requres to run + ramUsage: number = 0; + + // IP of server that this script is on. + server: string = ""; + + + constructor(fn: string = "", code: string = "", server: string = "") { + this.filename = fn; + this.code = code; + this.ramUsage = 0; + this.server = server; // IP of server this script is on + this.module = ""; + if (this.code !== "") {this.updateRamUsage();} + }; + + download(): void { + const filename = this.filename + ".js"; + const file = new Blob([this.code], {type: 'text/plain'}); + if (window.navigator.msSaveOrOpenBlob) {// IE10+ + window.navigator.msSaveOrOpenBlob(file, filename); + } else { // Others + var a = document.createElement("a"), + url = URL.createObjectURL(file); + a.href = url; + a.download = filename; + document.body.appendChild(a); + a.click(); + setTimeoutRef(function() { + document.body.removeChild(a); + window.URL.revokeObjectURL(url); + }, 0); + } + } + + // Save a script FROM THE SCRIPT EDITOR + saveScript(): void { + if (routing.isOn(Page.ScriptEditor)) { + //Update code and filename + const code = getCurrentEditor().getCode(); + this.code = code.replace(/^\s+|\s+$/g, ''); + + const filenameElem: HTMLInputElement | null = document.getElementById("script-editor-filename") as HTMLInputElement; + if (filenameElem == null) { + console.error(`Failed to get Script filename DOM element`); + return; + } + this.filename = filenameElem!.value; + + // Server + this.server = Player.currentServer; + + //Calculate/update ram usage, execution time, etc. + this.updateRamUsage(); + + this.module = ""; + } + } + + // Updates the script's RAM usage based on its code + async updateRamUsage(): void { + // TODO Commented this out because I think its unnecessary + // DOuble check/Test + // var codeCopy = this.code.repeat(1); + var res = await calculateRamUsage(this.code); + if (res !== -1) { + this.ramUsage = roundToTwo(res); + } + } + + // Serialize the current object to a JSON save state + toJSON(): any { + return Generic_toJSON("Script", this); + } +} + +Reviver.constructors.Script = Script; diff --git a/src/Script.js b/src/Script/ScriptHelpers.js old mode 100755 new mode 100644 similarity index 85% rename from src/Script.js rename to src/Script/ScriptHelpers.js index 5733ab404..45a6ebb99 --- a/src/Script.js +++ b/src/Script/ScriptHelpers.js @@ -308,44 +308,6 @@ function checkValidFilename(filename) { return false; } -function Script(fn = "", code = "", server = "") { - this.filename = fn; - this.code = code; - this.ramUsage = 0; - this.server = server; //IP of server this script is on - this.module = ""; - if (this.code !== "") {this.updateRamUsage();} -}; - -//Get the script data from the Script Editor and save it to the object -Script.prototype.saveScript = function() { - if (routing.isOn(Page.ScriptEditor)) { - //Update code and filename - const code = getCurrentEditor().getCode(); - this.code = code.replace(/^\s+|\s+$/g, ''); - - var filename = document.getElementById("script-editor-filename").value; - this.filename = filename; - - //Server - this.server = Player.currentServer; - - //Calculate/update ram usage, execution time, etc. - this.updateRamUsage(); - - this.module = ""; - } -} - -//Updates how much RAM the script uses when it is running. -Script.prototype.updateRamUsage = async function() { - var codeCopy = this.code.repeat(1); - var res = await calculateRamUsage(codeCopy); - if (res !== -1) { - this.ramUsage = roundToTwo(res); - } -} - // These special strings are used to reference the presence of a given logical // construct within a user script. const specialReferenceIF = "__SPECIAL_referenceIf"; @@ -747,36 +709,6 @@ async function calculateRamUsage(codeCopy) { return ramUsage; } -Script.prototype.download = function() { - var filename = this.filename + ".js"; - var file = new Blob([this.code], {type: 'text/plain'}); - if (window.navigator.msSaveOrOpenBlob) {// IE10+ - window.navigator.msSaveOrOpenBlob(file, filename); - } else { // Others - var a = document.createElement("a"), - url = URL.createObjectURL(file); - a.href = url; - a.download = filename; - document.body.appendChild(a); - a.click(); - setTimeoutRef(function() { - document.body.removeChild(a); - window.URL.revokeObjectURL(url); - }, 0); - } -} - -Script.prototype.toJSON = function() { - return Generic_toJSON("Script", this); -} - - -Script.fromJSON = function(value) { - return Generic_fromJSON(Script, value.data); -} - -Reviver.constructors.Script = Script; - //Called when the game is loaded. Loads all running scripts (from all servers) //into worker scripts so that they will start running function loadAllRunningScripts() { @@ -916,122 +848,5 @@ function findRunningScript(filename, args, server) { return null; } -function RunningScript(script, args) { - if (script == null || script == undefined) { return; } - this.filename = script.filename; - this.args = args; - this.server = script.server; //IP Address only - this.ramUsage = script.ramUsage; - - this.logs = []; //Script logging. Array of strings, with each element being a log entry - this.logUpd = false; - - //Stats to display on the Scripts menu, and used to determine offline progress - this.offlineRunningTime = 0.01; //Seconds - this.offlineMoneyMade = 0; - this.offlineExpGained = 0; - this.onlineRunningTime = 0.01; //Seconds - this.onlineMoneyMade = 0; - this.onlineExpGained = 0; - - this.threads = 1; - - // Holds a map of all servers, where server = key and the value for each - // server is an array of four numbers. The four numbers represent: - // [MoneyStolen, NumTimesHacked, NumTimesGrown, NumTimesWeaken] - // This data is used for offline progress - this.dataMap = {}; -} - -RunningScript.prototype.getCode = function() { - const server = AllServers[this.server]; - if (server == null) { return ""; } - for (let i = 0; i < server.scripts.length; ++i) { - if (server.scripts[i].filename === this.filename) { - return server.scripts[i].code; - } - } - - return ""; -} - -RunningScript.prototype.getRamUsage = function() { - if (this.ramUsage != null && this.ramUsage > 0) { return this.ramUsage; } // Use cached value - - const server = AllServers[this.server]; - if (server == null) { return 0; } - for (let i = 0; i < server.scripts.length; ++i) { - if (server.scripts[i].filename === this.filename) { - // Cache the ram usage for the next call - this.ramUsage = server.scripts[i].ramUsage; - return this.ramUsage; - } - } - - - return 0; -} - -RunningScript.prototype.log = function(txt) { - if (this.logs.length > Settings.MaxLogCapacity) { - //Delete first element and add new log entry to the end. - //TODO Eventually it might be better to replace this with circular array - //to improve performance - this.logs.shift(); - } - let logEntry = txt; - if (FconfSettings.ENABLE_TIMESTAMPS) { - logEntry = "[" + getTimestamp() + "] " + logEntry; - } - this.logs.push(logEntry); - this.logUpd = true; -} - -RunningScript.prototype.displayLog = function() { - for (var i = 0; i < this.logs.length; ++i) { - post(this.logs[i]); - } -} - -RunningScript.prototype.clearLog = function() { - this.logs.length = 0; -} - -//Update the moneyStolen and numTimesHack maps when hacking -RunningScript.prototype.recordHack = function(serverIp, moneyGained, n=1) { - if (this.dataMap[serverIp] == null || this.dataMap[serverIp].constructor !== Array) { - this.dataMap[serverIp] = [0, 0, 0, 0]; - } - this.dataMap[serverIp][0] += moneyGained; - this.dataMap[serverIp][1] += n; -} - -//Update the grow map when calling grow() -RunningScript.prototype.recordGrow = function(serverIp, n=1) { - if (this.dataMap[serverIp] == null || this.dataMap[serverIp].constructor !== Array) { - this.dataMap[serverIp] = [0, 0, 0, 0]; - } - this.dataMap[serverIp][2] += n; -} - -//Update the weaken map when calling weaken() { -RunningScript.prototype.recordWeaken = function(serverIp, n=1) { - if (this.dataMap[serverIp] == null || this.dataMap[serverIp].constructor !== Array) { - this.dataMap[serverIp] = [0, 0, 0, 0]; - } - this.dataMap[serverIp][3] += n; -} - -RunningScript.prototype.toJSON = function() { - return Generic_toJSON("RunningScript", this); -} - - -RunningScript.fromJSON = function(value) { - return Generic_fromJSON(RunningScript, value.data); -} - -Reviver.constructors.RunningScript = RunningScript; - export {loadAllRunningScripts, findRunningScript, - RunningScript, Script, scriptEditorInit, isScriptFilename}; + scriptEditorInit, isScriptFilename}; diff --git a/src/Server.js b/src/Server.js deleted file mode 100644 index 6aa27b6f4..000000000 --- a/src/Server.js +++ /dev/null @@ -1,468 +0,0 @@ -import { BitNodeMultipliers } from "./BitNode/BitNodeMultipliers"; -import { CodingContract, - ContractTypes } from "./CodingContracts"; -import { CONSTANTS } from "./Constants"; -import { Script, - isScriptFilename } from "./Script"; -import { Player } from "./Player"; -import { Programs } from "./Programs/Programs"; -import { SpecialServerIps } from "./SpecialServerIps"; -import { TextFile } from "./TextFile"; -import { getRandomInt } from "../utils/helpers/getRandomInt"; -import { createRandomIp, - ipExists } from "../utils/IPAddress"; -import { serverMetadata } from "./data/servers"; -import { Reviver, - Generic_toJSON, - Generic_fromJSON} from "../utils/JSONReviver"; -import {isValidIPAddress} from "../utils/helpers/isValidIPAddress"; - -function Server(params={ip:createRandomIp(), hostname:""}) { - /* Properties */ - //Connection information - this.ip = params.ip ? params.ip : createRandomIp(); - - var hostname = params.hostname; - var i = 0; - var suffix = ""; - while (GetServerByHostname(hostname+suffix) != null) { - //Server already exists - suffix = "-" + i; - ++i; - } - this.hostname = hostname + suffix; - this.organizationName = params.organizationName != null ? params.organizationName : ""; - this.isConnectedTo = params.isConnectedTo != null ? params.isConnectedTo : false; - - //Access information - this.hasAdminRights = params.adminRights != null ? params.adminRights : false; - this.purchasedByPlayer = params.purchasedByPlayer != null ? params.purchasedByPlayer : false; - this.manuallyHacked = false; //Flag that tracks whether or not the server has been hacked at least once - - //RAM, CPU speed and Scripts - this.maxRam = params.maxRam != null ? params.maxRam : 0; //GB - this.ramUsed = 0; - this.cpuCores = 1; //Max of 8, affects hacking times and Hacking Mission starting Cores - - this.scripts = []; - this.runningScripts = []; //Stores RunningScript objects - this.programs = []; - this.messages = []; - this.textFiles = []; - this.contracts = []; - this.dir = 0; //new Directory(this, null, ""); TODO - - /* Hacking information (only valid for "foreign" aka non-purchased servers) */ - this.requiredHackingSkill = params.requiredHackingSkill != null ? params.requiredHackingSkill : 1; - this.moneyAvailable = params.moneyAvailable != null ? params.moneyAvailable * BitNodeMultipliers.ServerStartingMoney : 0; - this.moneyMax = 25 * this.moneyAvailable * BitNodeMultipliers.ServerMaxMoney; - - //Hack Difficulty is synonymous with server security. Base Difficulty = Starting difficulty - this.hackDifficulty = params.hackDifficulty != null ? params.hackDifficulty * BitNodeMultipliers.ServerStartingSecurity : 1; - this.baseDifficulty = this.hackDifficulty; - this.minDifficulty = Math.max(1, Math.round(this.hackDifficulty / 3)); - this.serverGrowth = params.serverGrowth != null ? params.serverGrowth : 1; //Integer from 0 to 100. Affects money increase from grow() - - //The IP's of all servers reachable from this one (what shows up if you run scan/netstat) - // NOTE: Only contains IP and not the Server objects themselves - this.serversOnNetwork = []; - - //Port information, required for porthacking servers to get admin rights - this.numOpenPortsRequired = params.numOpenPortsRequired != null ? params.numOpenPortsRequired : 5; - this.sshPortOpen = false; //Port 22 - this.ftpPortOpen = false; //Port 21 - this.smtpPortOpen = false; //Port 25 - this.httpPortOpen = false; //Port 80 - this.sqlPortOpen = false; //Port 1433 - this.openPortCount = 0; -}; - -Server.prototype.setMaxRam = function(ram) { - this.maxRam = ram; -} - -//The serverOnNetwork array holds the IP of all the servers. This function -//returns the actual Server objects -Server.prototype.getServerOnNetwork = function(i) { - if (i > this.serversOnNetwork.length) { - console.log("Tried to get server on network that was out of range"); - return; - } - return AllServers[this.serversOnNetwork[i]]; -} - -//Given the name of the script, returns the corresponding -//script object on the server (if it exists) -Server.prototype.getScript = function(scriptName) { - for (var i = 0; i < this.scripts.length; i++) { - if (this.scripts[i].filename == scriptName) { - return this.scripts[i]; - } - } - return null; -} - -Server.prototype.capDifficulty = function() { - if (this.hackDifficulty < this.minDifficulty) {this.hackDifficulty = this.minDifficulty;} - if (this.hackDifficulty < 1) {this.hackDifficulty = 1;} - //Place some arbitrarily limit that realistically should never happen unless someone is - //screwing around with the game - if (this.hackDifficulty > 1000000) {this.hackDifficulty = 1000000;} -} - -//Strengthens a server's security level (difficulty) by the specified amount -Server.prototype.fortify = function(amt) { - this.hackDifficulty += amt; - this.capDifficulty(); -} - -Server.prototype.weaken = function(amt) { - this.hackDifficulty -= (amt * BitNodeMultipliers.ServerWeakenRate); - this.capDifficulty(); -} - -// Write to a script file -// Overwrites existing files. Creates new files if the script does not eixst -Server.prototype.writeToScriptFile = function(fn, code) { - var ret = {success: false, overwritten: false}; - if (!isScriptFilename(fn)) { return ret; } - - //Check if the script already exists, and overwrite it if it does - for (let i = 0; i < this.scripts.length; ++i) { - if (fn === this.scripts[i].filename) { - let script = this.scripts[i]; - script.code = code; - script.updateRamUsage(); - script.module = ""; - ret.overwritten = true; - ret.success = true; - return ret; - } - } - - //Otherwise, create a new script - var newScript = new Script(); - newScript.filename = fn; - newScript.code = code; - newScript.updateRamUsage(); - newScript.server = this.ip; - this.scripts.push(newScript); - ret.success = true; - return ret; -} - -// Write to a text file -// Overwrites existing files. Creates new files if the text file does not exist -Server.prototype.writeToTextFile = function(fn, txt) { - var ret = {success: false, overwritten: false}; - if (!fn.endsWith("txt")) { return ret; } - - //Check if the text file already exists, and overwrite if it does - for (let i = 0; i < this.textFiles.length; ++i) { - if (this.textFiles[i].fn === fn) { - ret.overwritten = true; - this.textFiles[i].text = txt; - ret.success = true; - return ret; - } - } - - //Otherwise create a new text file - var newFile = new TextFile(fn, txt); - this.textFiles.push(newFile); - ret.success = true; - return ret; -} - -Server.prototype.addContract = function(contract) { - this.contracts.push(contract); -} - -Server.prototype.removeContract = function(contract) { - if (contract instanceof CodingContract) { - this.contracts = this.contracts.filter((c) => { - return c.fn !== contract.fn; - }); - } else { - this.contracts = this.contracts.filter((c) => { - return c.fn !== contract; - }); - } -} - -Server.prototype.getContract = function(contractName) { - for (const contract of this.contracts) { - if (contract.fn === contractName) { - return contract; - } - } - return null; -} - -//Functions for loading and saving a Server -Server.prototype.toJSON = function() { - return Generic_toJSON("Server", this); -} - -Server.fromJSON = function(value) { - return Generic_fromJSON(Server, value.data); -} - -Reviver.constructors.Server = Server; - -export function initForeignServers() { - /* Create a randomized network for all the foreign servers */ - //Groupings for creating a randomized network - const networkLayers = []; - for (let i = 0; i < 15; i++) { - networkLayers.push([]); - } - - // Essentially any property that is of type 'number | IMinMaxRange' - const propertiesToPatternMatch = [ - "hackDifficulty", - "moneyAvailable", - "requiredHackingSkill", - "serverGrowth" - ]; - - const toNumber = (value) => { - switch (typeof value) { - case 'number': - return value; - case 'object': - return getRandomInt(value.min, value.max); - default: - throw Error(`Do not know how to convert the type '${typeof value}' to a number`); - } - } - - for (const metadata of serverMetadata) { - const serverParams = { - hostname: metadata.hostname, - ip: createRandomIp(), - numOpenPortsRequired: metadata.numOpenPortsRequired, - organizationName: metadata.organizationName - }; - - if (metadata.maxRamExponent !== undefined) { - serverParams.maxRam = Math.pow(2, toNumber(metadata.maxRamExponent)); - } - - for (const prop of propertiesToPatternMatch) { - if (metadata[prop] !== undefined) { - serverParams[prop] = toNumber(metadata[prop]); - } - } - - const server = new Server(serverParams); - for (const filename of (metadata.literature || [])) { - server.messages.push(filename); - } - - if (metadata.specialName !== undefined) { - SpecialServerIps.addIp(metadata.specialName, server.ip); - } - - AddToAllServers(server); - if (metadata.networkLayer !== undefined) { - networkLayers[toNumber(metadata.networkLayer) - 1].push(server); - } - } - - /* Create a randomized network for all the foreign servers */ - const linkComputers = (server1, server2) => { - server1.serversOnNetwork.push(server2.ip); - server2.serversOnNetwork.push(server1.ip); - }; - - const getRandomArrayItem = (arr) => arr[Math.floor(Math.random() * arr.length)]; - - const linkNetworkLayers = (network1, selectServer) => { - for (const server of network1) { - linkComputers(server, selectServer()); - } - }; - - // Connect the first tier of servers to the player's home computer - linkNetworkLayers(networkLayers[0], () => Player.getHomeComputer()); - for (let i = 1; i < networkLayers.length; i++) { - linkNetworkLayers(networkLayers[i], () => getRandomArrayItem(networkLayers[i - 1])); - } -} - -// Returns the number of cycles needed to grow the specified server by the -// specified amount. 'growth' parameter is in decimal form, not percentage -export function numCycleForGrowth(server, growth) { - let ajdGrowthRate = 1 + (CONSTANTS.ServerBaseGrowthRate - 1) / server.hackDifficulty; - if(ajdGrowthRate > CONSTANTS.ServerMaxGrowthRate) { - ajdGrowthRate = CONSTANTS.ServerMaxGrowthRate; - } - - const serverGrowthPercentage = server.serverGrowth / 100; - - const cycles = Math.log(growth)/(Math.log(ajdGrowthRate)*Player.hacking_grow_mult*serverGrowthPercentage); - return cycles; -} - -//Applied server growth for a single server. Returns the percentage growth -export function processSingleServerGrowth(server, numCycles) { - //Server growth processed once every 450 game cycles - const numServerGrowthCycles = Math.max(Math.floor(numCycles / 450), 0); - - //Get adjusted growth rate, which accounts for server security - const growthRate = CONSTANTS.ServerBaseGrowthRate; - var adjGrowthRate = 1 + (growthRate - 1) / server.hackDifficulty; - if (adjGrowthRate > CONSTANTS.ServerMaxGrowthRate) {adjGrowthRate = CONSTANTS.ServerMaxGrowthRate;} - - //Calculate adjusted server growth rate based on parameters - const serverGrowthPercentage = server.serverGrowth / 100; - const numServerGrowthCyclesAdjusted = numServerGrowthCycles * serverGrowthPercentage * BitNodeMultipliers.ServerGrowthRate; - - //Apply serverGrowth for the calculated number of growth cycles - var serverGrowth = Math.pow(adjGrowthRate, numServerGrowthCyclesAdjusted * Player.hacking_grow_mult); - if (serverGrowth < 1) { - console.log("WARN: serverGrowth calculated to be less than 1"); - serverGrowth = 1; - } - - const oldMoneyAvailable = server.moneyAvailable; - server.moneyAvailable *= serverGrowth; - - // in case of data corruption - if (server.moneyMax && isNaN(server.moneyAvailable)) { - server.moneyAvailable = server.moneyMax; - } - - // cap at max - if (server.moneyMax && server.moneyAvailable > server.moneyMax) { - server.moneyAvailable = server.moneyMax; - } - - // if there was any growth at all, increase security - if (oldMoneyAvailable !== server.moneyAvailable) { - //Growing increases server security twice as much as hacking - let usedCycles = numCycleForGrowth(server, server.moneyAvailable / oldMoneyAvailable); - usedCycles = Math.max(0, usedCycles); - server.fortify(2 * CONSTANTS.ServerFortifyAmount * Math.ceil(usedCycles)); - } - return server.moneyAvailable / oldMoneyAvailable; -} - -export function prestigeHomeComputer(homeComp) { - const hasBitflume = homeComp.programs.includes(Programs.BitFlume.name); - - homeComp.programs.length = 0; //Remove programs - homeComp.runningScripts = []; - homeComp.serversOnNetwork = []; - homeComp.isConnectedTo = true; - homeComp.ramUsed = 0; - homeComp.programs.push(Programs.NukeProgram.name); - if (hasBitflume) { homeComp.programs.push(Programs.BitFlume.name); } - - //Update RAM usage on all scripts - homeComp.scripts.forEach(function(script) { - script.updateRamUsage(); - }); - - homeComp.messages.length = 0; //Remove .lit and .msg files - homeComp.messages.push("hackers-starting-handbook.lit"); -} - -//List of all servers that exist in the game, indexed by their ip -let AllServers = {}; - -export function prestigeAllServers() { - for (var member in AllServers) { - delete AllServers[member]; - } - AllServers = {}; -} - -export function loadAllServers(saveString) { - AllServers = JSON.parse(saveString, Reviver); -} - -function SizeOfAllServers() { - var size = 0, key; - for (key in AllServers) { - if (AllServers.hasOwnProperty(key)) size++; - } - return size; -} - -//Add a server onto the map of all servers in the game -export function AddToAllServers(server) { - var serverIp = server.ip; - if (ipExists(serverIp)) { - console.log("IP of server that's being added: " + serverIp); - console.log("Hostname of the server thats being added: " + server.hostname); - console.log("The server that already has this IP is: " + AllServers[serverIp].hostname); - throw new Error("Error: Trying to add a server with an existing IP"); - return; - } - AllServers[serverIp] = server; -} - -//Returns server object with corresponding hostname -// Relatively slow, would rather not use this a lot -export function GetServerByHostname(hostname) { - for (var ip in AllServers) { - if (AllServers.hasOwnProperty(ip)) { - if (AllServers[ip].hostname == hostname) { - return AllServers[ip]; - } - } - } - return null; -} - -//Get server by IP or hostname. Returns null if invalid -export function getServer(s) { - if (!isValidIPAddress(s)) { - return GetServerByHostname(s); - } - if(AllServers[s] !== undefined) { - return AllServers[s]; - } - return null; -} - -//Debugging tool -function PrintAllServers() { - for (var ip in AllServers) { - if (AllServers.hasOwnProperty(ip)) { - console.log("Ip: " + ip + ", hostname: " + AllServers[ip].hostname); - } - } -} - -// Directory object (folders) -function Directory(server, parent, name) { - this.s = server; //Ref to server - this.p = parent; //Ref to parent directory - this.c = []; //Subdirs - this.n = name; - this.d = parent.d + 1; //We'll only have a maximum depth of 3 or something - this.scrs = []; //Holds references to the scripts in server.scripts - this.pgms = []; - this.msgs = []; -} - -Directory.prototype.createSubdir = function(name) { - var subdir = new Directory(this.s, this, name); - -} - -Directory.prototype.getPath = function(name) { - var res = []; - var i = this; - while (i !== null) { - res.unshift(i.n, "/"); - i = i.parent; - } - res.unshift("/"); - return res.join(""); -} - -export {Server, AllServers}; diff --git a/src/Server/AllServers.ts b/src/Server/AllServers.ts new file mode 100644 index 000000000..e04c33fbb --- /dev/null +++ b/src/Server/AllServers.ts @@ -0,0 +1,111 @@ +import { ipExists } from "../../utils/IPAddress"; + +// Map of all Servers that exist in the game +// Key (string) = IP +// Value = Server object +let AllServers = {}; + +// Saftely add a Server to the AllServers map +export function AddToAllServers(server) { + var serverIp = server.ip; + if (ipExists(serverIp)) { + console.log("IP of server that's being added: " + serverIp); + console.log("Hostname of the server thats being added: " + server.hostname); + console.log("The server that already has this IP is: " + AllServers[serverIp].hostname); + throw new Error("Error: Trying to add a server with an existing IP"); + return; + } + AllServers[serverIp] = server; +} + +export function initForeignServers() { + /* Create a randomized network for all the foreign servers */ + //Groupings for creating a randomized network + const networkLayers = []; + for (let i = 0; i < 15; i++) { + networkLayers.push([]); + } + + // Essentially any property that is of type 'number | IMinMaxRange' + const propertiesToPatternMatch = [ + "hackDifficulty", + "moneyAvailable", + "requiredHackingSkill", + "serverGrowth" + ]; + + const toNumber = (value) => { + switch (typeof value) { + case 'number': + return value; + case 'object': + return getRandomInt(value.min, value.max); + default: + throw Error(`Do not know how to convert the type '${typeof value}' to a number`); + } + } + + for (const metadata of serverMetadata) { + const serverParams = { + hostname: metadata.hostname, + ip: createRandomIp(), + numOpenPortsRequired: metadata.numOpenPortsRequired, + organizationName: metadata.organizationName + }; + + if (metadata.maxRamExponent !== undefined) { + serverParams.maxRam = Math.pow(2, toNumber(metadata.maxRamExponent)); + } + + for (const prop of propertiesToPatternMatch) { + if (metadata[prop] !== undefined) { + serverParams[prop] = toNumber(metadata[prop]); + } + } + + const server = new Server(serverParams); + for (const filename of (metadata.literature || [])) { + server.messages.push(filename); + } + + if (metadata.specialName !== undefined) { + SpecialServerIps.addIp(metadata.specialName, server.ip); + } + + AddToAllServers(server); + if (metadata.networkLayer !== undefined) { + networkLayers[toNumber(metadata.networkLayer) - 1].push(server); + } + } + + /* Create a randomized network for all the foreign servers */ + const linkComputers = (server1, server2) => { + server1.serversOnNetwork.push(server2.ip); + server2.serversOnNetwork.push(server1.ip); + }; + + const getRandomArrayItem = (arr) => arr[Math.floor(Math.random() * arr.length)]; + + const linkNetworkLayers = (network1, selectServer) => { + for (const server of network1) { + linkComputers(server, selectServer()); + } + }; + + // Connect the first tier of servers to the player's home computer + linkNetworkLayers(networkLayers[0], () => Player.getHomeComputer()); + for (let i = 1; i < networkLayers.length; i++) { + linkNetworkLayers(networkLayers[i], () => getRandomArrayItem(networkLayers[i - 1])); + } +} + +export function prestigeAllServers() { + for (var member in AllServers) { + delete AllServers[member]; + } + AllServers = {}; +} + +export function loadAllServers(saveString) { + AllServers = JSON.parse(saveString, Reviver); +} diff --git a/src/Server/Server.ts b/src/Server/Server.ts new file mode 100644 index 000000000..b4d1f33dc --- /dev/null +++ b/src/Server/Server.ts @@ -0,0 +1,303 @@ +// Class representing a single generic Server +import { BitNodeMultipliers } from "../BitNode/BitNodeMultipliers"; +import { CodingContract } from "../CodingContracts"; +import { Message } from "../Message/Message"; +import { RunningScript } from "../Script/RunningScript"; +import { Script } from "../Script/Script"; +import { TextFile } from "../TextFile"; + +import { createRandomIp } from "../../utils/IPAddress"; +import { Generic_fromJSON, + Generic_toJSON, + Reviver } from "../../utils/JSONReviver"; + +interface IConstructorParams { + adminRights?: boolean; + hackDifficulty?: number; + hostname: string; + ip?: string; + isConnectedTo?: boolean; + maxRam?: number; + moneyAvailable?: number; + numOpenPortsRequired?: number; + organizationName?: string; + purchasedByPlayer?: boolean; + requiredHackingSkill?: number; + serverGrowth?: number; +} + +export class Server { + // Initial server security level + // (i.e. security level when the server was created) + baseDifficulty: number = 1; + + // Coding Contract files on this server + contracts: CodingContract[] = []; + + // How many CPU cores this server has. Maximum of 8. + // Currently, this only affects hacking missions + cpuCores: number = 1; + + // Flag indicating whether the FTP port is open + ftpPortOpen: boolean = false; + + // Server Security Level + hackDifficulty: number = 1; + + // Flag indicating whether player has admin/root access to this server + hasAdminRights: boolean = false; + + // Hostname. Must be unique + hostname: string = ""; + + // Flag indicating whether HTTP Port is open + httpPortOpen: boolean = false; + + // IP Address. Must be unique + ip: string = ""; + + // Flag indicating whether player is curently connected to this server + isConnectedTo: boolean = false; + + // Flag indicating whether this server has been manually hacked (ie. + // hacked through Terminal) by the player + manuallyHacked: boolean = false; + + // RAM (GB) available on this server + maxRam: number = 0; + + // Message files AND Literature files on this Server + // For Literature files, this array contains only the filename (string) + // For Messages, it contains the actual Message object + // TODO Separate literature files into its own property + messages: (Message | string)[] = []; + + // Minimum server security level that this server can be weakened to + minDifficulty: number = 1; + + // How much money currently resides on the server and can be hacked + moneyAvailable: number = 0; + + // Maximum amount of money that this server can hold + moneyMax: number = 0; + + // Number of open ports required in order to gain admin/root access + numOpenPortsRequired: number = 5; + + // How many ports are currently opened on the server + openPortCount: number = 0; + + // Name of company/faction/etc. that this server belongs to. + // Optional, not applicable to all Servers + organizationName: string = ""; + + // Programs on this servers. Contains only the names of the programs + programs: string[] = []; + + // Flag indicating wehther this is a purchased server + purchasedByPlayer: boolean = false; + + // RAM (GB) used. i.e. unavailable RAM + ramUsed: number = 0; + + // Hacking level required to hack this server + requiredHackingSkill: number = 1; + + // RunningScript files on this server + runningScripts: RunningScript[] = []; + + // Script files on this Server + scripts: Script[] = []; + + // Parameter that affects how effectively this server's money can + // be increased using the grow() Netscript function + serverGrowth: number = 1; + + // Contains the IP Addresses of all servers that are immediately + // reachable from this one + serversOnNetwork: string[] = []; + + // Flag indicating whether SMTP Port is open + smtpPortOpen: boolean = false; + + // Flag indicating whether SQL Port is open + sqlPortOpen: boolean = false; + + // Flag indicating whether the SSH Port is open + sshPortOpen: boolean = false; + + // Text files on this server + textFiles: TextFile[] = []; + + constructor(params: IConstructorParams={hostname: "", ip: createRandomIp() }) { + /* Properties */ + //Connection information + this.ip = params.ip ? params.ip : createRandomIp(); + + var hostname = params.hostname; + var i = 0; + var suffix = ""; + while (GetServerByHostname(hostname+suffix) != null) { + //Server already exists + suffix = "-" + i; + ++i; + } + this.hostname = hostname + suffix; + this.organizationName = params.organizationName != null ? params.organizationName : ""; + this.isConnectedTo = params.isConnectedTo != null ? params.isConnectedTo : false; + + //Access information + this.hasAdminRights = params.adminRights != null ? params.adminRights : false; + this.purchasedByPlayer = params.purchasedByPlayer != null ? params.purchasedByPlayer : false; + + //RAM, CPU speed and Scripts + this.maxRam = params.maxRam != null ? params.maxRam : 0; //GB + + /* Hacking information (only valid for "foreign" aka non-purchased servers) */ + this.requiredHackingSkill = params.requiredHackingSkill != null ? params.requiredHackingSkill : 1; + this.moneyAvailable = params.moneyAvailable != null ? params.moneyAvailable * BitNodeMultipliers.ServerStartingMoney : 0; + this.moneyMax = 25 * this.moneyAvailable * BitNodeMultipliers.ServerMaxMoney; + + //Hack Difficulty is synonymous with server security. Base Difficulty = Starting difficulty + this.hackDifficulty = params.hackDifficulty != null ? params.hackDifficulty * BitNodeMultipliers.ServerStartingSecurity : 1; + this.baseDifficulty = this.hackDifficulty; + this.minDifficulty = Math.max(1, Math.round(this.hackDifficulty / 3)); + this.serverGrowth = params.serverGrowth != null ? params.serverGrowth : 1; //Integer from 0 to 100. Affects money increase from grow() + + //Port information, required for porthacking servers to get admin rights + this.numOpenPortsRequired = params.numOpenPortsRequired != null ? params.numOpenPortsRequired : 5; + }; + + setMaxRam(ram: number): void { + this.maxRam = ram; + } + + //The serverOnNetwork array holds the IP of all the servers. This function + //returns the actual Server objects + Server.prototype.getServerOnNetwork = function(i) { + if (i > this.serversOnNetwork.length) { + console.log("Tried to get server on network that was out of range"); + return; + } + return AllServers[this.serversOnNetwork[i]]; + } + + //Given the name of the script, returns the corresponding + //script object on the server (if it exists) + Server.prototype.getScript = function(scriptName) { + for (var i = 0; i < this.scripts.length; i++) { + if (this.scripts[i].filename == scriptName) { + return this.scripts[i]; + } + } + return null; + } + + Server.prototype.capDifficulty = function() { + if (this.hackDifficulty < this.minDifficulty) {this.hackDifficulty = this.minDifficulty;} + if (this.hackDifficulty < 1) {this.hackDifficulty = 1;} + //Place some arbitrarily limit that realistically should never happen unless someone is + //screwing around with the game + if (this.hackDifficulty > 1000000) {this.hackDifficulty = 1000000;} + } + + //Strengthens a server's security level (difficulty) by the specified amount + Server.prototype.fortify = function(amt) { + this.hackDifficulty += amt; + this.capDifficulty(); + } + + Server.prototype.weaken = function(amt) { + this.hackDifficulty -= (amt * BitNodeMultipliers.ServerWeakenRate); + this.capDifficulty(); + } + + // Write to a script file + // Overwrites existing files. Creates new files if the script does not eixst + Server.prototype.writeToScriptFile = function(fn, code) { + var ret = {success: false, overwritten: false}; + if (!isScriptFilename(fn)) { return ret; } + + //Check if the script already exists, and overwrite it if it does + for (let i = 0; i < this.scripts.length; ++i) { + if (fn === this.scripts[i].filename) { + let script = this.scripts[i]; + script.code = code; + script.updateRamUsage(); + script.module = ""; + ret.overwritten = true; + ret.success = true; + return ret; + } + } + + //Otherwise, create a new script + var newScript = new Script(); + newScript.filename = fn; + newScript.code = code; + newScript.updateRamUsage(); + newScript.server = this.ip; + this.scripts.push(newScript); + ret.success = true; + return ret; + } + + // Write to a text file + // Overwrites existing files. Creates new files if the text file does not exist + Server.prototype.writeToTextFile = function(fn, txt) { + var ret = {success: false, overwritten: false}; + if (!fn.endsWith("txt")) { return ret; } + + //Check if the text file already exists, and overwrite if it does + for (let i = 0; i < this.textFiles.length; ++i) { + if (this.textFiles[i].fn === fn) { + ret.overwritten = true; + this.textFiles[i].text = txt; + ret.success = true; + return ret; + } + } + + //Otherwise create a new text file + var newFile = new TextFile(fn, txt); + this.textFiles.push(newFile); + ret.success = true; + return ret; + } + + Server.prototype.addContract = function(contract) { + this.contracts.push(contract); + } + + Server.prototype.removeContract = function(contract) { + if (contract instanceof CodingContract) { + this.contracts = this.contracts.filter((c) => { + return c.fn !== contract.fn; + }); + } else { + this.contracts = this.contracts.filter((c) => { + return c.fn !== contract; + }); + } + } + + Server.prototype.getContract = function(contractName) { + for (const contract of this.contracts) { + if (contract.fn === contractName) { + return contract; + } + } + return null; + } +} + +//Functions for loading and saving a Server +Server.prototype.toJSON = function() { + return Generic_toJSON("Server", this); +} + +Server.fromJSON = function(value) { + return Generic_fromJSON(Server, value.data); +} + +Reviver.constructors.Server = Server; diff --git a/src/Server/ServerHelpers.js b/src/Server/ServerHelpers.js new file mode 100644 index 000000000..0ab1183f5 --- /dev/null +++ b/src/Server/ServerHelpers.js @@ -0,0 +1,126 @@ +import { BitNodeMultipliers } from "./BitNode/BitNodeMultipliers"; +import { CodingContract, + ContractTypes } from "./CodingContracts"; +import { CONSTANTS } from "./Constants"; +import { Script, + isScriptFilename } from "./Script"; +import { Player } from "./Player"; +import { Programs } from "./Programs/Programs"; +import { SpecialServerIps } from "./SpecialServerIps"; +import { TextFile } from "./TextFile"; +import { getRandomInt } from "../utils/helpers/getRandomInt"; +import { serverMetadata } from "./data/servers"; +import { Reviver, + Generic_toJSON, + Generic_fromJSON} from "../utils/JSONReviver"; +import {isValidIPAddress} from "../utils/helpers/isValidIPAddress"; + +// Returns the number of cycles needed to grow the specified server by the +// specified amount. 'growth' parameter is in decimal form, not percentage +export function numCycleForGrowth(server, growth) { + let ajdGrowthRate = 1 + (CONSTANTS.ServerBaseGrowthRate - 1) / server.hackDifficulty; + if(ajdGrowthRate > CONSTANTS.ServerMaxGrowthRate) { + ajdGrowthRate = CONSTANTS.ServerMaxGrowthRate; + } + + const serverGrowthPercentage = server.serverGrowth / 100; + + const cycles = Math.log(growth)/(Math.log(ajdGrowthRate)*Player.hacking_grow_mult*serverGrowthPercentage); + return cycles; +} + +//Applied server growth for a single server. Returns the percentage growth +export function processSingleServerGrowth(server, numCycles) { + //Server growth processed once every 450 game cycles + const numServerGrowthCycles = Math.max(Math.floor(numCycles / 450), 0); + + //Get adjusted growth rate, which accounts for server security + const growthRate = CONSTANTS.ServerBaseGrowthRate; + var adjGrowthRate = 1 + (growthRate - 1) / server.hackDifficulty; + if (adjGrowthRate > CONSTANTS.ServerMaxGrowthRate) {adjGrowthRate = CONSTANTS.ServerMaxGrowthRate;} + + //Calculate adjusted server growth rate based on parameters + const serverGrowthPercentage = server.serverGrowth / 100; + const numServerGrowthCyclesAdjusted = numServerGrowthCycles * serverGrowthPercentage * BitNodeMultipliers.ServerGrowthRate; + + //Apply serverGrowth for the calculated number of growth cycles + var serverGrowth = Math.pow(adjGrowthRate, numServerGrowthCyclesAdjusted * Player.hacking_grow_mult); + if (serverGrowth < 1) { + console.log("WARN: serverGrowth calculated to be less than 1"); + serverGrowth = 1; + } + + const oldMoneyAvailable = server.moneyAvailable; + server.moneyAvailable *= serverGrowth; + + // in case of data corruption + if (server.moneyMax && isNaN(server.moneyAvailable)) { + server.moneyAvailable = server.moneyMax; + } + + // cap at max + if (server.moneyMax && server.moneyAvailable > server.moneyMax) { + server.moneyAvailable = server.moneyMax; + } + + // if there was any growth at all, increase security + if (oldMoneyAvailable !== server.moneyAvailable) { + //Growing increases server security twice as much as hacking + let usedCycles = numCycleForGrowth(server, server.moneyAvailable / oldMoneyAvailable); + usedCycles = Math.max(0, usedCycles); + server.fortify(2 * CONSTANTS.ServerFortifyAmount * Math.ceil(usedCycles)); + } + return server.moneyAvailable / oldMoneyAvailable; +} + +export function prestigeHomeComputer(homeComp) { + const hasBitflume = homeComp.programs.includes(Programs.BitFlume.name); + + homeComp.programs.length = 0; //Remove programs + homeComp.runningScripts = []; + homeComp.serversOnNetwork = []; + homeComp.isConnectedTo = true; + homeComp.ramUsed = 0; + homeComp.programs.push(Programs.NukeProgram.name); + if (hasBitflume) { homeComp.programs.push(Programs.BitFlume.name); } + + //Update RAM usage on all scripts + homeComp.scripts.forEach(function(script) { + script.updateRamUsage(); + }); + + homeComp.messages.length = 0; //Remove .lit and .msg files + homeComp.messages.push("hackers-starting-handbook.lit"); +} + +function SizeOfAllServers() { + var size = 0, key; + for (key in AllServers) { + if (AllServers.hasOwnProperty(key)) size++; + } + return size; +} + +//Returns server object with corresponding hostname +// Relatively slow, would rather not use this a lot +export function GetServerByHostname(hostname) { + for (var ip in AllServers) { + if (AllServers.hasOwnProperty(ip)) { + if (AllServers[ip].hostname == hostname) { + return AllServers[ip]; + } + } + } + return null; +} + +//Get server by IP or hostname. Returns null if invalid +export function getServer(s) { + if (!isValidIPAddress(s)) { + return GetServerByHostname(s); + } + if(AllServers[s] !== undefined) { + return AllServers[s]; + } + return null; +} diff --git a/src/ServerPurchases.js b/src/Server/ServerPurchases.js similarity index 100% rename from src/ServerPurchases.js rename to src/Server/ServerPurchases.js diff --git a/src/SpecialServerIps.js b/src/Server/SpecialServerIps.js similarity index 100% rename from src/SpecialServerIps.js rename to src/Server/SpecialServerIps.js diff --git a/utils/IPAddress.js b/utils/IPAddress.js deleted file mode 100644 index 44fa97156..000000000 --- a/utils/IPAddress.js +++ /dev/null @@ -1,33 +0,0 @@ -import {AllServers} from "../src/Server"; -import {getRandomByte} from "./helpers/getRandomByte"; - -/* Functions to deal with manipulating IP addresses*/ - -//Generate a random IP address -//Will not return an IP address that already exists in the AllServers array -function createRandomIp() { - var ip = getRandomByte(99) + '.' + - getRandomByte(9) + '.' + - getRandomByte(9) + '.' + - getRandomByte(9); - - //If the Ip already exists, recurse to create a new one - if (ipExists(ip)) { - return createRandomIp(); - } - return ip; -} - -//Returns true if the IP already exists in one of the game's servers -function ipExists(ip) { - for (var property in AllServers) { - if (AllServers.hasOwnProperty(property)) { - if (property == ip) { - return true; - } - } - } - return false; -} - -export {createRandomIp, ipExists}; diff --git a/utils/IPAddress.ts b/utils/IPAddress.ts new file mode 100644 index 000000000..691f27505 --- /dev/null +++ b/utils/IPAddress.ts @@ -0,0 +1,25 @@ +import {AllServers} from "../src/Server"; +import {getRandomByte} from "./helpers/getRandomByte"; + +/* Functions to deal with manipulating IP addresses*/ + +//Generate a random IP address +//Will not return an IP address that already exists in the AllServers array +export function createRandomIp(): string { + const ip: string = getRandomByte(99) + '.' + + getRandomByte(9) + '.' + + getRandomByte(9) + '.' + + getRandomByte(9); + + // If the Ip already exists, recurse to create a new one + if (ipExists(ip)) { + return createRandomIp(); + } + + return ip; +} + +// Returns true if the IP already exists in one of the game's servers +export function ipExists(ip: string) { + return (AllServers[ip] != null); +}