diff --git a/config.default.json b/config.default.json index a555c18..d148055 100644 --- a/config.default.json +++ b/config.default.json @@ -1,5 +1,6 @@ { "aowowUrl": "https://wotlkdb.com", + "loadDbcs": true, "realms": [ { "name": "AzerothCore", diff --git a/package-lock.json b/package-lock.json index c0dadc4..ef9cd7a 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,18 +1,17 @@ { "name": "azerothcore-armory", - "version": "0.0.2", + "version": "0.1.0", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "azerothcore-armory", - "version": "0.0.2", + "version": "0.1.0", "license": "MIT", "dependencies": { "@supercharge/promise-pool": "^2.1.0", "camelcase": "^6.2.1", "cli-progress": "^3.9.1", - "csv-parser": "^3.0.0", "express": "^4.17.2", "express-handlebars": "^6.0.2", "glob": "^7.2.0", @@ -1226,20 +1225,6 @@ "node": ">=8" } }, - "node_modules/csv-parser": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/csv-parser/-/csv-parser-3.0.0.tgz", - "integrity": "sha512-s6OYSXAK3IdKqYO33y09jhypG/bSDHPuyCme/IdEHfWpLf/jKcpitVFyOC6UemgGk8v7Q5u2XE0vvwmanxhGlQ==", - "dependencies": { - "minimist": "^1.2.0" - }, - "bin": { - "csv-parser": "bin/csv-parser" - }, - "engines": { - "node": ">= 10" - } - }, "node_modules/date-fns": { "version": "2.28.0", "resolved": "https://registry.npmjs.org/date-fns/-/date-fns-2.28.0.tgz", @@ -5679,14 +5664,6 @@ "integrity": "sha512-v1plID3y9r/lPhviJ1wrXpLeyUIGAZ2SHNYTEapm7/8A9nLPoyvVp3RK/EPFqn5kEznyWgYZNsRtYYIWbuG8KA==", "dev": true }, - "csv-parser": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/csv-parser/-/csv-parser-3.0.0.tgz", - "integrity": "sha512-s6OYSXAK3IdKqYO33y09jhypG/bSDHPuyCme/IdEHfWpLf/jKcpitVFyOC6UemgGk8v7Q5u2XE0vvwmanxhGlQ==", - "requires": { - "minimist": "^1.2.0" - } - }, "date-fns": { "version": "2.28.0", "resolved": "https://registry.npmjs.org/date-fns/-/date-fns-2.28.0.tgz", diff --git a/package.json b/package.json index 8cf5587..333c5bb 100644 --- a/package.json +++ b/package.json @@ -1,14 +1,15 @@ { "name": "azerothcore-armory", - "version": "0.0.3", + "version": "0.1.0", "description": "", "scripts": { "build": "tsc -p tsconfig.json", "clean": "rimraf build/", - "start": "node build/armory/main.js", + "start": "node --expose-gc build/armory/main.js", "watch": "concurrently \"tsc -w --project .\" \"npm run nodemon\"", "nodemon": "nodemon -q -w build -w config.json build/armory/main.js", - "fetchdata": "node build/tools/fetchdata.js" + "fetchdata": "node build/tools/fetchdata.js", + "cleardata": "rimraf data/bone data/meta data/mo3 data/textures" }, "author": "https://github.com/r-o-b-o-t-o", "license": "MIT", @@ -29,7 +30,6 @@ "@supercharge/promise-pool": "^2.1.0", "camelcase": "^6.2.1", "cli-progress": "^3.9.1", - "csv-parser": "^3.0.0", "express": "^4.17.2", "express-handlebars": "^6.0.2", "glob": "^7.2.0", diff --git a/src/armory/Armory.ts b/src/armory/Armory.ts index 86608fc..b3c291e 100644 --- a/src/armory/Armory.ts +++ b/src/armory/Armory.ts @@ -5,21 +5,21 @@ import { Connection, createConnection } from "mysql2/promise"; import { engine as handlebarsEngine } from "express-handlebars"; import { Config } from "./Config"; -import { DbcReader } from "./data/DbcReader"; +import { DbcManager } from "./data/DbcReader"; import { CharacterCustomization } from "./data/CharacterCustomization"; import { IndexController } from "./controllers/IndexController"; import { CharacterController } from "./controllers/CharacterController"; export class Armory { public characterCustomization: CharacterCustomization; - public dbcReader: DbcReader; + public dbc: DbcManager; public config: Config; public worldDb: Connection; private charsDbs: { [key: string]: Connection }; public constructor() { - this.dbcReader = new DbcReader(); + this.dbc = new DbcManager(); this.characterCustomization = new CharacterCustomization(); this.charsDbs = {}; } @@ -31,7 +31,9 @@ export class Armory { console.log("Loading config..."); this.config = await Config.load(); console.log("Loading data files..."); - await this.dbcReader.loadAllFiles(); + if (this.config.loadDbcs) { + await this.dbc.loadAllFiles(); + } await this.characterCustomization.loadData(); console.log("Connecting to databases..."); @@ -68,6 +70,7 @@ export class Armory { await charsController.load(); app.get("/character/:realm/:name", charsController.character.bind(charsController)); + this.gc(); app.listen(listenPort, "0.0.0.0", () => { console.log(`Server is listening on 0.0.0.0:${listenPort}.`); }); @@ -76,4 +79,16 @@ export class Armory { public getCharactersDb(realm: string): Connection { return this.charsDbs[realm.toLowerCase()]; } + + public gc(): void { + if (this.config.loadDbcs) { + return; + } + + setTimeout(() => { + if (global.gc) { + global.gc(); + } + }, 500); + } } diff --git a/src/armory/Config.ts b/src/armory/Config.ts index b984cbf..b580a84 100644 --- a/src/armory/Config.ts +++ b/src/armory/Config.ts @@ -16,6 +16,7 @@ export interface IRealmConfig { export class Config { public aowowUrl: string; + public loadDbcs: boolean; public realms: IRealmConfig[]; public worldDatabase: IDatabaseConfig; diff --git a/src/armory/controllers/CharacterController.ts b/src/armory/controllers/CharacterController.ts index f805ae3..ec94848 100644 --- a/src/armory/controllers/CharacterController.ts +++ b/src/armory/controllers/CharacterController.ts @@ -47,8 +47,9 @@ export class CharacterController { public async load(): Promise { this.itemInventoryTypes = {}; - for (const item of this.armory.dbcReader.dbcItem) { - const retailItem = this.armory.dbcReader.dbcItemRetail.find(row => row.id === item.id); + const itemsRetail = await this.armory.dbc.itemRetail().toArray(); + for await (const item of this.armory.dbc.item()) { + const retailItem = itemsRetail.find(row => row.id === item.id); if (retailItem !== undefined) { this.itemInventoryTypes[item.id] = retailItem.inventoryType; } @@ -56,10 +57,10 @@ export class CharacterController { this.itemIcons = {}; const itemIconsByDisplayInfoId: { [key: number]: number } = {}; - for (const row of this.armory.dbcReader.dbcItemDisplayInfo) { + for await (const row of this.armory.dbc.itemDisplayInfo()) { itemIconsByDisplayInfoId[row.id] = row.inventoryIcon0; } - for (const item of this.armory.dbcReader.dbcItem) { + for await (const item of this.armory.dbc.item()) { const icon = itemIconsByDisplayInfoId[item.displayInfoId]; if (icon !== undefined) { this.itemIcons[item.id] = icon; @@ -67,12 +68,12 @@ export class CharacterController { } this.gemItems = {}; - for (const row of this.armory.dbcReader.dbcItem.filter(item => item.classId === ITEM_CLASS_GEM)) { + for await (const row of this.armory.dbc.item().filter(item => item.classId === ITEM_CLASS_GEM)) { this.gemItems[row.id] = true; } this.enchantSrcItems = {}; - for (const row of this.armory.dbcReader.dbcSpellItemEnchantment) { + for await (const row of this.armory.dbc.spellItemEnchantment()) { this.enchantSrcItems[row.id] = row.srcItemId; } @@ -100,7 +101,7 @@ export class CharacterController { return; } const equipmentData = await this.getEquipmentData(realm, charData.guid); - const customization = await this.getCustomizationOptions(charData); + const customization = this.getCustomizationOptions(charData); const equipment = equipmentData.map(row => { (row as any).icon = this.itemIcons[row.itemEntry]; (row as any).gems = this.getGemsFromEnchantments(row.enchantments); @@ -117,11 +118,13 @@ export class CharacterController { gender: charData.gender, level: charData.level, online: charData.online === 1, - characterModelItems: this.getModelViewerItems(equipmentData, charData.class), + characterModelItems: await this.getModelViewerItems(equipmentData, charData.class), customizationOptions: customization, equipment, }), }); + + this.armory.gc(); } private async getCharacterData(realm: string, charName: string): Promise { @@ -147,7 +150,7 @@ export class CharacterController { return rows as RowDataPacket[] as IEquipmentData[]; } - private getModelViewerItems(equipmentData: IEquipmentData[], charClass: number): number[][] { + private async getModelViewerItems(equipmentData: IEquipmentData[], charClass: number): Promise { if (charClass !== 3) { // Keep ranged weapon only if the character is a hunter equipmentData = equipmentData.filter(row => row.slot !== 17); @@ -160,11 +163,11 @@ export class CharacterController { const items: number[][] = []; for (const equipment of visibleEquipment) { - const modifiedAppearance = this.armory.dbcReader.dbcItemModifiedAppearance.find(row => row.itemId === equipment.itemEntry); + const modifiedAppearance = await this.armory.dbc.itemModifiedAppearance().find(row => row.itemId === equipment.itemEntry); if (modifiedAppearance === undefined) { continue; } - const appearance = this.armory.dbcReader.dbcItemAppearance.find(row => row.id === modifiedAppearance.itemAppearanceId); + const appearance = await this.armory.dbc.itemAppearance().find(row => row.id === modifiedAppearance.itemAppearanceId); if (appearance === undefined) { continue; } @@ -195,7 +198,7 @@ export class CharacterController { .filter(enchant => enchant in this.enchantSrcItems && !(this.enchantSrcItems[enchant] in this.gemItems) && enchant !== socketBonus); } - private async getCustomizationOptions(charData: ICharacterData): Promise { + private getCustomizationOptions(charData: ICharacterData): ICustomizationOption[] { const data = this.armory.characterCustomization.getCharacterCustomizationData(charData.race, charData.gender); const options = []; const setOptionByChoiceIndex = (optionName: string, choiceIndex: number) => { diff --git a/src/armory/data/DbcReader.ts b/src/armory/data/DbcReader.ts index 2845d92..03f9c33 100644 --- a/src/armory/data/DbcReader.ts +++ b/src/armory/data/DbcReader.ts @@ -1,50 +1,29 @@ import * as fs from "fs"; import * as path from "path"; -import * as csv from "csv-parser"; import * as camelCase from "camelcase"; export interface IItemDbc { id: number; classId: number; - subclassId: number; - soundOverrideSubclassId: number; - material: number; displayInfoId: number; inventoryType: number; - sheatheType: number; } export interface IItemRetailDbc { id: number; - classId: number; - subclassId: number; - material: number; inventoryType: number; - sheatheType: number; - soundOverrideSubclassId: number; - iconFileDataId: number; - itemGroupSoundsId: number; - contentTuningId: number; - modifiedCraftingReagentItemId: number; } export interface IItemAppearanceDbc { id: number; - displayType: number; itemDisplayInfoId: number; - defaultIconFileDataId: number; - uiOrder: number; - playerConditionId: number; } export interface IItemModifiedAppearanceDbc { id: number; itemId: number; - itemAppearanceModifierId: number; itemAppearanceId: number; - orderIndex: number; - transmogSourceTypeEnum: number; } export interface IItemDisplayInfoDbc { @@ -53,25 +32,13 @@ export interface IItemDisplayInfoDbc { } export interface IMountDbc { - nameLang: string; - sourceTextLang: string; - descriptionLang: string; id: number; - mountTypeId: number; - flags: number; - sourceTypeEnum: number; sourceSpellId: number; - playerConditionId: number; - mountFlyRideHeight: number; - uiModelSceneId: number; - mountSpecialRiderAnimKitId: number; - mountSpecialSpellVisualKitId: number; } export interface IMountXDisplayDbc { id: number; creatureDisplayInfoId: number; - playerConditionId: number; mountId: number; } @@ -85,45 +52,300 @@ export interface ISpellItemEnchantmentDbc { srcItemId: number; } -export class DbcReader { - public dbcItem: IItemDbc[]; - public dbcItemRetail: IItemRetailDbc[]; - public dbcItemAppearance: IItemAppearanceDbc[]; - public dbcItemModifiedAppearance: IItemModifiedAppearanceDbc[]; - public dbcItemDisplayInfo: IItemDisplayInfoDbc[]; - public dbcMount: IMountDbc[]; - public dbcMountDisplay: IMountXDisplayDbc[]; - public dbcSpell: ISpellDbc[]; - public dbcSpellItemEnchantment: ISpellItemEnchantmentDbc[]; +interface IAsyncGeneratorWithArrayMethods { + [Symbol.asyncIterator](): AsyncGenerator; + toArray(): Promise; + map(fn: (t: T) => M): IAsyncGeneratorWithArrayMethods; + filter(fn: (t: T) => boolean): IAsyncGeneratorWithArrayMethods; + find(fn: (t: T) => boolean): Promise; +} - public readDbcFile(file: string): Promise { - return new Promise((res, rej) => { - const rows = []; +class ArrayAsAsyncGenerator implements IAsyncGeneratorWithArrayMethods { + private data: T[]; - fs.createReadStream(file) - .pipe(csv({ - mapHeaders: ({ header, index }) => camelCase(header).replace(/[\[\]]/g, ""), - mapValues: ({ header, index, value }) => isNaN(value) ? value : Number(value), - })) - .on("error", rej) - .on("data", (data) => rows.push(data)) - .on("end", () => { - res(rows); - }); + public constructor(data: T[]) { + this.data = data; + } + + async toArray(): Promise { + return this.data; + } + + async *[Symbol.asyncIterator](): AsyncGenerator { + for (const x of this.data) { + yield x; + } + } + + public map(fn: (t: T) => M): ArrayAsAsyncGenerator { + return new ArrayAsAsyncGenerator(this.data.map(fn)); + } + + filter(fn: (t: T) => boolean): ArrayAsAsyncGenerator { + return new ArrayAsAsyncGenerator(this.data.filter(fn)); + } + + async find(fn: (t: T) => boolean): Promise { + return this.data.find(fn); + } +} + +class AsyncGenWrapper implements IAsyncGeneratorWithArrayMethods { + private gen: AsyncGenerator; + + public constructor(gen: AsyncGenerator) { + this.gen = gen; + } + + public static from(array: T[]): AsyncGenWrapper { + return new AsyncGenWrapper(async function* () { + for (const x of array) { + yield x; + } + }()); + } + + public async *[Symbol.asyncIterator](): AsyncGenerator { + for await (const x of this.gen) { + yield x; + } + } + + public async toArray(): Promise { + const values = []; + for await (const x of this) { + values.push(x); + } + return values; + } + + private wrap(g: (that: AsyncGenWrapper) => AsyncGenerator): AsyncGenWrapper { + return new AsyncGenWrapper(g(this)); + } + + public map(fn: (t: T) => M): AsyncGenWrapper { + return this.wrap(async function* (me) { + for await (const x of me) { + yield fn(x); + } }); } - public async loadAllFiles(): Promise { - const dir = path.join(process.cwd(), "data"); + public filter(fn: (t: T) => boolean): AsyncGenWrapper { + return this.wrap(async function* (me) { + for await (const x of me) { + if (fn(x)) { + yield x; + } + } + }); + } - this.dbcItem = await this.readDbcFile(path.join(dir, "Item_3.3.5_12340.csv")); - this.dbcItemRetail = await this.readDbcFile(path.join(dir, "Item_9.2.0_41462.csv")); - this.dbcItemAppearance = await this.readDbcFile(path.join(dir, "ItemAppearance_9.2.0_41462.csv")); - this.dbcItemModifiedAppearance = await this.readDbcFile(path.join(dir, "ItemModifiedAppearance_9.2.0_41462.csv")); - this.dbcItemDisplayInfo = await this.readDbcFile(path.join(dir, "ItemDisplayInfo_3.3.5_12340.csv")); - this.dbcMount = await this.readDbcFile(path.join(dir, "Mount_9.2.0_41462.csv")); - this.dbcMountDisplay = await this.readDbcFile(path.join(dir, "MountXDisplay_9.2.0_41462.csv")); - this.dbcSpell = await this.readDbcFile(path.join(dir, "Spell_3.3.5_12340.csv")); - this.dbcSpellItemEnchantment = await this.readDbcFile(path.join(dir, "SpellItemEnchantment_3.3.5_12340.csv")); + public async find(fn: (t: T) => boolean): Promise { + for await (const x of this) { + if (fn(x)) { + return x; + } + } + } +} + +class DbcReader { + private filePath: string; + private fields: string[]; + + public constructor(filePath: string, keepFields: string[] = []) { + this.filePath = filePath; + this.fields = keepFields; + } + + public async *read(): AsyncGenerator { + const stream = fs.createReadStream(this.filePath); + const itr = this.readLines(stream); + const headerLine = await itr.next(); + if (headerLine.done === true) { + return; + } + + const headerCols = this.parseCsvLine(headerLine.value) + .map(header => camelCase(header).replace(/[\[\]]/g, "")); + + for await (const line of itr) { + const cols = this.parseCsvLine(line) + .map(value => { + const parsed: number = parseInt(value, 10); + return isNaN(parsed) ? value : parsed; + }); + const row = {}; + headerCols.forEach((header, headerIdx) => { + if (this.fields.length === 0 || this.fields.includes(header)) { + row[header] = cols[headerIdx]; + } + }); + yield row as T; + } + } + + private async *readLines(stream: fs.ReadStream): AsyncGenerator { + let previous = ""; + + for await (const chunk of stream) { + previous += chunk; + let unixEolIndex: number; + let winEolIndex: number; + + while ((unixEolIndex = previous.indexOf("\n")) >= 0 || (winEolIndex = previous.indexOf("\r\n")) >= 0) { + const unix = unixEolIndex >= 0; + const line = previous.slice(0, (unix ? unixEolIndex : winEolIndex) - 1); + yield line; + previous = previous.slice(unix ? (unixEolIndex + 1) : (winEolIndex + 2)); + } + } + + if (previous.length > 0) { + yield previous; + } + } + + private parseCsvLine(line: string): string[] { + // Adapted from https://stackoverflow.com/a/14991797 + const arr = []; + let quote = false; // 'true' means we're inside a quoted field + + // Iterate over each character, keep track of current row and column (of the returned array) + for (let col = 0, c = 0; c < line.length; c++) { + let cc = line[c], nc = line[c + 1]; // Current character, next character + arr[col] = arr[col] || ""; // Create a new column (start with empty string) if necessary + + // If the current character is a quotation mark, and we're inside a + // quoted field, and the next character is also a quotation mark, + // add a quotation mark to the current column and skip the next character + if (cc == '"' && quote && nc == '"') { + arr[col] += cc; ++c; + continue; + } + + // If it's just one quotation mark, begin/end quoted field + if (cc == '"') { + quote = !quote; + continue; + } + + // If it's a comma and we're not in a quoted field, move on to the next column + if (cc == ',' && !quote) { + ++col; + continue; + } + + // If it's a newline (CRLF) and we're not in a quoted field, move on to the next row + if (cc == '\r' && nc == '\n' && !quote) { + break; + } + + // If it's a newline (LF or CR) and we're not in a quoted field, move on to the next row + if ((cc == '\n' && !quote) || + (cc == '\r' && !quote)) { + break; + } + + // Otherwise, append the current character to the current column + arr[col] += cc; + } + + return arr; + } +} + +const dir = path.join(process.cwd(), "data"); +export const DbcFiles = { + item: path.join(dir, "Item_3.3.5_12340.csv"), + itemRetail: path.join(dir, "Item_9.2.0_41462.csv"), + itemAppearance: path.join(dir, "ItemAppearance_9.2.0_41462.csv"), + itemModifiedAppearance: path.join(dir, "ItemModifiedAppearance_9.2.0_41462.csv"), + itemDisplayInfo: path.join(dir, "ItemDisplayInfo_3.3.5_12340.csv"), + mount: path.join(dir, "Mount_9.2.0_41462.csv"), + mountDisplay: path.join(dir, "MountXDisplay_9.2.0_41462.csv"), + spell: path.join(dir, "Spell_3.3.5_12340.csv"), + spellItemEnchantment: path.join(dir, "SpellItemEnchantment_3.3.5_12340.csv"), +}; + +const dbcFields = { + item: ["id", "classId", "displayInfoId", "inventoryType"], + itemRetail: ["id", "inventoryType"], + itemAppearance: ["id", "itemDisplayInfoId"], + itemModifiedAppearance: ["id", "itemId", "itemAppearanceId"], + itemDisplayInfo: ["id", "inventoryIcon0"], + mount: ["id", "sourceSpellId"], + mountDisplay: ["id", "creatureDisplayInfoId", "mountId"], + spell: ["id", "mechanic"], + spellItemEnchantment: ["id", "srcItemId"], +}; + +export class DbcManager { + private _item: IItemDbc[]; + private _itemRetail: IItemRetailDbc[]; + private _itemAppearance: IItemAppearanceDbc[]; + private _itemModifiedAppearance: IItemModifiedAppearanceDbc[]; + private _itemDisplayInfo: IItemDisplayInfoDbc[]; + private _mount: IMountDbc[]; + private _mountDisplay: IMountXDisplayDbc[]; + private _spell: ISpellDbc[]; + private _spellItemEnchantment: ISpellItemEnchantmentDbc[]; + + public async loadAllFiles(): Promise { + this._item = await this.read(DbcFiles.item, dbcFields.item).toArray(); + this._itemRetail = await this.read(DbcFiles.itemRetail, dbcFields.itemRetail).toArray(); + this._itemAppearance = await this.read(DbcFiles.itemAppearance, dbcFields.itemAppearance).toArray(); + this._itemModifiedAppearance = await this.read(DbcFiles.itemModifiedAppearance, dbcFields.itemModifiedAppearance).toArray(); + this._itemDisplayInfo = await this.read(DbcFiles.itemDisplayInfo, dbcFields.itemDisplayInfo).toArray(); + this._mount = await this.read(DbcFiles.mount, dbcFields.mount).toArray(); + this._mountDisplay = await this.read(DbcFiles.mountDisplay, dbcFields.mountDisplay).toArray(); + this._spell = await this.read(DbcFiles.spell, dbcFields.spell).toArray(); + this._spellItemEnchantment = await this.read(DbcFiles.spellItemEnchantment, dbcFields.spellItemEnchantment).toArray(); + } + + public item() { + return this.getLoadedDataOrRead(DbcFiles.item, this._item, dbcFields.item); + } + + public itemRetail() { + return this.getLoadedDataOrRead(DbcFiles.itemRetail, this._itemRetail, dbcFields.itemRetail); + } + + public itemAppearance() { + return this.getLoadedDataOrRead(DbcFiles.itemAppearance, this._itemAppearance, dbcFields.itemAppearance); + } + + public itemModifiedAppearance() { + return this.getLoadedDataOrRead(DbcFiles.itemModifiedAppearance, this._itemModifiedAppearance, dbcFields.itemModifiedAppearance); + } + + public itemDisplayInfo() { + return this.getLoadedDataOrRead(DbcFiles.itemDisplayInfo, this._itemDisplayInfo, dbcFields.itemDisplayInfo); + } + + public mount() { + return this.getLoadedDataOrRead(DbcFiles.mount, this._mount, dbcFields.mount); + } + + public mountDisplay() { + return this.getLoadedDataOrRead(DbcFiles.mountDisplay, this._mountDisplay, dbcFields.mountDisplay); + } + + public spell() { + return this.getLoadedDataOrRead(DbcFiles.spell, this._spell, dbcFields.spell); + } + + public spellItemEnchantment() { + return this.getLoadedDataOrRead(DbcFiles.spellItemEnchantment, this._spellItemEnchantment, dbcFields.spellItemEnchantment); + } + + private read(file: string, keepFields: string[] = []): AsyncGenWrapper { + const reader = new DbcReader(file, keepFields); + return new AsyncGenWrapper(reader.read()); + } + + private getLoadedDataOrRead(path: string, data: T[], keepFields: string[] = []): IAsyncGeneratorWithArrayMethods { + return data === undefined ? this.read(path, keepFields) : new ArrayAsAsyncGenerator(data); } } diff --git a/src/tools/fetchdata.ts b/src/tools/fetchdata.ts index b5f54f6..931e3b4 100644 --- a/src/tools/fetchdata.ts +++ b/src/tools/fetchdata.ts @@ -11,7 +11,7 @@ import * as prettyMs from "pretty-ms"; import * as cliProgress from "cli-progress"; import promisepool = require("@supercharge/promise-pool"); -import { DbcReader, IItemAppearanceDbc, IItemModifiedAppearanceDbc, IMountDbc, IMountXDisplayDbc } from "../armory/data/DbcReader"; +import { DbcManager, IItemAppearanceDbc, IItemModifiedAppearanceDbc, IMountDbc, IMountXDisplayDbc } from "../armory/data/DbcReader"; require("source-map-support").install(); @@ -73,7 +73,7 @@ class HttpRequestError extends Error { } } -let dbcReader: DbcReader; +let dbc: DbcManager; let dbcItemAppearanceById: { [key: number]: IItemAppearanceDbc }; let dbcItemModifiedAppearanceByItemId: { [key: number]: IItemModifiedAppearanceDbc }; let dbcMountBySourceSpellId: { [key: number]: IMountDbc }; @@ -215,7 +215,7 @@ async function downloadRaces(): Promise { } async function downloadArmors(): Promise { - const rows = dbcReader.dbcItem.filter((row) => row.classId === classIdArmor); + const rows = await dbc.item().filter((row) => row.classId === classIdArmor).toArray(); const progress = new Progress("Downloading armor data...", rows.length); @@ -247,7 +247,7 @@ async function downloadArmors(): Promise { } async function downloadWeapons(): Promise { - const rows = dbcReader.dbcItem.filter((row) => row.classId === classIdWeapon); + const rows = await dbc.item().filter((row) => row.classId === classIdWeapon).toArray(); const progress = new Progress("Downloading weapon data...", rows.length); @@ -280,32 +280,32 @@ async function downloadWeapons(): Promise { async function readDbcData(): Promise { console.log("Reading DBC data..."); - dbcReader = new DbcReader(); - await dbcReader.loadAllFiles(); + dbc = new DbcManager(); + await dbc.loadAllFiles(); dbcItemAppearanceById = {}; - for (const row of dbcReader.dbcItemAppearance) { + for await (const row of dbc.itemAppearance()) { dbcItemAppearanceById[row.id] = row; } dbcItemModifiedAppearanceByItemId = {}; - for (const row of dbcReader.dbcItemModifiedAppearance) { + for await (const row of dbc.itemModifiedAppearance()) { dbcItemModifiedAppearanceByItemId[row.itemId] = row; } dbcMountBySourceSpellId = {}; - for (const row of dbcReader.dbcMount) { + for await (const row of dbc.mount()) { dbcMountBySourceSpellId[row.sourceSpellId] = row; } dbcMountDisplayByMountId = {}; - for (const row of dbcReader.dbcMountDisplay) { + for await (const row of dbc.mountDisplay()) { dbcMountDisplayByMountId[row.mountId] = row; } } async function downloadMounts(): Promise { - const mountSpells = dbcReader.dbcSpell.filter(spell => spell.mechanic === spellMechanicMounted); + const mountSpells = await dbc.spell().filter(spell => spell.mechanic === spellMechanicMounted).toArray(); const progress = new Progress("Downloading mount data...", mountSpells.length); await promisepool.PromisePool