2022-03-20 09:32:29 +01:00
import { getRamCost } from "./RamCostGenerator" ;
import type { IPort } from "../NetscriptPort" ;
import type { BaseServer } from "../Server/BaseServer" ;
import type { WorkerScript } from "./WorkerScript" ;
import { makeRuntimeRejectMsg } from "../NetscriptEvaluator" ;
import { Player } from "../Player" ;
2022-04-12 08:51:10 +02:00
import { CityName } from "src/Locations/data/CityNames" ;
2022-05-07 23:43:45 +02:00
import { Settings } from "../Settings/Settings" ;
import { CONSTANTS } from "../Constants" ;
2022-03-20 09:32:29 +01:00
2022-03-30 01:11:13 +02:00
type ExternalFunction = ( . . . args : any [ ] ) = > any ;
type ExternalAPI = {
[ string : string ] : ExternalAPI | ExternalFunction ;
2022-04-07 02:00:54 +02:00
} ;
2022-03-30 01:11:13 +02:00
2022-04-09 07:15:45 +02:00
type InternalFunction < F extends ( ...args : unknown [ ] ) = > unknown > = ( ctx : NetscriptContext ) = > F ;
2022-03-30 01:11:13 +02:00
export type InternalAPI < API > = {
2022-04-07 02:00:54 +02:00
[ Property in keyof API ] : API [ Property ] extends ExternalFunction
? InternalFunction < API [ Property ] >
: API [ Property ] extends ExternalAPI
? InternalAPI < API [ Property ] >
: never ;
} ;
2022-03-30 01:11:13 +02:00
type WrappedNetscriptFunction = ( . . . args : unknown [ ] ) = > unknown ;
type WrappedNetscriptAPI = {
2022-04-09 07:15:45 +02:00
readonly [ string : string ] : WrappedNetscriptAPI | WrappedNetscriptFunction ;
2022-04-07 02:00:54 +02:00
} ;
2022-03-20 09:32:29 +01:00
export type NetscriptContext = {
makeRuntimeErrorMsg : ( message : string ) = > string ;
log : ( message : ( ) = > string ) = > void ;
2022-03-29 23:57:17 +02:00
workerScript : WorkerScript ;
function : string ;
helper : WrappedNetscriptHelpers ;
2022-03-20 09:32:29 +01:00
} ;
type NetscriptHelpers = {
updateDynamicRam : ( fnName : string , ramCost : number ) = > void ;
makeRuntimeErrorMsg : ( caller : string , msg : string ) = > string ;
string : ( funcName : string , argName : string , v : unknown ) = > string ;
number : ( funcName : string , argName : string , v : unknown ) = > number ;
2022-04-12 08:51:10 +02:00
city : ( funcName : string , argName : string , v : unknown ) = > CityName ;
2022-03-20 09:32:29 +01:00
boolean : ( v : unknown ) = > boolean ;
getServer : ( hostname : string , callingFnName : string ) = > BaseServer ;
checkSingularityAccess : ( func : string ) = > void ;
hack : ( hostname : any , manual : any , { threads : requestedThreads , stock } ? : any ) = > Promise < number > ;
getValidPort : ( funcName : string , port : any ) = > IPort ;
2022-04-07 02:00:54 +02:00
} ;
2022-03-20 09:32:29 +01:00
2022-03-29 23:57:17 +02:00
type WrappedNetscriptHelpers = {
makeRuntimeErrorMsg : ( msg : string ) = > string ;
string : ( argName : string , v : unknown ) = > string ;
number : ( argName : string , v : unknown ) = > number ;
2022-04-12 08:51:10 +02:00
city : ( argName : string , v : unknown ) = > CityName ;
2022-03-29 23:57:17 +02:00
boolean : ( v : unknown ) = > boolean ;
2022-04-07 02:00:54 +02:00
getServer : ( hostname : string ) = > BaseServer ;
2022-03-29 23:57:17 +02:00
checkSingularityAccess : ( ) = > void ;
hack : ( hostname : any , manual : any , { threads : requestedThreads , stock } ? : any ) = > Promise < number > ;
getValidPort : ( port : any ) = > IPort ;
2022-04-07 02:00:54 +02:00
} ;
2022-03-29 23:57:17 +02:00
2022-04-08 01:57:16 +02:00
function wrapFunction (
2022-04-07 02:00:54 +02:00
helpers : NetscriptHelpers ,
wrappedAPI : any ,
workerScript : WorkerScript ,
2022-04-08 01:57:16 +02:00
func : ( _ctx : NetscriptContext ) = > ( . . . args : unknown [ ] ) = > unknown ,
2022-04-07 02:00:54 +02:00
. . . tree : string [ ]
) : void {
2022-04-08 02:02:59 +02:00
const functionPath = tree . join ( "." ) ;
2022-03-20 09:32:29 +01:00
const functionName = tree . pop ( ) ;
2022-04-07 02:00:54 +02:00
if ( typeof functionName !== "string" ) {
throw makeRuntimeRejectMsg ( workerScript , "Failure occured while wrapping netscript api" ) ;
2022-03-20 09:32:29 +01:00
}
const ctx = {
2022-04-07 02:00:54 +02:00
makeRuntimeErrorMsg : ( message : string ) = > {
2022-04-08 01:57:16 +02:00
return helpers . makeRuntimeErrorMsg ( functionPath , message ) ;
2022-04-07 02:00:54 +02:00
} ,
log : ( message : ( ) = > string ) = > {
2022-04-08 01:57:16 +02:00
workerScript . log ( functionPath , message ) ;
2022-04-07 02:00:54 +02:00
} ,
2022-03-29 23:57:17 +02:00
workerScript ,
function : functionName ,
helper : {
2022-04-08 01:57:16 +02:00
makeRuntimeErrorMsg : ( msg : string ) = > helpers . makeRuntimeErrorMsg ( functionPath , msg ) ,
string : ( argName : string , v : unknown ) = > helpers . string ( functionPath , argName , v ) ,
number : ( argName : string , v : unknown ) = > helpers . number ( functionPath , argName , v ) ,
2022-04-12 08:51:10 +02:00
city : ( argName : string , v : unknown ) = > helpers . city ( functionPath , argName , v ) ,
2022-03-29 23:57:17 +02:00
boolean : helpers . boolean ,
2022-04-08 01:57:16 +02:00
getServer : ( hostname : string ) = > helpers . getServer ( hostname , functionPath ) ,
2022-03-29 23:57:17 +02:00
checkSingularityAccess : ( ) = > helpers . checkSingularityAccess ( functionName ) ,
hack : helpers.hack ,
2022-04-08 01:57:16 +02:00
getValidPort : ( port : any ) = > helpers . getValidPort ( functionPath , port ) ,
2022-04-07 02:00:54 +02:00
} ,
2022-03-20 09:32:29 +01:00
} ;
2022-05-07 23:43:45 +02:00
const safetyEnabled = Settings . InfinityLoopSafety ;
2022-04-08 01:57:16 +02:00
function wrappedFunction ( . . . args : unknown [ ] ) : unknown {
2022-03-29 23:57:17 +02:00
helpers . updateDynamicRam ( ctx . function , getRamCost ( Player , . . . tree , ctx . function ) ) ;
2022-05-07 23:43:45 +02:00
if ( safetyEnabled ) workerScript . infiniteLoopSafetyCounter ++ ;
if ( workerScript . infiniteLoopSafetyCounter > CONSTANTS . InfiniteLoopLimit )
throw new Error (
2022-05-24 14:12:49 +02:00
` Infinite loop without sleep detected. ${ CONSTANTS . InfiniteLoopLimit } ns functions were called without 'sleep'. This will cause your UI to hang. Are you using 'asleep' by mistake? ` ,
2022-05-07 23:43:45 +02:00
) ;
2022-04-08 01:57:16 +02:00
return func ( ctx ) ( . . . args ) ;
2022-03-20 09:32:29 +01:00
}
const parent = getNestedProperty ( wrappedAPI , . . . tree ) ;
Object . defineProperty ( parent , functionName , {
value : wrappedFunction ,
2022-04-07 02:00:54 +02:00
writable : true ,
2022-04-08 02:02:59 +02:00
enumerable : true ,
2022-03-20 09:32:29 +01:00
} ) ;
}
2022-04-07 02:00:54 +02:00
export function wrapAPI (
helpers : NetscriptHelpers ,
wrappedAPI : ExternalAPI ,
workerScript : WorkerScript ,
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types
namespace : any ,
. . . tree : string [ ]
) : WrappedNetscriptAPI {
if ( typeof namespace !== "object" ) throw new Error ( "Invalid namespace?" ) ;
2022-03-20 09:32:29 +01:00
for ( const property of Object . getOwnPropertyNames ( namespace ) ) {
switch ( typeof namespace [ property ] ) {
2022-04-07 02:00:54 +02:00
case "function" : {
2022-03-20 09:32:29 +01:00
wrapFunction ( helpers , wrappedAPI , workerScript , namespace [ property ] , . . . tree , property ) ;
break ;
}
2022-04-07 02:00:54 +02:00
case "object" : {
2022-03-20 09:32:29 +01:00
wrapAPI ( helpers , wrappedAPI , workerScript , namespace [ property ] , . . . tree , property ) ;
break ;
}
default : {
setNestedProperty ( wrappedAPI , namespace [ property ] , . . . tree , property ) ;
}
}
}
return wrappedAPI ;
}
function setNestedProperty ( root : any , value : any , . . . tree : string [ ] ) : any {
let target = root ;
const key = tree . pop ( ) ;
2022-04-07 02:00:54 +02:00
if ( typeof key !== "string" ) {
throw new Error ( "Failure occured while wrapping netscript api (setNestedProperty)" ) ;
2022-03-20 09:32:29 +01:00
}
for ( const branch of tree ) {
if ( target [ branch ] === undefined ) {
target [ branch ] = { } ;
}
target = target [ branch ] ;
}
target [ key ] = value ;
}
function getNestedProperty ( root : any , . . . tree : string [ ] ) : any {
let target = root ;
for ( const branch of tree ) {
if ( target [ branch ] === undefined ) {
target [ branch ] = { } ;
}
target = target [ branch ] ;
}
return target ;
}