Improve event emitter

This commit is contained in:
Olivier Gagnon 2021-09-18 15:44:39 -04:00
parent 4b6a6300f5
commit 61e3959a25
15 changed files with 46 additions and 61 deletions

@ -3,4 +3,4 @@
*/
import { EventEmitter } from "../utils/EventEmitter";
export const WorkerScriptStartStopEventEmitter = new EventEmitter();
export const WorkerScriptStartStopEventEmitter = new EventEmitter<[]>();

@ -116,7 +116,7 @@ function removeWorkerScript(workerScript: WorkerScript, rerenderUi = true): void
}
if (rerenderUi) {
WorkerScriptStartStopEventEmitter.emitEvent();
WorkerScriptStartStopEventEmitter.emit();
}
} else {
console.error(`Invalid argument passed into removeWorkerScript():`);

@ -1359,7 +1359,7 @@ function NetscriptFunctions(workerScript) {
for (let i = server.runningScripts.length - 1; i >= 0; --i) {
killWorkerScript(server.runningScripts[i], server.ip, false);
}
WorkerScriptStartStopEventEmitter.emitEvent();
WorkerScriptStartStopEventEmitter.emit();
workerScript.log(
"killall",
`Killing all scripts on '${server.hostname}'. May take a few minutes for the scripts to die.`,

@ -45,7 +45,7 @@ export function prestigeWorkerScripts() {
killWorkerScript(ws);
}
WorkerScriptStartStopEventEmitter.emitEvent();
WorkerScriptStartStopEventEmitter.emit();
workerScripts.clear();
}
@ -501,7 +501,7 @@ export function createAndAddWorkerScript(runningScriptObj, server, parent) {
// Add the WorkerScript to the global pool
workerScripts.set(pid, s);
WorkerScriptStartStopEventEmitter.emitEvent();
WorkerScriptStartStopEventEmitter.emit();
// Start the script's execution
let p = null; // Script's resulting promise

@ -311,4 +311,4 @@ export function initStockMarketFnForReact(): void {
initSymbolToStockMap();
}
export const eventEmitterForUiReset = new EventEmitter();
export const eventEmitterForUiReset = new EventEmitter<[]>();

@ -27,7 +27,7 @@ type IProps = {
buyStockLong: txFn;
buyStockShort: txFn;
cancelOrder: (params: any) => void;
eventEmitterForReset?: EventEmitter;
eventEmitterForReset?: EventEmitter<[]>;
initStockMarket: () => void;
p: IPlayer;
placeOrder: placeOrderFn;

@ -31,7 +31,7 @@ type IProps = {
buyStockLong: txFn;
buyStockShort: txFn;
cancelOrder: (params: any) => void;
eventEmitterForReset?: EventEmitter;
eventEmitterForReset?: EventEmitter<[]>;
p: IPlayer;
placeOrder: placeOrderFn;
sellStockLong: txFn;

@ -73,7 +73,6 @@ export interface ITerminal {
executeCommand(router: IRouter, player: IPlayer, command: string): void;
executeCommands(router: IRouter, player: IPlayer, commands: string): void;
// If there was any changes, will return true, once.
pollChanges(): boolean;
process(router: IRouter, player: IPlayer, cycles: number): void;
prestige(): void;
getProgressText(): string;

@ -5,6 +5,7 @@ import { HacknetServer } from "../Hacknet/HacknetServer";
import { BaseServer } from "../Server/BaseServer";
import { Programs } from "../Programs/Programs";
import { CodingContractResult } from "../CodingContracts";
import { TerminalEvents } from "./TerminalEvents";
import { TextFile } from "../TextFile";
import { Script } from "../Script/Script";
@ -69,7 +70,6 @@ import { unalias } from "./commands/unalias";
import { wget } from "./commands/wget";
export class Terminal implements ITerminal {
hasChanges = false;
// Flags to determine whether the player is currently running a hack or an analyze
action: TTimer | null = null;
@ -88,18 +88,10 @@ export class Terminal implements ITerminal {
process(router: IRouter, player: IPlayer, cycles: number): void {
if (this.action === null) return;
this.action.timeLeft -= (CONSTANTS._idleSpeed * cycles) / 1000;
this.hasChanges = true;
TerminalEvents.emit();
if (this.action.timeLeft < 0) this.finishAction(router, player, false);
}
pollChanges(): boolean {
if (this.hasChanges) {
this.hasChanges = false;
return true;
}
return false;
}
append(item: Output | Link): void {
this.outputHistory.push(item);
if (this.outputHistory.length > Settings.MaxTerminalCapacity) {
@ -109,12 +101,12 @@ export class Terminal implements ITerminal {
print(s: string): void {
this.append(new Output(s, "primary"));
this.hasChanges = true;
TerminalEvents.emit();
}
error(s: string): void {
this.append(new Output(s, "error"));
this.hasChanges = true;
TerminalEvents.emit();
}
startHack(player: IPlayer): void {
@ -327,7 +319,7 @@ export class Terminal implements ITerminal {
setcwd(dir: string): void {
this.currDir = dir;
this.hasChanges = true;
TerminalEvents.emit();
}
async runContract(player: IPlayer, contractName: string): Promise<void> {
@ -490,7 +482,7 @@ export class Terminal implements ITerminal {
clear(): void {
// TODO: remove this once we figure out the height issue.
this.outputHistory = [new Output(`Bitburner v${CONSTANTS.Version}`, "primary")];
this.hasChanges = true;
TerminalEvents.emit();
}
prestige(): void {

@ -0,0 +1,2 @@
import { EventEmitter } from "../utils/EventEmitter";
export const TerminalEvents = new EventEmitter<[]>();

@ -9,6 +9,6 @@ export function killall(terminal: ITerminal, router: IRouter, player: IPlayer, s
for (let i = server.runningScripts.length - 1; i >= 0; --i) {
killWorkerScript(server.runningScripts[i], server.ip, false);
}
WorkerScriptStartStopEventEmitter.emitEvent();
WorkerScriptStartStopEventEmitter.emit();
terminal.print("Killing all running scripts");
}

@ -11,6 +11,7 @@ import { ITerminal, Output, Link } from "../ITerminal";
import { IRouter } from "../../ui/Router";
import { IPlayer } from "../../PersonObjects/IPlayer";
import { TerminalInput } from "./TerminalInput";
import { TerminalEvents } from "../TerminalEvents";
interface IActionTimerProps {
terminal: ITerminal;
@ -55,10 +56,7 @@ export function TerminalRoot({ terminal, router, player }: IProps): React.ReactE
}
useEffect(() => {
const id = setInterval(() => {
if (terminal.pollChanges()) rerender();
}, 100);
return () => clearInterval(id);
return TerminalEvents.subscribe(rerender);
}, []);
function doScroll(): void {

@ -49,11 +49,7 @@ export function ServerAccordions(props: IProps): React.ReactElement {
}
useEffect(() => {
WorkerScriptStartStopEventEmitter.addSubscriber({
cb: rerender,
id: subscriberId,
});
return () => WorkerScriptStartStopEventEmitter.removeSubscriber(subscriberId);
return WorkerScriptStartStopEventEmitter.subscribe(rerender);
}, []);
const handleChangePage = (event: unknown, newPage: number) => {

@ -7,7 +7,7 @@ import * as React from "react";
import { EventEmitter } from "../../utils/EventEmitter";
type IProps = {
eventEmitterForReset?: EventEmitter;
eventEmitterForReset?: EventEmitter<[]>;
id?: string;
};
@ -29,6 +29,7 @@ const styleMarkup = {
};
export class ErrorBoundary extends React.Component<IProps, IState> {
unsubscribe: (() => void) | null = null;
constructor(props: IProps) {
super(props);
this.state = {
@ -50,16 +51,13 @@ export class ErrorBoundary extends React.Component<IProps, IState> {
};
if (this.hasEventEmitter()) {
(this.props.eventEmitterForReset as EventEmitter).addSubscriber({
cb: cb,
id: this.props.id as string,
});
this.unsubscribe = (this.props.eventEmitterForReset as EventEmitter<[]>).subscribe(cb);
}
}
componentWillUnmount(): void {
if (this.hasEventEmitter()) {
(this.props.eventEmitterForReset as EventEmitter).removeSubscriber(this.props.id as string);
if (this.unsubscribe !== null) {
this.unsubscribe();
}
}

@ -17,33 +17,33 @@ export interface ISubscriber {
id: string;
}
export class EventEmitter {
/**
* Map of Subscriber name -> Callback function
*/
subscribers: IMap<cbFn> = {};
function uuidv4(): string {
return "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g, function (c) {
var r = (Math.random() * 16) | 0,
v = c == "x" ? r : (r & 0x3) | 0x8;
return v.toString(16);
});
}
constructor(subs?: ISubscriber[]) {
if (Array.isArray(subs)) {
for (const s of subs) {
this.addSubscriber(s);
}
}
export class EventEmitter<T extends any[]> {
subscribers: { [key: string]: (...args: [...T]) => void | undefined } = {};
subscribe(s: (...args: [...T]) => void): () => void {
let uuid = uuidv4();
while (this.subscribers[uuid] !== undefined) uuid = uuidv4();
this.subscribers[uuid] = s;
return () => {
delete this.subscribers[uuid];
};
}
addSubscriber(s: ISubscriber): void {
this.subscribers[s.id] = s.cb;
}
emitEvent(...args: any[]): void {
emit(...args: [...T]): void {
for (const s in this.subscribers) {
const sub = this.subscribers[s];
if (sub === undefined) continue;
sub(args);
sub(...args);
}
}
removeSubscriber(id: string): void {
delete this.subscribers[id];
}
}