/** * Abstract Base Class for any Server object */ import { CodingContract } from "../CodingContracts"; import { Message } from "../Message/Message"; import { RunningScript } from "../Script/RunningScript"; import { Script } from "../Script/Script"; import { isValidFilePath } from "../Terminal/DirectoryHelpers"; import { TextFile } from "../TextFile"; import { IReturnStatus } from "../types"; import { isScriptFilename } from "../Script/ScriptHelpersTS"; import { createRandomIp } from "../../utils/IPAddress"; import { compareArrays } from "../../utils/helpers/compareArrays"; interface IConstructorParams { adminRights?: boolean; hostname: string; ip?: string; isConnectedTo?: boolean; maxRam?: number; organizationName?: string; } export class BaseServer { // 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 = 1; // Flag indicating whether the FTP port is open ftpPortOpen = false; // Flag indicating whether player has admin/root access to this server hasAdminRights = false; // Hostname. Must be unique hostname = ""; // Flag indicating whether HTTP Port is open httpPortOpen = false; // IP Address. Must be unique ip = ""; // Flag indicating whether player is curently connected to this server isConnectedTo = false; // RAM (GB) available on this server maxRam = 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)[] = []; // Name of company/faction/etc. that this server belongs to. // Optional, not applicable to all Servers organizationName = ""; // Programs on this servers. Contains only the names of the programs programs: string[] = []; // RAM (GB) used. i.e. unavailable RAM ramUsed = 0; // RunningScript files on this server runningScripts: RunningScript[] = []; // Script files on this Server scripts: Script[] = []; // Contains the IP Addresses of all servers that are immediately // reachable from this one serversOnNetwork: string[] = []; // Flag indicating whether SMTP Port is open smtpPortOpen = false; // Flag indicating whether SQL Port is open sqlPortOpen = false; // Flag indicating whether the SSH Port is open sshPortOpen = false; // Text files on this server textFiles: TextFile[] = []; constructor(params: IConstructorParams={ hostname: "", ip: createRandomIp() }) { this.ip = params.ip ? params.ip : createRandomIp(); this.hostname = params.hostname; 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; } addContract(contract: CodingContract) { this.contracts.push(contract); } getContract(contractName: string): CodingContract | null { for (const contract of this.contracts) { if (contract.fn === contractName) { return contract; } } return null; } /** * Find an actively running script on this server * @param scriptName - Filename of script to search for * @param scriptArgs - Arguments that script is being run with * @returns RunningScript for the specified active script * Returns null if no such script can be found */ getRunningScript(scriptName: string, scriptArgs: any[]): RunningScript | null { for (const rs of this.runningScripts) { if (rs.filename === scriptName && compareArrays(rs.args, scriptArgs)) { return rs; } } return null; } /** * Given the name of the script, returns the corresponding * Script object on the server (if it exists) */ getScript(scriptName: string): Script | null { for (let i = 0; i < this.scripts.length; i++) { if (this.scripts[i].filename === scriptName) { return this.scripts[i]; } } return null; } /** * Returns boolean indicating whether the given script is running on this server */ isRunning(fn: string): boolean { for (const runningScriptObj of this.runningScripts) { if (runningScriptObj.filename === fn) { return true; } } return false; } removeContract(contract: CodingContract) { 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; }); } } /** * Remove a file from the server * @param fn {string} Name of file to be deleted * @returns {IReturnStatus} Return status object indicating whether or not file was deleted */ removeFile(fn: string): IReturnStatus { if (fn.endsWith(".exe") || fn.match(/^.+\.exe-\d+(?:\.\d*)?%-INC$/) != null) { for (let i = 0; i < this.programs.length; ++i) { if (this.programs[i] === fn) { this.programs.splice(i, 1); return { res: true }; } } } else if (isScriptFilename(fn)) { for (let i = 0; i < this.scripts.length; ++i) { if (this.scripts[i].filename === fn) { if (this.isRunning(fn)) { return { res: false, msg: "Cannot delete a script that is currently running!", }; } this.scripts.splice(i, 1); return { res: true }; } } } else if (fn.endsWith(".lit")) { for (let i = 0; i < this.messages.length; ++i) { const f = this.messages[i]; if (typeof f === "string" && f === fn) { this.messages.splice(i, 1); return { res: true }; } } } else if (fn.endsWith(".txt")) { for (let i = 0; i < this.textFiles.length; ++i) { if (this.textFiles[i].fn === fn) { this.textFiles.splice(i, 1); return { res: true }; } } } else if (fn.endsWith(".cct")) { for (let i = 0; i < this.contracts.length; ++i) { if (this.contracts[i].fn === fn) { this.contracts.splice(i, 1); return { res: true }; } } } return { res: false, msg: "No such file exists" }; } /** * Called when a script is run on this server. * All this function does is add a RunningScript object to the * `runningScripts` array. It does NOT check whether the script actually can * be run. */ runScript(script: RunningScript): void { this.runningScripts.push(script); } setMaxRam(ram: number): void { this.maxRam = ram; } /** * Write to a script file * Overwrites existing files. Creates new files if the script does not eixst */ writeToScriptFile(fn: string, code: string) { const ret = { success: false, overwritten: false }; if (!isValidFilePath(fn) || !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) { const script = this.scripts[i]; script.code = code; script.updateRamUsage(this.scripts); script.markUpdated(); ret.overwritten = true; ret.success = true; return ret; } } // Otherwise, create a new script const newScript = new Script(fn, code, this.ip, this.scripts); 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 writeToTextFile(fn: string, txt: string) { const ret = { success: false, overwritten: false }; if (!isValidFilePath(fn) || !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 const newFile = new TextFile(fn, txt); this.textFiles.push(newFile); ret.success = true; return ret; } }