mirror of
https://github.com/bitburner-official/bitburner-src.git
synced 2024-11-08 08:43:53 +01:00
CORP: Robotics industry NaN fix + better exports validation (#578)
This commit is contained in:
parent
4c4c4a0335
commit
cbff2a420b
@ -4,7 +4,7 @@
|
|||||||
|
|
||||||
## BasicHGWOptions.additionalMsec property
|
## BasicHGWOptions.additionalMsec property
|
||||||
|
|
||||||
Number of additional milliseconds that will be spent waiting between the start of the function and when it completes. Experimental in 2.2.2, may be removed in 2.3.
|
Number of additional milliseconds that will be spent waiting between the start of the function and when it completes.
|
||||||
|
|
||||||
**Signature:**
|
**Signature:**
|
||||||
|
|
||||||
|
@ -16,7 +16,7 @@ interface BasicHGWOptions
|
|||||||
|
|
||||||
| Property | Modifiers | Type | Description |
|
| Property | Modifiers | Type | Description |
|
||||||
| --- | --- | --- | --- |
|
| --- | --- | --- | --- |
|
||||||
| [additionalMsec?](./bitburner.basichgwoptions.additionalmsec.md) | | number | _(Optional)_ Number of additional milliseconds that will be spent waiting between the start of the function and when it completes. Experimental in 2.2.2, may be removed in 2.3. |
|
| [additionalMsec?](./bitburner.basichgwoptions.additionalmsec.md) | | number | _(Optional)_ Number of additional milliseconds that will be spent waiting between the start of the function and when it completes. |
|
||||||
| [stock?](./bitburner.basichgwoptions.stock.md) | | boolean | _(Optional)_ Set to true this action will affect the stock market. |
|
| [stock?](./bitburner.basichgwoptions.stock.md) | | boolean | _(Optional)_ Set to true this action will affect the stock market. |
|
||||||
| [threads?](./bitburner.basichgwoptions.threads.md) | | number | _(Optional)_ Number of threads to use for this function. Must be less than or equal to the number of threads the script is running with. |
|
| [threads?](./bitburner.basichgwoptions.threads.md) | | number | _(Optional)_ Number of threads to use for this function. Must be less than or equal to the number of threads the script is running with. |
|
||||||
|
|
||||||
|
@ -15,7 +15,6 @@ cancelExportMaterial(
|
|||||||
targetDivision: string,
|
targetDivision: string,
|
||||||
targetCity: CityName | `${CityName}`,
|
targetCity: CityName | `${CityName}`,
|
||||||
materialName: string,
|
materialName: string,
|
||||||
amt: number,
|
|
||||||
): void;
|
): void;
|
||||||
```
|
```
|
||||||
|
|
||||||
@ -28,7 +27,6 @@ cancelExportMaterial(
|
|||||||
| targetDivision | string | Target division |
|
| targetDivision | string | Target division |
|
||||||
| targetCity | [CityName](./bitburner.cityname.md) \| \`${[CityName](./bitburner.cityname.md)<!-- -->}\` | Target city |
|
| targetCity | [CityName](./bitburner.cityname.md) \| \`${[CityName](./bitburner.cityname.md)<!-- -->}\` | Target city |
|
||||||
| materialName | string | Name of the material |
|
| materialName | string | Name of the material |
|
||||||
| amt | number | Amount of material to export. |
|
|
||||||
|
|
||||||
**Returns:**
|
**Returns:**
|
||||||
|
|
||||||
|
@ -22,7 +22,7 @@ Requires the Warehouse API upgrade from your corporation.
|
|||||||
| --- | --- |
|
| --- | --- |
|
||||||
| [bulkPurchase(divisionName, city, materialName, amt)](./bitburner.warehouseapi.bulkpurchase.md) | Set material to bulk buy |
|
| [bulkPurchase(divisionName, city, materialName, amt)](./bitburner.warehouseapi.bulkpurchase.md) | Set material to bulk buy |
|
||||||
| [buyMaterial(divisionName, city, materialName, amt)](./bitburner.warehouseapi.buymaterial.md) | Set material buy data |
|
| [buyMaterial(divisionName, city, materialName, amt)](./bitburner.warehouseapi.buymaterial.md) | Set material buy data |
|
||||||
| [cancelExportMaterial(sourceDivision, sourceCity, targetDivision, targetCity, materialName, amt)](./bitburner.warehouseapi.cancelexportmaterial.md) | Cancel material export |
|
| [cancelExportMaterial(sourceDivision, sourceCity, targetDivision, targetCity, materialName)](./bitburner.warehouseapi.cancelexportmaterial.md) | Cancel material export |
|
||||||
| [discontinueProduct(divisionName, productName)](./bitburner.warehouseapi.discontinueproduct.md) | Discontinue a product. |
|
| [discontinueProduct(divisionName, productName)](./bitburner.warehouseapi.discontinueproduct.md) | Discontinue a product. |
|
||||||
| [exportMaterial(sourceDivision, sourceCity, targetDivision, targetCity, materialName, amt)](./bitburner.warehouseapi.exportmaterial.md) | Set material export data |
|
| [exportMaterial(sourceDivision, sourceCity, targetDivision, targetCity, materialName, amt)](./bitburner.warehouseapi.exportmaterial.md) | Set material export data |
|
||||||
| [getMaterial(divisionName, city, materialName)](./bitburner.warehouseapi.getmaterial.md) | Get material data |
|
| [getMaterial(divisionName, city, materialName)](./bitburner.warehouseapi.getmaterial.md) | Get material data |
|
||||||
|
@ -48,6 +48,17 @@ export function NewDivision(corporation: Corporation, industry: IndustryType, na
|
|||||||
export function removeDivision(corporation: Corporation, name: string) {
|
export function removeDivision(corporation: Corporation, name: string) {
|
||||||
if (!corporation.divisions.has(name)) throw new Error("There is no division called " + name);
|
if (!corporation.divisions.has(name)) throw new Error("There is no division called " + name);
|
||||||
corporation.divisions.delete(name);
|
corporation.divisions.delete(name);
|
||||||
|
// We also need to remove any exports that were pointing to the old division
|
||||||
|
for (const otherDivision of corporation.divisions.values()) {
|
||||||
|
for (const warehouse of getRecordValues(otherDivision.warehouses)) {
|
||||||
|
for (const material of getRecordValues(warehouse.materials)) {
|
||||||
|
// Work backwards through exports array so splicing doesn't affect the loop
|
||||||
|
for (let i = material.exports.length - 1; i >= 0; i--) {
|
||||||
|
if (material.exports[i].division === name) material.exports.splice(i, 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export function purchaseOffice(corporation: Corporation, division: Division, city: CityName): void {
|
export function purchaseOffice(corporation: Corporation, division: Division, city: CityName): void {
|
||||||
@ -443,52 +454,62 @@ export function Research(researchingDivision: Division, researchName: CorpResear
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Set a new export for a material. Throw on any invalid input. */
|
||||||
export function ExportMaterial(
|
export function ExportMaterial(
|
||||||
divisionName: string,
|
targetDivision: Division,
|
||||||
cityName: CityName,
|
targetCity: CityName,
|
||||||
material: Material,
|
material: Material,
|
||||||
amt: string,
|
amount: string,
|
||||||
division?: Division,
|
|
||||||
): void {
|
): void {
|
||||||
// Sanitize amt
|
if (!isRelevantMaterial(material.name, targetDivision)) {
|
||||||
let sanitizedAmt = amt.replace(/\s+/g, "").toUpperCase();
|
throw new Error(`You cannot export material: ${material.name} to division: ${targetDivision.name}!`);
|
||||||
|
}
|
||||||
|
if (!targetDivision.warehouses[targetCity]) {
|
||||||
|
throw new Error(`Cannot export to ${targetCity} in division ${targetDivision.name} because there is no warehouse.`);
|
||||||
|
}
|
||||||
|
if (material === targetDivision.warehouses[targetCity]?.materials[material.name]) {
|
||||||
|
throw new Error(`Source and target division/city cannot be the same.`);
|
||||||
|
}
|
||||||
|
for (const existingExport of material.exports) {
|
||||||
|
if (existingExport.division === targetDivision.name && existingExport.city === targetCity) {
|
||||||
|
throw new Error(`Tried to initialize an export to a duplicate warehouse.
|
||||||
|
Target warehouse (division / city): ${existingExport.division} / ${existingExport.city}
|
||||||
|
Existing export amount: ${existingExport.amount}
|
||||||
|
Attempted export amount: ${amount}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Perform sanitization and tests
|
||||||
|
let sanitizedAmt = amount.replace(/\s+/g, "").toUpperCase();
|
||||||
sanitizedAmt = sanitizedAmt.replace(/[^-()\d/*+.MAXEPRODINV]/g, "");
|
sanitizedAmt = sanitizedAmt.replace(/[^-()\d/*+.MAXEPRODINV]/g, "");
|
||||||
let temp = sanitizedAmt.replace(/MAX/g, "1");
|
for (const testReplacement of ["(1.23)", "(-1.23)"]) {
|
||||||
temp = temp.replace(/IPROD/g, "1");
|
const replaced = sanitizedAmt.replace(/(IPROD|EPROD|IINV|EINV)/g, testReplacement);
|
||||||
temp = temp.replace(/EPROD/g, "1");
|
let evaluated, error;
|
||||||
temp = temp.replace(/IINV/g, "1");
|
try {
|
||||||
temp = temp.replace(/EINV/g, "1");
|
evaluated = eval(replaced);
|
||||||
try {
|
} catch (e) {
|
||||||
temp = eval(temp);
|
error = e;
|
||||||
} catch (e) {
|
}
|
||||||
throw new Error("Invalid expression entered for export amount: " + e);
|
if (!error && isNaN(evaluated)) error = "evaluated value is NaN";
|
||||||
|
if (error) {
|
||||||
|
throw new Error(`Error while trying to set the exported amount of ${material.name}.
|
||||||
|
Error occurred while testing keyword replacement with ${testReplacement}.
|
||||||
|
Your input: ${amount}
|
||||||
|
Sanitized input: ${sanitizedAmt}
|
||||||
|
Input after replacement: ${replaced}
|
||||||
|
Evaluated value: ${evaluated}
|
||||||
|
Error encountered: ${error}`);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const n = parseFloat(temp);
|
const exportObj = { division: targetDivision.name, city: targetCity, amount: sanitizedAmt };
|
||||||
|
|
||||||
if (n == null || isNaN(n)) {
|
|
||||||
throw new Error("Invalid amount entered for export");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!division || !isRelevantMaterial(material.name, division)) {
|
|
||||||
throw new Error(`You cannot export material: ${material.name} to division: ${divisionName}!`);
|
|
||||||
}
|
|
||||||
|
|
||||||
const exportObj = { division: divisionName, city: cityName, amount: sanitizedAmt };
|
|
||||||
material.exports.push(exportObj);
|
material.exports.push(exportObj);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function CancelExportMaterial(divisionName: string, cityName: string, material: Material, amt: string): void {
|
export function CancelExportMaterial(divisionName: string, cityName: CityName, material: Material): void {
|
||||||
for (let i = 0; i < material.exports.length; ++i) {
|
const index = material.exports.findIndex((exp) => exp.division === divisionName && exp.city === cityName);
|
||||||
if (
|
if (index === -1) return;
|
||||||
material.exports[i].division !== divisionName ||
|
material.exports.splice(index, 1);
|
||||||
material.exports[i].city !== cityName ||
|
|
||||||
material.exports[i].amount !== amt
|
|
||||||
)
|
|
||||||
continue;
|
|
||||||
material.exports.splice(i, 1);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export function LimitProductProduction(product: Product, cityName: CityName, quantity: number): void {
|
export function LimitProductProduction(product: Product, cityName: CityName, quantity: number): void {
|
||||||
|
@ -434,7 +434,12 @@ export class Division {
|
|||||||
const divider = requiredMatsEntries.length;
|
const divider = requiredMatsEntries.length;
|
||||||
for (const [reqMatName, reqMat] of requiredMatsEntries) {
|
for (const [reqMatName, reqMat] of requiredMatsEntries) {
|
||||||
const reqMatQtyNeeded = reqMat * prod * producableFrac;
|
const reqMatQtyNeeded = reqMat * prod * producableFrac;
|
||||||
warehouse.materials[reqMatName].stored -= reqMatQtyNeeded;
|
// producableFrac already takes into account that we have enough stored
|
||||||
|
// Math.max is used here to avoid stored becoming negative (which can lead to NaNs)
|
||||||
|
warehouse.materials[reqMatName].stored = Math.max(
|
||||||
|
0,
|
||||||
|
warehouse.materials[reqMatName].stored - reqMatQtyNeeded,
|
||||||
|
);
|
||||||
warehouse.materials[reqMatName].productionAmount = 0;
|
warehouse.materials[reqMatName].productionAmount = 0;
|
||||||
warehouse.materials[reqMatName].productionAmount -=
|
warehouse.materials[reqMatName].productionAmount -=
|
||||||
reqMatQtyNeeded / (corpConstants.secondsPerMarketCycle * marketCycles);
|
reqMatQtyNeeded / (corpConstants.secondsPerMarketCycle * marketCycles);
|
||||||
@ -446,7 +451,7 @@ export class Division {
|
|||||||
let tempQlt =
|
let tempQlt =
|
||||||
office.employeeProductionByJob[CorpEmployeeJob.Engineer] / 90 +
|
office.employeeProductionByJob[CorpEmployeeJob.Engineer] / 90 +
|
||||||
Math.pow(this.researchPoints, this.researchFactor) +
|
Math.pow(this.researchPoints, this.researchFactor) +
|
||||||
Math.pow(warehouse.materials["AI Cores"].stored, this.aiCoreFactor) / 10e3;
|
Math.pow(Math.max(0, warehouse.materials["AI Cores"].stored), this.aiCoreFactor) / 10e3;
|
||||||
const logQlt = Math.max(Math.pow(tempQlt, 0.5), 1);
|
const logQlt = Math.max(Math.pow(tempQlt, 0.5), 1);
|
||||||
tempQlt = Math.min(tempQlt, avgQlt * logQlt);
|
tempQlt = Math.min(tempQlt, avgQlt * logQlt);
|
||||||
warehouse.materials[this.producedMaterials[j]].quality = Math.max(
|
warehouse.materials[this.producedMaterials[j]].quality = Math.max(
|
||||||
|
@ -54,7 +54,7 @@ export function ExportModal(props: IProps): React.ReactElement {
|
|||||||
|
|
||||||
function exportMaterial(): void {
|
function exportMaterial(): void {
|
||||||
try {
|
try {
|
||||||
ExportMaterial(targetDivision.name, targetCity, props.mat, exportAmount, targetDivision);
|
ExportMaterial(targetDivision, targetCity, props.mat, exportAmount);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
dialogBoxCreate(err + "");
|
dialogBoxCreate(err + "");
|
||||||
}
|
}
|
||||||
|
@ -465,29 +465,22 @@ export function NetscriptCorporation(): InternalAPI<NSCorporation> {
|
|||||||
checkAccess(ctx, CorpUnlockName.WarehouseAPI);
|
checkAccess(ctx, CorpUnlockName.WarehouseAPI);
|
||||||
const sourceDivision = helpers.string(ctx, "sourceDivision", _sourceDivision);
|
const sourceDivision = helpers.string(ctx, "sourceDivision", _sourceDivision);
|
||||||
assertMember(ctx, CityName, "City", "sourceCity", sourceCity);
|
assertMember(ctx, CityName, "City", "sourceCity", sourceCity);
|
||||||
const targetDivision = helpers.string(ctx, "targetDivision", _targetDivision);
|
const targetDivision = getDivision(helpers.string(ctx, "targetDivision", _targetDivision));
|
||||||
assertMember(ctx, CityName, "City", "targetCity", targetCity);
|
assertMember(ctx, CityName, "City", "targetCity", targetCity);
|
||||||
assertMember(ctx, corpConstants.materialNames, "Material Name", "materialName", materialName);
|
assertMember(ctx, corpConstants.materialNames, "Material Name", "materialName", materialName);
|
||||||
const amt = helpers.string(ctx, "amt", _amt);
|
const amt = helpers.string(ctx, "amt", _amt);
|
||||||
ExportMaterial(
|
ExportMaterial(targetDivision, targetCity, getMaterial(sourceDivision, sourceCity, materialName), amt);
|
||||||
targetDivision,
|
|
||||||
targetCity,
|
|
||||||
getMaterial(sourceDivision, sourceCity, materialName),
|
|
||||||
amt + "",
|
|
||||||
getDivision(targetDivision),
|
|
||||||
);
|
|
||||||
},
|
},
|
||||||
cancelExportMaterial:
|
cancelExportMaterial:
|
||||||
(ctx) =>
|
(ctx) =>
|
||||||
(_sourceDivision, sourceCity, _targetDivision, targetCity, materialName, _amt): void => {
|
(_sourceDivision, sourceCity, _targetDivision, targetCity, materialName): void => {
|
||||||
checkAccess(ctx, CorpUnlockName.WarehouseAPI);
|
checkAccess(ctx, CorpUnlockName.WarehouseAPI);
|
||||||
const sourceDivision = helpers.string(ctx, "sourceDivision", _sourceDivision);
|
const sourceDivision = helpers.string(ctx, "sourceDivision", _sourceDivision);
|
||||||
assertMember(ctx, CityName, "City Name", "sourceCity", sourceCity);
|
assertMember(ctx, CityName, "City Name", "sourceCity", sourceCity);
|
||||||
const targetDivision = helpers.string(ctx, "targetDivision", _targetDivision);
|
const targetDivision = helpers.string(ctx, "targetDivision", _targetDivision);
|
||||||
assertMember(ctx, CityName, "City Name", "targetCity", targetCity);
|
assertMember(ctx, CityName, "City Name", "targetCity", targetCity);
|
||||||
assertMember(ctx, corpConstants.materialNames, "Material Name", "materialName", materialName);
|
assertMember(ctx, corpConstants.materialNames, "Material Name", "materialName", materialName);
|
||||||
const amt = helpers.string(ctx, "amt", _amt);
|
CancelExportMaterial(targetDivision, targetCity, getMaterial(sourceDivision, sourceCity, materialName));
|
||||||
CancelExportMaterial(targetDivision, targetCity, getMaterial(sourceDivision, sourceCity, materialName), amt);
|
|
||||||
},
|
},
|
||||||
limitMaterialProduction: (ctx) => (_divisionName, cityName, materialName, _qty) => {
|
limitMaterialProduction: (ctx) => (_divisionName, cityName, materialName, _qty) => {
|
||||||
checkAccess(ctx, CorpUnlockName.WarehouseAPI);
|
checkAccess(ctx, CorpUnlockName.WarehouseAPI);
|
||||||
|
@ -37,6 +37,8 @@ import { SpecialServers } from "./Server/data/SpecialServers";
|
|||||||
import { v2APIBreak } from "./utils/v2APIBreak";
|
import { v2APIBreak } from "./utils/v2APIBreak";
|
||||||
import { Corporation } from "./Corporation/Corporation";
|
import { Corporation } from "./Corporation/Corporation";
|
||||||
import { Terminal } from "./Terminal";
|
import { Terminal } from "./Terminal";
|
||||||
|
import { getRecordValues } from "./Types/Record";
|
||||||
|
import { ExportMaterial } from "./Corporation/Actions";
|
||||||
|
|
||||||
/* SaveObject.js
|
/* SaveObject.js
|
||||||
* Defines the object used to save/load games
|
* Defines the object used to save/load games
|
||||||
@ -688,6 +690,38 @@ function evaluateVersionCompatibility(ver: string | number): void {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// Sanitize corporation exports
|
||||||
|
let anyExportsFailed = false;
|
||||||
|
if (Player.corporation) {
|
||||||
|
for (const division of Player.corporation.divisions.values()) {
|
||||||
|
for (const warehouse of getRecordValues(division.warehouses)) {
|
||||||
|
for (const material of getRecordValues(warehouse.materials)) {
|
||||||
|
const originalExports = material.exports;
|
||||||
|
// Clear all exports for the material
|
||||||
|
material.exports = [];
|
||||||
|
for (const originalExport of originalExports) {
|
||||||
|
// Throw if there was a failure re-establishing an export
|
||||||
|
try {
|
||||||
|
const targetDivision = Player.corporation.divisions.get(originalExport.division);
|
||||||
|
if (!targetDivision) throw new Error(`Target division ${originalExport.division} did not exist`);
|
||||||
|
// Set the export again. ExportMaterial throws on failure
|
||||||
|
ExportMaterial(targetDivision, originalExport.city, material, originalExport.amount);
|
||||||
|
} catch (e) {
|
||||||
|
anyExportsFailed = true;
|
||||||
|
// We just need the text error, not a full stack trace
|
||||||
|
console.error(`Failed to load export of material ${material.name} (${division.name} ${warehouse.city})
|
||||||
|
Original export details: ${JSON.stringify(originalExport)}
|
||||||
|
Error: ${e}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (anyExportsFailed)
|
||||||
|
Terminal.error(
|
||||||
|
"Some material exports failed to validate while loading and have been removed. See console for more info.",
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
2
src/ScriptEditor/NetscriptDefinitions.d.ts
vendored
2
src/ScriptEditor/NetscriptDefinitions.d.ts
vendored
@ -7093,7 +7093,6 @@ export interface WarehouseAPI {
|
|||||||
* @param targetDivision - Target division
|
* @param targetDivision - Target division
|
||||||
* @param targetCity - Target city
|
* @param targetCity - Target city
|
||||||
* @param materialName - Name of the material
|
* @param materialName - Name of the material
|
||||||
* @param amt - Amount of material to export.
|
|
||||||
*/
|
*/
|
||||||
cancelExportMaterial(
|
cancelExportMaterial(
|
||||||
sourceDivision: string,
|
sourceDivision: string,
|
||||||
@ -7101,7 +7100,6 @@ export interface WarehouseAPI {
|
|||||||
targetDivision: string,
|
targetDivision: string,
|
||||||
targetCity: CityName | `${CityName}`,
|
targetCity: CityName | `${CityName}`,
|
||||||
materialName: string,
|
materialName: string,
|
||||||
amt: number,
|
|
||||||
): void;
|
): void;
|
||||||
/**
|
/**
|
||||||
* Purchase warehouse for a new city
|
* Purchase warehouse for a new city
|
||||||
|
Loading…
Reference in New Issue
Block a user