diff --git a/src/Hacknet/HacknetNode.ts b/src/Hacknet/HacknetNode.ts index 4982b606b..5cb950232 100644 --- a/src/Hacknet/HacknetNode.ts +++ b/src/Hacknet/HacknetNode.ts @@ -18,8 +18,18 @@ import { HacknetNodeConstants } from "./data/Constants"; import { dialogBoxCreate } from "../ui/React/DialogBox"; import { Generic_fromJSON, Generic_toJSON, Reviver } from "../utils/JSONReviver"; +import { ObjectValidator, minMax } from "../utils/Validator"; export class HacknetNode implements IHacknetNode { + + static validationData: ObjectValidator = { + cores: minMax(1, 1, HacknetNodeConstants.MaxCores), + level: minMax(1, 1, HacknetNodeConstants.MaxLevel), + ram: minMax(1, 1, HacknetNodeConstants.MaxRam), + onlineTimeSeconds: minMax(0, 0, Infinity), + totalMoneyGenerated: minMax(0, 0, Infinity) + } + // Node's number of cores cores = 1; diff --git a/src/utils/JSONReviver.ts b/src/utils/JSONReviver.ts index eeb832cec..5b7447d09 100644 --- a/src/utils/JSONReviver.ts +++ b/src/utils/JSONReviver.ts @@ -1,5 +1,7 @@ /* Generic Reviver, toJSON, and fromJSON functions used for saving and loading objects */ +import { validateObject } from "./Validator"; + interface IReviverValue { ctor: string; data: any; @@ -26,7 +28,11 @@ export function Reviver(key: string, value: IReviverValue | null): any { const ctor = Reviver.constructors[value.ctor]; if (typeof ctor === "function" && typeof ctor.fromJSON === "function") { - return ctor.fromJSON(value); + const obj = ctor.fromJSON(value); + if (ctor.validationData !== undefined) { + validateObject(obj, ctor.validationData); + } + return obj; } } return value; diff --git a/src/utils/Validator.ts b/src/utils/Validator.ts new file mode 100644 index 000000000..21daba59e --- /dev/null +++ b/src/utils/Validator.ts @@ -0,0 +1,78 @@ +export type ObjectValidator = { + [key in keyof T]?: ParameterValidator; +} + +interface ParameterValidatorObject { + default?: any; + min?: number; + max?: number; + func?: (obj: Type, validator: ObjectValidator, key: Key) => void; +} +type ParameterValidatorFunction = (obj: Type, key: Key) => void; +type ParameterValidator = ParameterValidatorObject | ParameterValidatorFunction + +export function validateObject, Key extends keyof Type>(obj: Type, validator: ObjectValidator): void { + for (const key of Object.keys(validator) as Key[]) { + const paramValidator = validator[key]; + if (paramValidator !== undefined) { + if (typeof paramValidator === 'function') { + paramValidator(obj, key); + } else { + if (paramValidator.func !== undefined) { + paramValidator.func(obj, validator, key); + } else { + if ((typeof obj[key]) !== (typeof paramValidator.default)) { + obj[key] = paramValidator.default + } + if (typeof obj[key] === 'number' && paramValidator.min !== undefined) { + if (obj[key] < paramValidator.min) obj[key] = paramValidator.min as Type[Key]; + } + if (typeof obj[key] === 'number' && paramValidator.max !== undefined) { + if (obj[key] > paramValidator.max) obj[key] = paramValidator.max as Type[Key]; + } + } + } + } + } +} + +export function minMax(def: number, min: number, max: number): (obj: Type, key: Key & keyof Type) => void { + return (obj, key) => { + if (typeof obj[key] !== 'number') { + obj[key] = def as unknown as Type[Key]; + return; + } + if ((obj[key] as unknown as number) < min) { + obj[key] = min as unknown as Type[Key]; + } + if ((obj[key] as unknown as number) > max) { + obj[key] = max as unknown as Type[Key]; + } + }; +} + +export function oneOf(def: Value, options: Value[]): (obj: Type, key: Key & keyof Type) => void { + return (obj, key) => { + if (typeof obj[key] !== typeof def) { + obj[key] = def as unknown as Type[Key]; + return; + } + if (!options.includes(obj[key] as unknown as Value)) { + obj[key] = def as unknown as Type[Key]; + } + }; +} + +export function subsetOf(options: Value[]): (obj: Type, key: Key & keyof Type) => void { + return (obj, key) => { + if (typeof obj[key] !== 'object' || !Array.isArray(obj[key])) { + obj[key] = [] as unknown as Type[Key]; + return; + } + const validValues: Value[] = []; + for (const value of obj[key] as unknown as Value[]) { + if (options.includes(value)) validValues.push(value); + } + obj[key] = validValues as unknown as Type[Key]; + }; +} \ No newline at end of file