Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

BUGFIX: Running scripts may be loaded before main UI #1726

Open
wants to merge 2 commits into
base: dev
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
64 changes: 40 additions & 24 deletions src/NetscriptWorker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,8 @@ import { CompleteRunOptions, getRunningScriptsByArgs } from "./Netscript/Netscri
import { handleUnknownError } from "./Netscript/ErrorMessages";
import { isLegacyScript, legacyScriptExtension, resolveScriptFilePath, ScriptFilePath } from "./Paths/ScriptFilePath";
import { root } from "./Paths/Directory";
import { Player } from "./Player";
import { UIEventEmitter, UIEventType } from "./ui/UIEventEmitter";

export const NetscriptPorts = new Map<PortNumber, Port>();

Expand Down Expand Up @@ -398,33 +400,47 @@ function createAutoexec(server: BaseServer): RunningScript | null {
* into worker scripts so that they will start running
*/
export function loadAllRunningScripts(): void {
const skipScriptLoad = window.location.href.toLowerCase().includes("?noscripts");
if (skipScriptLoad) {
Terminal.warn("Skipped loading player scripts during startup");
console.info("Skipping the load of any scripts during startup");
}
for (const server of GetAllServers()) {
// Reset each server's RAM usage to 0
server.ramUsed = 0;

const rsList = server.savedScripts;
server.savedScripts = undefined;
if (skipScriptLoad || !rsList) {
// Start game with no scripts
continue;
/**
* While loading the save data, the game engine calls this function to load all running scripts. With each script, we
* calculate the offline data, so we need the current "lastUpdate" and "playtimeSinceLastAug" from the save data.
* After the main UI is loaded and the logic of this function starts executing, those info in the Player object might be
* overwritten, so we need to save them here and use them later in "scriptCalculateOfflineProduction".
*/
const playerLastUpdate = Player.lastUpdate;
const playerPlaytimeSinceLastAug = Player.playtimeSinceLastAug;
const unsubscribe = UIEventEmitter.subscribe((event) => {
if (event !== UIEventType.MainUILoaded) {
return;
}
if (server.hostname === "home") {
// Push autoexec script onto the front of the list
const runningScript = createAutoexec(server);
if (runningScript) {
rsList.unshift(runningScript);
}
unsubscribe();
const skipScriptLoad = window.location.href.toLowerCase().includes("?noscripts");
if (skipScriptLoad) {
Terminal.warn("Skipped loading player scripts during startup");
console.info("Skipping the load of any scripts during startup");
}
for (const runningScript of rsList) {
startWorkerScript(runningScript, server);
scriptCalculateOfflineProduction(runningScript);
for (const server of GetAllServers()) {
// Reset each server's RAM usage to 0
server.ramUsed = 0;

const rsList = server.savedScripts;
server.savedScripts = undefined;
if (skipScriptLoad || !rsList) {
// Start game with no scripts
continue;
}
if (server.hostname === "home") {
// Push autoexec script onto the front of the list
const runningScript = createAutoexec(server);
if (runningScript) {
rsList.unshift(runningScript);
}
}
for (const runningScript of rsList) {
startWorkerScript(runningScript, server);
scriptCalculateOfflineProduction(runningScript, playerLastUpdate, playerPlaytimeSinceLastAug);
}
}
}
});
}

/** Run a script from inside another script (run(), exec(), spawn(), etc.) */
Expand Down
10 changes: 7 additions & 3 deletions src/Script/ScriptHelpers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,14 @@ import { scriptKey } from "../utils/helpers/scriptKey";

import type { ScriptFilePath } from "../Paths/ScriptFilePath";

export function scriptCalculateOfflineProduction(runningScript: RunningScript): void {
export function scriptCalculateOfflineProduction(
runningScript: RunningScript,
playerLastUpdate: number,
playerPlaytimeSinceLastAug: number,
): void {
//The Player object stores the last update time from when we were online
const thisUpdate = new Date().getTime();
const lastUpdate = Player.lastUpdate;
const lastUpdate = playerLastUpdate;
const timePassed = Math.max((thisUpdate - lastUpdate) / 1000, 0); //Seconds

//Calculate the "confidence" rating of the script's true production. This is based
Expand Down Expand Up @@ -55,7 +59,7 @@ export function scriptCalculateOfflineProduction(runningScript: RunningScript):
Player.gainHackingExp(expGain);

const moneyGain =
(runningScript.onlineMoneyMade / Player.playtimeSinceLastAug) * timePassed * CONSTANTS.OfflineHackingIncome;
(runningScript.onlineMoneyMade / playerPlaytimeSinceLastAug) * timePassed * CONSTANTS.OfflineHackingIncome;
// money is given to player during engine load
Player.scriptProdSinceLastAug += moneyGain;

Expand Down
4 changes: 2 additions & 2 deletions src/engine.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -291,10 +291,10 @@ const Engine: {
const offlineHackingIncome =
(Player.moneySourceA.hacking / Player.playtimeSinceLastAug) * timeOffline * CONSTANTS.OfflineHackingIncome;
Player.gainMoney(offlineHackingIncome, "hacking");
// Process offline progress

loadAllRunningScripts(); // This also takes care of offline production for those scripts

// Process offline progress
if (Player.currentWork !== null) {
Player.focus = true;
Player.processWork(numCyclesOffline);
Expand Down Expand Up @@ -447,7 +447,7 @@ const Engine: {
Engine._lastUpdate = _thisUpdate - offset;
Player.lastUpdate = _thisUpdate - offset;
Engine.updateGame(diff);
if (GameCycleEvents.hasSubscibers()) {
if (GameCycleEvents.hasSubscribers()) {
ReactDOM.unstable_batchedUpdates(() => {
GameCycleEvents.emit();
});
Expand Down
7 changes: 7 additions & 0 deletions src/ui/GameRoot.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,7 @@ import { HistoryProvider } from "./React/Documentation";
import { GoRoot } from "../Go/ui/GoRoot";
import { Settings } from "../Settings/Settings";
import { isBitNodeFinished } from "../BitNode/BitNodeUtils";
import { UIEventEmitter, UIEventType } from "./UIEventEmitter";

const htmlLocation = location;

Expand Down Expand Up @@ -381,9 +382,15 @@ export function GameRoot(): React.ReactElement {
mainPage = <ImportSave saveData={pageWithContext.saveData} automatic={!!pageWithContext.automatic} />;
withSidebar = false;
bypassGame = true;
break;
}
}

// Emit an event to notify subscribers that the main UI is loaded.
useEffect(() => {
UIEventEmitter.emit(UIEventType.MainUILoaded);
}, []);

return (
<MathJaxContext version={3} src={__webpack_public_path__ + "mathjax/tex-chtml.js"}>
<ErrorBoundary key={errorBoundaryKey} softReset={softReset}>
Expand Down
7 changes: 7 additions & 0 deletions src/ui/UIEventEmitter.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import { EventEmitter } from "../utils/EventEmitter";

export enum UIEventType {
MainUILoaded,
}

export const UIEventEmitter = new EventEmitter<UIEventType[]>();
2 changes: 1 addition & 1 deletion src/utils/EventEmitter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ export class EventEmitter<T extends any[]> {
}
}

hasSubscibers(): boolean {
hasSubscribers(): boolean {
return this.subscribers.size > 0;
}
}
2 changes: 2 additions & 0 deletions test/jest/Save.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { loadAllRunningScripts } from "../../src/NetscriptWorker";
import { Settings } from "../../src/Settings/Settings";
import { Player, setPlayer } from "../../src/Player";
import { PlayerObject } from "../../src/PersonObjects/Player/PlayerObject";
import { UIEventEmitter, UIEventType } from "../../src/ui/UIEventEmitter";
jest.useFakeTimers();

// Direct tests of loading and saving.
Expand Down Expand Up @@ -146,6 +147,7 @@ function loadStandardServers() {
}
}`); // Fix confused highlighting `
loadAllRunningScripts();
UIEventEmitter.emit(UIEventType.MainUILoaded);
}

test("load/saveAllServers", () => {
Expand Down