diff --git a/src/NetscriptFunctions.ts b/src/NetscriptFunctions.ts index 996864fb4..3d292e73d 100644 --- a/src/NetscriptFunctions.ts +++ b/src/NetscriptFunctions.ts @@ -582,10 +582,12 @@ export const ns: InternalAPI = { return true; } if (!Player.hasProgram(CompletedProgramName.nuke)) { - throw helpers.errorMessage(ctx, "You do not have the NUKE.exe virus!"); + helpers.log(ctx, () => "You do not have the NUKE.exe virus!"); + return false; } if (server.openPortCount < server.numOpenPortsRequired) { - throw helpers.errorMessage(ctx, "Not enough ports opened to use NUKE.exe virus."); + helpers.log(ctx, () => "Not enough ports opened to use NUKE.exe virus."); + return false; } server.hasAdminRights = true; helpers.log(ctx, () => `Executed NUKE.exe virus on '${server.hostname}' to gain root access.`); @@ -599,7 +601,8 @@ export const ns: InternalAPI = { return false; } if (!Player.hasProgram(CompletedProgramName.bruteSsh)) { - throw helpers.errorMessage(ctx, "You do not have the BruteSSH.exe program!"); + helpers.log(ctx, () => "You do not have the BruteSSH.exe program!"); + return false; } if (!server.sshPortOpen) { helpers.log(ctx, () => `Executed BruteSSH.exe on '${server.hostname}' to open SSH port (22).`); @@ -618,7 +621,8 @@ export const ns: InternalAPI = { return false; } if (!Player.hasProgram(CompletedProgramName.ftpCrack)) { - throw helpers.errorMessage(ctx, "You do not have the FTPCrack.exe program!"); + helpers.log(ctx, () => "You do not have the FTPCrack.exe program!"); + return false; } if (!server.ftpPortOpen) { helpers.log(ctx, () => `Executed FTPCrack.exe on '${server.hostname}' to open FTP port (21).`); @@ -637,7 +641,8 @@ export const ns: InternalAPI = { return false; } if (!Player.hasProgram(CompletedProgramName.relaySmtp)) { - throw helpers.errorMessage(ctx, "You do not have the relaySMTP.exe program!"); + helpers.log(ctx, () => "You do not have the relaySMTP.exe program!"); + return false; } if (!server.smtpPortOpen) { helpers.log(ctx, () => `Executed relaySMTP.exe on '${server.hostname}' to open SMTP port (25).`); @@ -656,7 +661,8 @@ export const ns: InternalAPI = { return false; } if (!Player.hasProgram(CompletedProgramName.httpWorm)) { - throw helpers.errorMessage(ctx, "You do not have the HTTPWorm.exe program!"); + helpers.log(ctx, () => "You do not have the HTTPWorm.exe program!"); + return false; } if (!server.httpPortOpen) { helpers.log(ctx, () => `Executed HTTPWorm.exe on '${server.hostname}' to open HTTP port (80).`); @@ -675,7 +681,8 @@ export const ns: InternalAPI = { return false; } if (!Player.hasProgram(CompletedProgramName.sqlInject)) { - throw helpers.errorMessage(ctx, "You do not have the SQLInject.exe program!"); + helpers.log(ctx, () => "You do not have the SQLInject.exe program!"); + return false; } if (!server.sqlPortOpen) { helpers.log(ctx, () => `Executed SQLInject.exe on '${server.hostname}' to open SQL port (1433).`); diff --git a/src/utils/APIBreaks/3.0.0.ts b/src/utils/APIBreaks/3.0.0.ts index aa27169c0..e352068f1 100644 --- a/src/utils/APIBreaks/3.0.0.ts +++ b/src/utils/APIBreaks/3.0.0.ts @@ -115,7 +115,7 @@ export const breakingChanges300: VersionBreakingChange = { brokenAPIs: [], info: "With some APIs, when you passed values to their params, you could pass a value that was not an exact match. " + - 'For example, with "ns.singularity.commitCrime", you could pass "Rob Store", "rob store", "RobStore", "robstore", "robStore", etc. ' + + 'For example, with ns.singularity.commitCrime, you could pass "Rob Store", "rob store", "RobStore", "robstore", "robStore", etc. ' + 'This is called "fuzzy matching". Now, you must pass an exact value (i.e., Rob Store). This change affects:\n' + "- Bladeburner action and type: BladeburnerActionType, BladeburnerGeneralActionName, BladeburnerContractName, BladeburnerOperationName, BladeburnerBlackOpName, SpecialBladeburnerActionTypeForSleeve, BladeburnerActionTypeForSleeve.\n" + "- Crime: CrimeType\n" + @@ -129,5 +129,19 @@ export const breakingChanges300: VersionBreakingChange = { showPopUp: true, doNotSkip: true, }, + { + brokenAPIs: [ + { name: "ns.nuke" }, + { name: "ns.brutessh" }, + { name: "ns.ftpcrack" }, + { name: "ns.relaysmtp" }, + { name: "ns.httpworm" }, + { name: "ns.sqlinject" }, + ], + info: + `ns.nuke, ns.brutessh, ns.ftpcrack, ns.relaysmtp, ns.httpworm, and ns.sqlinject now do not throw an error when you do not have the required .exe file or enough opened ports.\n` + + "This should not be a problem with most scripts. However, if you were catching errors and branching on the result of success/failure, you will need to use the return value instead.", + showPopUp: true, + }, ], }; diff --git a/src/utils/APIBreaks/APIBreak.ts b/src/utils/APIBreaks/APIBreak.ts index 96b3953d2..3139e4c70 100644 --- a/src/utils/APIBreaks/APIBreak.ts +++ b/src/utils/APIBreaks/APIBreak.ts @@ -72,13 +72,18 @@ function detectImpactAndMigrateLines(script: Script, brokenFunctions: APIBreakIn } /** Returns a map keyed by hostname */ -function detectImpactAndMigrate(brokenFunctions: APIBreakInfo["brokenAPIs"]): ImpactMap { +function detectImpactAndMigrate(brokenFunctions: APIBreakInfo["brokenAPIs"]): { + impactMap: ImpactMap; + totalDetectedLines: number; +} { const returnMap = new Map(); + let totalDetectedLines = 0; for (const server of GetAllServers()) { const impactedScripts = new Map(); for (const [filename, script] of server.scripts) { const impactedLines = detectImpactAndMigrateLines(script, brokenFunctions); if (impactedLines) { + totalDetectedLines += impactedLines.length; impactedScripts.set(filename, impactedLines); } } @@ -86,18 +91,21 @@ function detectImpactAndMigrate(brokenFunctions: APIBreakInfo["brokenAPIs"]): Im returnMap.set(server.hostname, impactedScripts); } } - return returnMap; + return { impactMap: returnMap, totalDetectedLines }; } /** Show the player a dialog for their API breaks, and save an info file for the player to review later */ export function showAPIBreaks(version: string, { additionalText, apiBreakingChanges }: VersionBreakingChange) { const details: { + apiBreakInfo: APIBreakInfo; text: string; + totalDetectedLines: number; showPopUp: boolean; }[] = []; let numberOfPopUps = 0; for (const breakInfo of apiBreakingChanges) { - const impactMap = detectImpactAndMigrate(breakInfo.brokenAPIs); + const scanResult = detectImpactAndMigrate(breakInfo.brokenAPIs); + const impactMap = scanResult.impactMap; // Skip processing if we don't find any affected code and the breaking change does not enable the "doNotSkip" flag. if (impactMap.size === 0 && !breakInfo.doNotSkip) { continue; @@ -124,7 +132,9 @@ export function showAPIBreaks(version: string, { additionalText, apiBreakingChan .join("\n\n"); } details.push({ + apiBreakInfo: breakInfo, text: detailText, + totalDetectedLines: scanResult.totalDetectedLines, showPopUp: breakInfo.showPopUp, }); if (breakInfo.showPopUp) { @@ -155,7 +165,18 @@ export function showAPIBreaks(version: string, { additionalText, apiBreakingChan if (!detail.showPopUp) { continue; } - dialogBoxCreate(`API BREAK VERSION ${version} DETAILS ${popUpIndex + 1} of ${numberOfPopUps}\n\n${detail.text}`); + dialogBoxCreate( + `API BREAK VERSION ${version} DETAILS ${popUpIndex + 1} of ${numberOfPopUps}\n\n${detail.apiBreakInfo.info}` + + /** + * If we can detect the affected lines via apiBreakInfo.brokenAPIs, we will show the number of affected lines. + * However, some breaking changes cannot be reliably detected, so we intentionally leave apiBreakInfo.brokenAPIs + * empty. With these changes, the number of affected lines is always 0, but saying that there are no affected + * lines is misleading, so we won't say anything about the number of affected lines. + */ + (detail.apiBreakInfo.brokenAPIs.length > 0 + ? `\n\nWe found ${pluralize(detail.totalDetectedLines, "affected line")}.` + : ""), + ); ++popUpIndex; } }