From ae38b11edea2ae944ba0bb07e4004977066478f4 Mon Sep 17 00:00:00 2001 From: Olivier Gagnon Date: Tue, 24 May 2022 18:34:00 -0400 Subject: [PATCH] made static ram cost typecheck that it's missing no property. --- src/Netscript/APIWrapper.ts | 2 +- src/Netscript/RamCostGenerator.ts | 131 ++++++++++++++++++++++++++---- src/NetscriptFunctions.ts | 4 +- src/NetscriptFunctions/Extra.ts | 73 +++++++++-------- 4 files changed, 154 insertions(+), 56 deletions(-) diff --git a/src/Netscript/APIWrapper.ts b/src/Netscript/APIWrapper.ts index 58c892d4f..65c1df905 100644 --- a/src/Netscript/APIWrapper.ts +++ b/src/Netscript/APIWrapper.ts @@ -18,7 +18,7 @@ type InternalFunction unknown> = (ctx: Netscri export type InternalAPI = { [Property in keyof API]: API[Property] extends ExternalFunction ? InternalFunction - : API[Property] extends ExternalAPI + : API[Property] extends object ? InternalAPI : never; }; diff --git a/src/Netscript/RamCostGenerator.ts b/src/Netscript/RamCostGenerator.ts index 25549ae6f..f14e1e80d 100644 --- a/src/Netscript/RamCostGenerator.ts +++ b/src/Netscript/RamCostGenerator.ts @@ -1,6 +1,18 @@ import { IPlayer } from "src/PersonObjects/IPlayer"; import { IMap } from "../types"; +import { NS as INS } from "../ScriptEditor/NetscriptDefinitions"; + +import { INetscriptExtra } from "../NetscriptFunctions/Extra"; + +type RamCostTree = { + [Property in keyof API]: API[Property] extends () => void + ? number | ((p: IPlayer) => void) + : API[Property] extends object + ? RamCostTree + : never; +}; + // TODO remember to update RamCalculations.js and WorkerScript.js // RAM costs for Netscript functions @@ -89,7 +101,7 @@ function SF4Cost(cost: number): (player: IPlayer) => number { } // Hacknet API -const hacknet: IMap = { +const hacknet = { numNodes: 0, purchaseNode: 0, getPurchaseNodeCost: 0, @@ -106,10 +118,15 @@ const hacknet: IMap = { hashCost: 0, spendHashes: 0, maxNumNodes: 0, + hashCapacity: 0, + getHashUpgrades: 0, + getHashUpgradeLevel: 0, + getStudyMult: 0, + getTrainingMult: 0, }; // Stock API -const stock: IMap = { +const stock = { getSymbols: RamCostConstants.ScriptGetStockRamCost, getPrice: RamCostConstants.ScriptGetStockRamCost, getAskPrice: RamCostConstants.ScriptGetStockRamCost, @@ -134,7 +151,7 @@ const stock: IMap = { }; // Singularity API -const singularity: IMap = { +const singularity = { universityCourse: SF4Cost(RamCostConstants.ScriptSingularityFn1RamCost), gymWorkout: SF4Cost(RamCostConstants.ScriptSingularityFn1RamCost), travelToCity: SF4Cost(RamCostConstants.ScriptSingularityFn1RamCost), @@ -190,7 +207,7 @@ const singularity: IMap = { }; // Gang API -const gang: IMap = { +const gang = { createGang: RamCostConstants.ScriptGangApiBaseRamCost / 4, inGang: RamCostConstants.ScriptGangApiBaseRamCost / 4, getMemberNames: RamCostConstants.ScriptGangApiBaseRamCost / 4, @@ -215,7 +232,7 @@ const gang: IMap = { }; // Bladeburner API -const bladeburner: IMap = { +const bladeburner = { getContractNames: RamCostConstants.ScriptBladeburnerApiBaseRamCost / 10, getOperationNames: RamCostConstants.ScriptBladeburnerApiBaseRamCost / 10, getBlackOpNames: RamCostConstants.ScriptBladeburnerApiBaseRamCost / 10, @@ -253,15 +270,13 @@ const bladeburner: IMap = { getBonusTime: 0, }; -const infiltration: IMap = { - calculateDifficulty: RamCostConstants.ScriptInfiltrationCalculateDifficulty, - calculateRewards: RamCostConstants.ScriptInfiltrationCalculateRewards, - calculateGetLocations: RamCostConstants.ScriptInfiltrationGetLocations, - calculateGetInfiltrations: RamCostConstants.ScriptInfiltrationGetInfiltrations, +const infiltration = { + getPossibleLocations: RamCostConstants.ScriptInfiltrationGetLocations, + getInfiltration: RamCostConstants.ScriptInfiltrationGetInfiltrations, }; // Coding Contract API -const codingcontract: IMap = { +const codingcontract = { attempt: RamCostConstants.ScriptCodingContractBaseRamCost, getContractType: RamCostConstants.ScriptCodingContractBaseRamCost / 2, getData: RamCostConstants.ScriptCodingContractBaseRamCost / 2, @@ -270,7 +285,7 @@ const codingcontract: IMap = { }; // Duplicate Sleeve API -const sleeve: IMap = { +const sleeve = { getNumSleeves: RamCostConstants.ScriptSleeveBaseRamCost, setToShockRecovery: RamCostConstants.ScriptSleeveBaseRamCost, setToSynchronize: RamCostConstants.ScriptSleeveBaseRamCost, @@ -290,7 +305,7 @@ const sleeve: IMap = { }; // Stanek API -const stanek: IMap = { +const stanek = { giftWidth: RamCostConstants.ScriptStanekWidth, giftHeight: RamCostConstants.ScriptStanekHeight, chargeFragment: RamCostConstants.ScriptStanekCharge, @@ -305,7 +320,7 @@ const stanek: IMap = { }; // UI API -const ui: IMap = { +const ui = { getTheme: 0, setTheme: 0, resetTheme: 0, @@ -313,17 +328,84 @@ const ui: IMap = { setStyles: 0, resetStyles: 0, getGameInfo: 0, + clearTerminal: 0, }; // Grafting API -const grafting: IMap = { +const grafting = { getAugmentationGraftPrice: 3.75, getAugmentationGraftTime: 3.75, getGraftableAugmentations: 5, graftAugmentation: 7.5, }; -export const RamCosts: IMap = { +const corporation = { + createCorporation: 0, + hasUnlockUpgrade: 0, + getUnlockUpgradeCost: 0, + getUpgradeLevel: 0, + getUpgradeLevelCost: 0, + getExpandIndustryCost: 0, + getExpandCityCost: 0, + getInvestmentOffer: 0, + acceptInvestmentOffer: 0, + goPublic: 0, + bribe: 0, + getCorporation: 0, + getDivision: 0, + expandIndustry: 0, + expandCity: 0, + unlockUpgrade: 0, + levelUpgrade: 0, + issueDividends: 0, + buyBackShares: 0, + sellShares: 0, + getBonusTime: 0, + sellMaterial: 0, + sellProduct: 0, + discontinueProduct: 0, + setSmartSupply: 0, + setSmartSupplyUseLeftovers: 0, + buyMaterial: 0, + bulkPurchase: 0, + getWarehouse: 0, + getProduct: 0, + getMaterial: 0, + setMaterialMarketTA1: 0, + setMaterialMarketTA2: 0, + setProductMarketTA1: 0, + setProductMarketTA2: 0, + exportMaterial: 0, + cancelExportMaterial: 0, + purchaseWarehouse: 0, + upgradeWarehouse: 0, + makeProduct: 0, + limitMaterialProduction: 0, + limitProductProduction: 0, + getPurchaseWarehouseCost: 0, + getUpgradeWarehouseCost: 0, + hasWarehouse: 0, + assignJob: 0, + hireEmployee: 0, + upgradeOfficeSize: 0, + throwParty: 0, + buyCoffee: 0, + hireAdVert: 0, + research: 0, + getOffice: 0, + getEmployee: 0, + getHireAdVertCost: 0, + getHireAdVertCount: 0, + getResearchCost: 0, + hasResearched: 0, + setAutoJobAssignment: 0, + getOfficeSizeUpgradeCost: 0, +}; + +const SourceRamCosts = { + args: undefined as unknown as never[], // special use case + enums: undefined as unknown as never, + corporation, hacknet, stock, singularity, @@ -363,7 +445,6 @@ export const RamCosts: IMap = { enableLog: 0, isLogEnabled: 0, getScriptLogs: 0, - clearTerminal: RamCostConstants.ScriptClearTerminalCost, nuke: RamCostConstants.ScriptPortProgramRamCost, brutessh: RamCostConstants.ScriptPortProgramRamCost, ftpcrack: RamCostConstants.ScriptPortProgramRamCost, @@ -382,7 +463,6 @@ export const RamCosts: IMap = { ps: RamCostConstants.ScriptScanRamCost, getRecentScripts: RamCostConstants.ScriptRecentScriptsRamCost, hasRootAccess: RamCostConstants.ScriptHasRootAccessRamCost, - getIp: RamCostConstants.ScriptGetHostnameRamCost, getHostname: RamCostConstants.ScriptGetHostnameRamCost, getHackingLevel: RamCostConstants.ScriptGetHackingLevelRamCost, getHackingMultipliers: RamCostConstants.ScriptGetMultipliersRamCost, @@ -439,6 +519,15 @@ export const RamCosts: IMap = { getOwnedSourceFiles: RamCostConstants.ScriptGetOwnedSourceFiles, tail: 0, toast: 0, + closeTail: 0, + clearPort: 0, + openDevMenu: 0, + alert: 0, + flags: 0, + exploit: 0, + bypass: 0, + alterReality: 0, + rainbow: 0, heart: { // Easter egg function break: 0, @@ -492,6 +581,12 @@ export const RamCosts: IMap = { }, }; +export const RamCosts: IMap = SourceRamCosts; + +// This line in particular is there so typescript typechecks that we are not missing any static ram cost. +// eslint-disable-next-line @typescript-eslint/no-unused-vars +const _typecheck: RamCostTree = SourceRamCosts; + export function getRamCost(player: IPlayer, ...args: string[]): number { if (args.length === 0) { console.warn(`No arguments passed to getRamCost()`); diff --git a/src/NetscriptFunctions.ts b/src/NetscriptFunctions.ts index 6dddccbe9..db34000e9 100644 --- a/src/NetscriptFunctions.ts +++ b/src/NetscriptFunctions.ts @@ -62,7 +62,7 @@ import { isString } from "./utils/helpers/isString"; import { BaseServer } from "./Server/BaseServer"; import { NetscriptGang } from "./NetscriptFunctions/Gang"; import { NetscriptSleeve } from "./NetscriptFunctions/Sleeve"; -import { NetscriptExtra } from "./NetscriptFunctions/Extra"; +import { NetscriptExtra, INetscriptExtra } from "./NetscriptFunctions/Extra"; import { NetscriptHacknet } from "./NetscriptFunctions/Hacknet"; import { NetscriptStanek } from "./NetscriptFunctions/Stanek"; import { NetscriptInfiltration } from "./NetscriptFunctions/Infiltration"; @@ -532,7 +532,7 @@ export function NetscriptFunctions(workerScript: WorkerScript): NS { }, }; - const extra = NetscriptExtra(Player, workerScript, helper); + const extra = wrapAPI(helper, {}, workerScript, NetscriptExtra(Player)) as unknown as INetscriptExtra; const formulas = wrapAPI(helper, {}, workerScript, NetscriptFormulas(Player, helper), "formulas") .formulas as unknown as IFormulas; diff --git a/src/NetscriptFunctions/Extra.ts b/src/NetscriptFunctions/Extra.ts index 4fc5a888a..916ad96da 100644 --- a/src/NetscriptFunctions/Extra.ts +++ b/src/NetscriptFunctions/Extra.ts @@ -1,9 +1,8 @@ -import { WorkerScript } from "../Netscript/WorkerScript"; import { IPlayer } from "../PersonObjects/IPlayer"; import { Exploit } from "../Exploits/Exploit"; import * as bcrypt from "bcryptjs"; -import { INetscriptHelper } from "./INetscriptHelper"; import { Apr1Events as devMenu } from "../ui/Apr1"; +import { InternalAPI, NetscriptContext } from "../Netscript/APIWrapper"; export interface INetscriptExtra { heart: { @@ -16,35 +15,37 @@ export interface INetscriptExtra { rainbow(guess: string): void; } -export function NetscriptExtra(player: IPlayer, workerScript: WorkerScript, helper: INetscriptHelper): INetscriptExtra { +export function NetscriptExtra(player: IPlayer): InternalAPI { return { heart: { // Easter egg function - break: function (): number { + break: () => (): number => { return player.karma; }, }, - openDevMenu: function (): void { + openDevMenu: () => (): void => { devMenu.emit(); }, - exploit: function (): void { + exploit: () => (): void => { player.giveExploit(Exploit.UndocumentedFunctionCall); }, - bypass: function (doc: unknown): void { - // reset both fields first - const d = doc as any; - d.completely_unused_field = undefined; - const real_document: any = document; - real_document.completely_unused_field = undefined; - // set one to true and check that it affected the other. - real_document.completely_unused_field = true; - if (d.completely_unused_field && workerScript.ramUsage === 1.6) { - player.giveExploit(Exploit.Bypass); - } - d.completely_unused_field = undefined; - real_document.completely_unused_field = undefined; - }, - alterReality: function (): void { + bypass: + (ctx: NetscriptContext) => + (doc: unknown): void => { + // reset both fields first + const d = doc as any; + d.completely_unused_field = undefined; + const real_document: any = document; + real_document.completely_unused_field = undefined; + // set one to true and check that it affected the other. + real_document.completely_unused_field = true; + if (d.completely_unused_field && ctx.workerScript.ramUsage === 1.6) { + player.giveExploit(Exploit.Bypass); + } + d.completely_unused_field = undefined; + real_document.completely_unused_field = undefined; + }, + alterReality: () => (): void => { // We need to trick webpack into not optimizing a variable that is guaranteed to be false (and doesn't use prototypes) let x = false; const recur = function (depth: number): void { @@ -59,20 +60,22 @@ export function NetscriptExtra(player: IPlayer, workerScript: WorkerScript, help player.giveExploit(Exploit.RealityAlteration); } }, - rainbow: function (guess: unknown): boolean { - function tryGuess(): boolean { - // eslint-disable-next-line no-sync - const verified = bcrypt.compareSync( - helper.string("rainbow", "guess", guess), - "$2a$10$aertxDEkgor8baVtQDZsLuMwwGYmkRM/ohcA6FjmmzIHQeTCsrCcO", - ); - if (verified) { - player.giveExploit(Exploit.INeedARainbow); - return true; + rainbow: + (ctx: NetscriptContext) => + (guess: unknown): boolean => { + function tryGuess(): boolean { + // eslint-disable-next-line no-sync + const verified = bcrypt.compareSync( + ctx.helper.string("guess", guess), + "$2a$10$aertxDEkgor8baVtQDZsLuMwwGYmkRM/ohcA6FjmmzIHQeTCsrCcO", + ); + if (verified) { + player.giveExploit(Exploit.INeedARainbow); + return true; + } + return false; } - return false; - } - return tryGuess(); - }, + return tryGuess(); + }, }; }