diff --git a/src/Alias.ts b/src/Alias.ts index 73029f69b..b80aed6a3 100644 --- a/src/Alias.ts +++ b/src/Alias.ts @@ -36,7 +36,7 @@ export function printAliases(): void { // Returns true if successful, false otherwise export function parseAliasDeclaration(dec: string, global = false): boolean { - const re = /^([_|\w|!|%|,|@]+)="(.+)"$/; + const re = /^([\w|!|%|,|@|-]+)="(.+)"$/; const matches = dec.match(re); if (matches == null || matches.length != 3) { return false; diff --git a/src/Script/isScriptFilename.ts b/src/Script/isScriptFilename.ts index 3506828e6..b7f13b174 100644 --- a/src/Script/isScriptFilename.ts +++ b/src/Script/isScriptFilename.ts @@ -1,3 +1,5 @@ +export const validScriptExtensions: Array = [`.js`, `.script`, `.ns`]; + export function isScriptFilename(f: string): boolean { - return f.endsWith(".js") || f.endsWith(".script") || f.endsWith(".ns"); + return validScriptExtensions.some((ext) => f.endsWith(ext)); } diff --git a/src/Server/ServerHelpers.ts b/src/Server/ServerHelpers.ts index 4f49f2338..affb42a00 100644 --- a/src/Server/ServerHelpers.ts +++ b/src/Server/ServerHelpers.ts @@ -16,22 +16,25 @@ import { isValidNumber } from "../utils/helpers/isValidNumber"; * does not have a duplicate hostname/ip. */ export function safetlyCreateUniqueServer(params: IConstructorParams): Server { + let hostname: string = params.hostname.replace(/ /g, `-`); + if (params.ip != null && ipExists(params.ip)) { params.ip = createUniqueRandomIp(); } - if (GetServer(params.hostname) != null) { + if (GetServer(hostname) != null) { + hostname = `${hostname}-0`; + // Use a for loop to ensure that we don't get suck in an infinite loop somehow - let hostname: string = params.hostname; for (let i = 0; i < 200; ++i) { - hostname = `${params.hostname}-${i}`; + hostname = hostname.replace(/-[0-9]+$/, `-${i}`); if (GetServer(hostname) == null) { break; } } - params.hostname = hostname; } + params.hostname = hostname; return new Server(params); } diff --git a/src/Server/ServerPurchases.ts b/src/Server/ServerPurchases.ts index 7d2b754e8..1c7f6d08d 100644 --- a/src/Server/ServerPurchases.ts +++ b/src/Server/ServerPurchases.ts @@ -96,7 +96,7 @@ export function purchaseServer(hostname: string, ram: number, cost: number, p: I p.loseMoney(cost, "servers"); - dialogBoxCreate("Server successfully purchased with hostname " + hostname); + dialogBoxCreate("Server successfully purchased with hostname " + newServ.hostname); } // Manually upgrade RAM on home computer (NOT through Netscript) diff --git a/src/Settings/Settings.ts b/src/Settings/Settings.ts index 8a4cbdd31..a1cc7d006 100644 --- a/src/Settings/Settings.ts +++ b/src/Settings/Settings.ts @@ -1,5 +1,6 @@ import { ISelfInitializer, ISelfLoading } from "../types"; import { OwnedAugmentationsOrderSetting, PurchaseAugmentationsOrderSetting } from "./SettingEnums"; +import { defaultTheme, ITheme } from "./Themes"; /** * Represents the default settings the player could customize. @@ -111,42 +112,7 @@ interface IDefaultSettings { /* * Theme colors */ - theme: { - [key: string]: string | undefined; - primarylight: string; - primary: string; - primarydark: string; - successlight: string; - success: string; - successdark: string; - errorlight: string; - error: string; - errordark: string; - secondarylight: string; - secondary: string; - secondarydark: string; - warninglight: string; - warning: string; - warningdark: string; - infolight: string; - info: string; - infodark: string; - welllight: string; - well: string; - white: string; - black: string; - hp: string; - money: string; - hack: string; - combat: string; - cha: string; - int: string; - rep: string; - disabled: string; - backgroundprimary: string; - backgroundsecondary: string; - button: string; - }; + theme: ITheme; } /** @@ -193,41 +159,7 @@ export const defaultSettings: IDefaultSettings = { SuppressTIXPopup: false, SuppressSavedGameToast: false, - theme: { - primarylight: "#0f0", - primary: "#0c0", - primarydark: "#090", - successlight: "#0f0", - success: "#0c0", - successdark: "#090", - errorlight: "#f00", - error: "#c00", - errordark: "#900", - secondarylight: "#AAA", - secondary: "#888", - secondarydark: "#666", - warninglight: "#ff0", - warning: "#cc0", - warningdark: "#990", - infolight: "#69f", - info: "#36c", - infodark: "#039", - welllight: "#444", - well: "#222", - white: "#fff", - black: "#000", - hp: "#dd3434", - money: "#ffd700", - hack: "#adff2f", - combat: "#faffdf", - cha: "#a671d1", - int: "#6495ed", - rep: "#faffdf", - disabled: "#66cfbc", - backgroundprimary: "#000", - backgroundsecondary: "#000", - button: "#333", - }, + theme: defaultTheme, }; /** @@ -262,41 +194,7 @@ export const Settings: ISettings & ISelfInitializer & ISelfLoading = { MonacoInsertSpaces: false, MonacoFontSize: 20, - theme: { - primarylight: defaultSettings.theme.primarylight, - primary: defaultSettings.theme.primary, - primarydark: defaultSettings.theme.primarydark, - successlight: defaultSettings.theme.successlight, - success: defaultSettings.theme.success, - successdark: defaultSettings.theme.successdark, - errorlight: defaultSettings.theme.errorlight, - error: defaultSettings.theme.error, - errordark: defaultSettings.theme.errordark, - secondarylight: defaultSettings.theme.secondarylight, - secondary: defaultSettings.theme.secondary, - secondarydark: defaultSettings.theme.secondarydark, - warninglight: defaultSettings.theme.warninglight, - warning: defaultSettings.theme.warning, - warningdark: defaultSettings.theme.warningdark, - infolight: defaultSettings.theme.infolight, - info: defaultSettings.theme.info, - infodark: defaultSettings.theme.infodark, - welllight: defaultSettings.theme.welllight, - well: defaultSettings.theme.well, - white: defaultSettings.theme.white, - black: defaultSettings.theme.black, - hp: defaultSettings.theme.hp, - money: defaultSettings.theme.money, - hack: defaultSettings.theme.hack, - combat: defaultSettings.theme.combat, - cha: defaultSettings.theme.cha, - int: defaultSettings.theme.int, - rep: defaultSettings.theme.rep, - disabled: defaultSettings.theme.disabled, - backgroundprimary: defaultSettings.theme.backgroundprimary, - backgroundsecondary: defaultSettings.theme.backgroundsecondary, - button: defaultSettings.theme.button, - }, + theme: { ...defaultTheme }, init() { Object.assign(Settings, defaultSettings); }, diff --git a/src/Settings/Themes.ts b/src/Settings/Themes.ts new file mode 100644 index 000000000..a67ac4bd7 --- /dev/null +++ b/src/Settings/Themes.ts @@ -0,0 +1,110 @@ +export interface ITheme { + [key: string]: string | undefined; + primarylight: string; + primary: string; + primarydark: string; + successlight: string; + success: string; + successdark: string; + errorlight: string; + error: string; + errordark: string; + secondarylight: string; + secondary: string; + secondarydark: string; + warninglight: string; + warning: string; + warningdark: string; + infolight: string; + info: string; + infodark: string; + welllight: string; + well: string; + white: string; + black: string; + hp: string; + money: string; + hack: string; + combat: string; + cha: string; + int: string; + rep: string; + disabled: string; + backgroundprimary: string; + backgroundsecondary: string; + button: string; +} + +export const defaultTheme: ITheme = { + primarylight: "#0f0", + primary: "#0c0", + primarydark: "#090", + successlight: "#0f0", + success: "#0c0", + successdark: "#090", + errorlight: "#f00", + error: "#c00", + errordark: "#900", + secondarylight: "#AAA", + secondary: "#888", + secondarydark: "#666", + warninglight: "#ff0", + warning: "#cc0", + warningdark: "#990", + infolight: "#69f", + info: "#36c", + infodark: "#039", + welllight: "#444", + well: "#222", + white: "#fff", + black: "#000", + hp: "#dd3434", + money: "#ffd700", + hack: "#adff2f", + combat: "#faffdf", + cha: "#a671d1", + int: "#6495ed", + rep: "#faffdf", + disabled: "#66cfbc", + backgroundprimary: "#000", + backgroundsecondary: "#000", + button: "#333", +}; + +export interface IPredefinedThemes { + 'Default': ITheme; + 'Monokai': ITheme; +} + +export const getPredefinedThemes = (): IPredefinedThemes => ({ + 'Default': defaultTheme, + 'Monokai': { + ...defaultTheme, + + backgroundprimary: '#272822', + backgroundsecondary: '#1B1C18', + primary: '#F8F8F2', + primarylight: '#FFF', + primarydark: '#FAFAEB', + success: '#A6E22E', + successlight: '#ADE146', + successdark: '#98E104', + error: '#F92672', + errorlight: '#FF69A0', + errordark: '#D10F56', + warning: '#E6DB74', + warninglight: '#E1D992', + warningdark: '#EDDD54', + info: '#66D9EF', + infolight: '#92E1F1', + infodark: '#31CDED', + + hp: '#F92672', + money: '#E6DB74', + hack: '#A6E22E', + combat: '#75715E', + cha: '#AE81FF', + int: '#66D9EF', + rep: '#E69F66', + } +}); diff --git a/src/Terminal/commands/check.ts b/src/Terminal/commands/check.ts index be734864a..0bc198a2f 100644 --- a/src/Terminal/commands/check.ts +++ b/src/Terminal/commands/check.ts @@ -3,7 +3,7 @@ import { IRouter } from "../../ui/Router"; import { IPlayer } from "../../PersonObjects/IPlayer"; import { BaseServer } from "../../Server/BaseServer"; import { findRunningScript } from "../../Script/ScriptHelpers"; -import { isScriptFilename } from "../../Script/isScriptFilename"; +import { isScriptFilename, validScriptExtensions } from "../../Script/isScriptFilename"; export function check( terminal: ITerminal, @@ -13,19 +13,21 @@ export function check( args: (string | number | boolean)[], ): void { if (args.length < 1) { - terminal.error("Incorrect number of arguments. Usage: check [script] [arg1] [arg2]..."); + terminal.error(`Incorrect number of arguments. Usage: check [script] [arg1] [arg2]...`); } else { const scriptName = terminal.getFilepath(args[0] + ""); // Can only tail script files if (!isScriptFilename(scriptName)) { - terminal.error("tail can only be called on .script files (filename must end with .script)"); + terminal.error( + `'check' can only be called on scripts files (filename must end with ${validScriptExtensions.join(", ")})`, + ); return; } - // Check that the script exists on this machine + // Check that the script is running on this machine const runningScript = findRunningScript(scriptName, args.slice(1), server); if (runningScript == null) { - terminal.error("No such script exists"); + terminal.error(`No script named ${scriptName} is running on the server`); return; } runningScript.displayLog(); diff --git a/src/Terminal/commands/kill.ts b/src/Terminal/commands/kill.ts index b2eca0431..be18d733a 100644 --- a/src/Terminal/commands/kill.ts +++ b/src/Terminal/commands/kill.ts @@ -27,7 +27,7 @@ export function kill( if (res) { terminal.print(`Killing script with PID ${pid}`); } else { - terminal.error(`Failed to kill script with PID ${pid}. No such script exists`); + terminal.error(`Failed to kill script with PID ${pid}. No such script is running`); } return; diff --git a/src/Terminal/commands/ls.tsx b/src/Terminal/commands/ls.tsx index f964a86a6..5cc498308 100644 --- a/src/Terminal/commands/ls.tsx +++ b/src/Terminal/commands/ls.tsx @@ -17,7 +17,7 @@ export function ls( terminal.error("Incorrect usage of ls command. Usage: ls [dir] [| grep pattern]"); } - if (numArgs > 5 || numArgs === 3) { + if (numArgs > 4 || numArgs === 2) { return incorrectUsage(); } @@ -30,12 +30,12 @@ export function ls( prefix += "/"; } - // If there are 4+ arguments, then the last 3 must be for grep - if (numArgs >= 4) { - if (args[numArgs - 1] !== "grep" || args[numArgs - 2] !== "|") { + // If there are 3+ arguments, then the last 3 must be for grep + if (numArgs >= 3) { + if (args[numArgs - 2] !== "grep" || args[numArgs - 3] !== "|") { return incorrectUsage(); } - filter = args[numArgs] + ""; + filter = args[numArgs - 1] + ""; } // If the second argument is not a pipe, then it must be for listing a directory diff --git a/src/Terminal/commands/mem.ts b/src/Terminal/commands/mem.ts index 799af2181..42abc25ea 100644 --- a/src/Terminal/commands/mem.ts +++ b/src/Terminal/commands/mem.ts @@ -29,7 +29,7 @@ export function mem( const script = terminal.getScript(player, scriptName); if (script == null) { - terminal.error("No such script exists!"); + terminal.error("mem failed. No such script exists!"); return; } diff --git a/src/Terminal/commands/runProgram.ts b/src/Terminal/commands/runProgram.ts index f9e17b2d8..5f48029e6 100644 --- a/src/Terminal/commands/runProgram.ts +++ b/src/Terminal/commands/runProgram.ts @@ -33,7 +33,7 @@ export function runProgram( } for (const program of Object.values(Programs)) { - if (program.name === programName) { + if (program.name.toLocaleLowerCase() === programName.toLocaleLowerCase()) { program.run( router, terminal, diff --git a/src/Terminal/commands/scp.ts b/src/Terminal/commands/scp.ts index f1561d6d4..d5def4d68 100644 --- a/src/Terminal/commands/scp.ts +++ b/src/Terminal/commands/scp.ts @@ -90,7 +90,7 @@ export function scp( } } if (sourceScript == null) { - terminal.error("scp() failed. No such script exists"); + terminal.error("scp failed. No such script exists"); return; } diff --git a/src/Terminal/commands/tail.ts b/src/Terminal/commands/tail.ts index 1773d472f..d1101e76a 100644 --- a/src/Terminal/commands/tail.ts +++ b/src/Terminal/commands/tail.ts @@ -3,7 +3,7 @@ import { IRouter } from "../../ui/Router"; import { IPlayer } from "../../PersonObjects/IPlayer"; import { BaseServer } from "../../Server/BaseServer"; import { findRunningScriptByPid } from "../../Script/ScriptHelpers"; -import { isScriptFilename } from "../../Script/isScriptFilename"; +import { isScriptFilename, validScriptExtensions } from "../../Script/isScriptFilename"; import { compareArrays } from "../../utils/helpers/compareArrays"; import { LogBoxEvents } from "../../ui/React/LogBoxManager"; @@ -20,7 +20,7 @@ export function tail( } else if (typeof commandArray[0] === "string") { const scriptName = terminal.getFilepath(commandArray[0]); if (!isScriptFilename(scriptName)) { - terminal.error("tail can only be called on .script, .ns, .js files, or by pid"); + terminal.error(`tail can only be called on ${validScriptExtensions.join(", ")} files, or by PID`); return; } @@ -66,11 +66,11 @@ export function tail( } // if there's no candidate then we just don't know. - terminal.error("No such script exists."); + terminal.error(`No script named ${scriptName} is running on the server`); } else if (typeof commandArray[0] === "number") { const runningScript = findRunningScriptByPid(commandArray[0], server); if (runningScript == null) { - terminal.error("No such script exists"); + terminal.error(`No script with PID ${commandArray[0]} is running on the server`); return; } LogBoxEvents.emit(runningScript); diff --git a/src/ui/React/ThemeEditorModal.tsx b/src/ui/React/ThemeEditorModal.tsx index b8b1f204d..6582ea377 100644 --- a/src/ui/React/ThemeEditorModal.tsx +++ b/src/ui/React/ThemeEditorModal.tsx @@ -7,9 +7,11 @@ import Paper from "@mui/material/Paper"; import TextField from "@mui/material/TextField"; import IconButton from "@mui/material/IconButton"; import ReplyIcon from "@mui/icons-material/Reply"; +import PaletteSharpIcon from "@mui/icons-material/PaletteSharp"; import { Color, ColorPicker } from "material-ui-color"; import { ThemeEvents } from "./Theme"; import { Settings, defaultSettings } from "../../Settings/Settings"; +import { ITheme, getPredefinedThemes } from "../../Settings/Themes"; interface IProps { open: boolean; @@ -64,11 +66,18 @@ export function ThemeEditorModal(props: IProps): React.ReactElement { ...Settings.theme, }); - function resetTheme(): void { - setCustomTheme({ - ...defaultSettings.theme, - }); - Object.assign(Settings.theme, defaultSettings.theme); + const predefinedThemes = getPredefinedThemes(); + const themes = predefinedThemes && Object.entries(predefinedThemes) + .map(([name, templateTheme]) => ( + + )) || <>; + + function setTheme(theme: ITheme): void { + setCustomTheme(theme); + Object.assign(Settings.theme, theme); ThemeEvents.emit(); } @@ -96,250 +105,262 @@ export function ThemeEditorModal(props: IProps): React.ReactElement { ThemeEvents.emit(); } + function setTemplateTheme(theme: ITheme): void { + setTheme(theme); + } + return ( - - Example tooltip}> - - - - - - - - text with primary color - text with secondary color - text with error color - + + Example tooltip}> + + + + + + + + +
+ text with primary color  + text with secondary color  + text with error color + +
+
-
- - - -
+ + + + - - - +
+ + + -
- - - +
+ + + -
- - - +
+ + + -
- - - +
+ + + -
- - - +
+ + + -
- - - - - - - +
+ + + + + + + -
- - - - - - - - -
-
- - - - ), - }} - /> +
+ + + + + + + + +
+ + + + <> + Copy the string above if you want to backup or share your theme with others. + Use the buttons below to replace the current theme with a pre-built template + {themes} + +
); }