From 419ac34175f46cfa11881758de4afd4161a35934 Mon Sep 17 00:00:00 2001 From: larry-the-table-guy <180724454+larry-the-table-guy@users.noreply.github.com> Date: Fri, 4 Oct 2024 20:29:53 -0400 Subject: [PATCH] DexTypes: CoW init for TypeInfo When possible, DexTypes will re-use objects from the parent. To achieve this, inheritance is integrated with construction. dataCache.Typechart reflects the data from the file. Because dataCache.Typechart no longer has the same information, it is deleted after DexTypes is constructed. In short, it became a temporary intermediate representation. This lowers the long-term memory usage. --- sim/dex-data.ts | 58 +++++++++++++++++++++++++++++++++++++++++++++---- sim/dex.ts | 11 ++++++---- 2 files changed, 61 insertions(+), 8 deletions(-) diff --git a/sim/dex-data.ts b/sim/dex-data.ts index 033a8b28a720..4d679c356918 100644 --- a/sim/dex-data.ts +++ b/sim/dex-data.ts @@ -6,6 +6,9 @@ */ import {Utils} from '../lib'; +/** Unfortunately we do for..in too much to want to deal with the casts */ +export interface DexTable {[id: string]: T} + /** * Converts anything to an ID. An ID must have only lowercase alphanumeric * characters. @@ -210,6 +213,7 @@ export interface TypeData { HPivs?: SparseStatsTable; isNonstandard?: Nonstandard | null; } +const TYPE_DATA_KEYS: readonly (keyof TypeData)[] = Object.freeze(['damageTaken', 'HPivs', 'HPdvs', 'isNonstandard']); export type ModdedTypeData = TypeData | Partial> & {inherit: true}; export interface TypeDataTable {[typeid: IDEntry]: TypeData} @@ -285,15 +289,61 @@ export class DexTypes { allCache: readonly TypeInfo[]; namesCache: readonly string[]; - constructor(dex: ModdedDex) { + constructor(dex: ModdedDex, patches: ModdedTypeDataTable, parent?: DexTypes) { this.dex = dex; + + let patchesEmpty = true; + for (const k in patches) { // eslint-disable-line @typescript-eslint/no-unused-vars + patchesEmpty = false; + break; + } + if (parent && patchesEmpty) { + this.allCache = parent.allCache; + this.namesCache = parent.namesCache; + this.typeCache = parent.typeCache; + return; + } const allCache = []; const namesCache = []; - for (const _id in this.dex.data.TypeChart) { + if (parent) { + for (const parentType of parent.all()) { + const id = parentType.id; + const patchEntry = patches[id]; + // null means don't inherit + if (patchEntry === null) { + delete patches[id]; + continue; + } + let type; + // if patchEntry present, construct + // else re-use parent's object + if (patchEntry) { + // if inherit, copy fields from parent into child + if ('inherit' in patchEntry && patchEntry.inherit === true) { + delete (patchEntry as any).inherit; + for (const field of TYPE_DATA_KEYS) { + if (field in patchEntry) continue; + // nonsense to appease tsc. + (patchEntry as any)[field] = parentType[field]; + } + // patchEntry is now a complete TypeData + } + type = new TypeInfo({name: parentType.name, id, ...patchEntry}); + type = dex.deepFreeze(type); + delete patches[id]; + } else { + type = parentType; + } + this.typeCache.set(id, type); + allCache.push(type); + if (!type.isNonstandard) namesCache.push(type.name); + } + } + for (const _id in patches) { const id = _id as ID; const typeName = id.charAt(0).toUpperCase() + id.substr(1); - const type = new TypeInfo({name: typeName, id, ...this.dex.data.TypeChart[id]}); - this.typeCache.set(id, this.dex.deepFreeze(type)); + const type = new TypeInfo({name: typeName, id, ...patches[id]}); + this.typeCache.set(id, dex.deepFreeze(type)); allCache.push(type); if (!type.isNonstandard) namesCache.push(type.name); } diff --git a/sim/dex.ts b/sim/dex.ts index 37f71a71d839..2c07ea03b88c 100644 --- a/sim/dex.ts +++ b/sim/dex.ts @@ -31,6 +31,8 @@ import * as fs from 'fs'; import * as path from 'path'; import * as Data from './dex-data'; +import {DexTable} from './dex-data'; +export {DexTable} from './dex-data'; import {Condition, DexConditions} from './dex-conditions'; import {DataMove, DexMoves} from './dex-moves'; import {Item, DexItems} from './dex-items'; @@ -78,8 +80,6 @@ const DATA_FILES = { TypeChart: 'typechart', }; -/** Unfortunately we do for..in too much to want to deal with the casts */ -export interface DexTable {[id: string]: T} export interface AliasesTable {[id: IDEntry]: string} interface DexTableData { @@ -95,7 +95,7 @@ interface DexTableData { PokemonGoData: DexTable; Scripts: DexTable; Conditions: DexTable; - TypeChart: DexTable; + TypeChart: import('./dex-data').ModdedTypeDataTable; } interface TextTableData { Abilities: DexTable; @@ -185,6 +185,7 @@ export class ModdedDex { if (parentDex) { dataCache['Aliases'] = parentDex.data['Aliases']; for (const dataType of DATA_TYPES) { + if (dataType === 'TypeChart') continue; // ported to CoW const parentTypedData: DexTable = parentDex.data[dataType]; const childTypedData: DexTable = dataCache[dataType] || (dataCache[dataType] = {}); for (const entryId in parentTypedData) { @@ -225,7 +226,8 @@ export class ModdedDex { this.species = new DexSpecies(this); this.conditions = new DexConditions(this); this.natures = new Data.DexNatures(this); - this.types = new Data.DexTypes(this); + this.types = new Data.DexTypes(this, dataCache.TypeChart, parentDex?.types); + delete dataCache.TypeChart; this.stats = new Data.DexStats(this); } @@ -258,6 +260,7 @@ export class ModdedDex { } modData(dataType: DataType, id: string) { + if (dataType === 'TypeChart') throw new Error("todo: modify modData"); if (this.isBase) return this.data[dataType][id]; if (this.data[dataType][id] !== this.mod(this.parentMod).data[dataType][id]) return this.data[dataType][id]; return (this.data[dataType][id] = Utils.deepClone(this.data[dataType][id]));