API: Add API break utilities, and add an API break for bladeburner.getCurrentAction (#1248)

This commit is contained in:
Snarling
2024-05-14 19:24:03 -04:00
committed by GitHub
parent 9dc3b22919
commit 574c284321
6 changed files with 131 additions and 12 deletions
+29
View File
@@ -0,0 +1,29 @@
import { APIBreakInfo } from "./APIBreak";
export const breakInfos261: APIBreakInfo[] = [
{
brokenFunctions: ["ns.bladeburner.getCurrentAction"],
info:
"ns.bladeburner.getCurrentAction:\n" +
'When not performing a bladeburner action, previously returned {type: "Idle", name: ""}, now returns null.\n' +
"Because of this change, the null case now needs to be dealt with prior to accessing properties on the return of getCurrentAction, including destructuring.\n" +
"Additionally, any existing code for filtering out the Idle case will need to be adjusted.\n\n" +
"See https://github.com/bitburner-official/bitburner-src/issues/1249 or PR https://github.com/bitburner-official/bitburner-src/pull/1248 for more details.",
},
{
brokenFunctions: [
"ns.bladeburner.getActionCountRemaining",
"ns.bladeburner.getActionEstimatedSuccessChance",
"ns.bladeburner.getActionTime",
],
info:
"ns.bladeburner.getActionCountRemaining:\n" +
'Previously returned -1 when called with type "Idle" and name "". This is no longer valid usage and will result in an error.\n\n' +
"ns.bladeburner.getActionEstimatedSuccessChance:\n" +
'Previously returned [-1, -1] when called with type "Idle" and name "". This is no longer valid usage and will result in an error.\n\n' +
"ns.bladeburner.getActionTime:\n" +
'Previously returned -1 when called with type "Idle" and name "". This is no longer valid usage and will result in an error.\n\n' +
"See the related changes for ns.bladeburner.getCurrentAction, which were shown earlier in these API break details.\n" +
"In most cases, the fixes for ns.bladeburner.getCurrentAction will fix this group of isses as well.",
},
];
+91
View File
@@ -0,0 +1,91 @@
// General reusable tools for API breaks
import type { ScriptFilePath } from "../../Paths/ScriptFilePath";
import type { Script } from "../../Script/Script";
import { Player } from "@player";
import { GetAllServers } from "../../Server/AllServers";
import { resolveTextFilePath } from "../../Paths/TextFilePath";
import { dialogBoxCreate as dialogBoxCreateOriginal } from "../../ui/React/DialogBox";
import { Terminal } from "../../Terminal";
// Temporary until fixing alerts manager to store alerts outside of react scope
const dialogBoxCreate = (text: string) => setTimeout(() => dialogBoxCreateOriginal(text), 2000);
/** For a single server, map of script filepath to an array of line numbers where impacted functions were detected */
type ScriptImpactMap = Map<ScriptFilePath, number[]>;
/** For an overall API break, map of server hostnames to an array of impacted scripts */
type ImpactMap = Map<string, ScriptImpactMap>;
export interface APIBreakInfo {
/** The API functions impacted by the API break */
brokenFunctions: string[];
/** Info that should be shown to the player, alongside the list of impacted scripts */
info: string;
}
function getImpactedLines(script: Script, brokenFunctions: string[]): number[] | null {
const impactedLines: number[] = [];
script.content.split("\n").forEach((line, i) => {
for (const brokenFunction of brokenFunctions) {
if (line.includes(brokenFunction)) return impactedLines.push(i + 1);
}
});
return impactedLines.length ? impactedLines : null;
}
/** Returns a map keyed by all ser */
function getImpactMap(brokenFunctions: string[]): ImpactMap | null {
const returnMap = new Map<string, ScriptImpactMap>();
for (const server of GetAllServers()) {
const impactedScripts = new Map<ScriptFilePath, number[]>();
for (const [filename, script] of server.scripts) {
const impactedLines = getImpactedLines(script, brokenFunctions);
if (impactedLines) impactedScripts.set(filename, impactedLines);
}
if (impactedScripts.size) returnMap.set(server.hostname, impactedScripts);
}
return returnMap.size ? returnMap : null;
}
/** 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, ...breakInfos: APIBreakInfo[]) {
const details = [];
for (const breakInfo of breakInfos) {
const impactMap = getImpactMap(breakInfo.brokenFunctions);
if (!impactMap) continue;
details.push(
breakInfo.info +
`\n\nUsage of the following functions may have been affected:\n${breakInfo.brokenFunctions.join("\n")}\n\n` +
[...impactMap]
.map(
([hostname, scriptImpactMap]) =>
`Potentially affected files on server ${hostname} (with line numbers):\n` +
[...scriptImpactMap]
.map(
([filename, lineNumbers]) =>
`${filename}: (Line number${lineNumbers.length > 1 ? "s" : ""}: ${lineNumbers.join(", ")})`,
)
.join("\n"),
)
.join("\n\n"),
);
}
if (!details.length) return;
const textFileName = resolveTextFilePath(`APIBreakInfo-${version}.txt`);
if (!textFileName) throw new Error("Version string created an invalid API break file name");
Player.getHomeComputer().writeToTextFile(
textFileName,
`API BREAK INFO FOR ${version}\n\n${details.join("\n\n\n\n")}`,
);
Terminal.warn(`AN API BREAK FROM VERSION ${version} MAY HAVE AFFECTED SOME OF YOUR SCRIPTS.`);
Terminal.warn(`INFORMATION ABOUT THIS POTENTIAL IMPACT HAS BEEN LOGGED IN ${textFileName} ON YOUR HOME COMPUTER.`);
dialogBoxCreate(
`SOME OF YOUR SCRIPTS HAVE POTENTIALLY BEEN IMPACTED BY AN API BREAK, DUE TO CHANGES IN VERSION ${version}\n\n` +
"The following dialog boxes will provide details of the potential impact to your scripts.\n" +
`A file with these details has also been saved on your home computer under filename ${textFileName}.`,
);
details.forEach((detail, i) => {
dialogBoxCreate(`API BREAK VERSION ${version} DETAILS ${i + 1} of ${details.length}\n\n${detail}`);
});
}