chore: add and apply eslint
This commit is contained in:
parent
7cd2748d33
commit
bb6428955f
17 changed files with 1455 additions and 132 deletions
33
.eslintrc.json
Normal file
33
.eslintrc.json
Normal file
|
|
@ -0,0 +1,33 @@
|
|||
{
|
||||
"env": {
|
||||
"es2021": true
|
||||
},
|
||||
"extends": ["eslint:recommended", "plugin:@typescript-eslint/recommended"],
|
||||
"parser": "@typescript-eslint/parser",
|
||||
"plugins": ["@typescript-eslint"],
|
||||
"rules": {
|
||||
"semi": ["error", "always"],
|
||||
"@typescript-eslint/no-unused-vars": [
|
||||
"warn",
|
||||
{
|
||||
"args": "none"
|
||||
}
|
||||
],
|
||||
"no-duplicate-imports": "warn",
|
||||
"no-promise-executor-return": "warn",
|
||||
"quote-props": ["warn", "as-needed"],
|
||||
"indent": "off",
|
||||
"@typescript-eslint/indent": [
|
||||
"error",
|
||||
"tab",
|
||||
{
|
||||
"ignoredNodes": [
|
||||
"FunctionExpression > .params[decorators.length > 0]",
|
||||
"FunctionExpression > .params > :matches(Decorator, :not(:first-child))",
|
||||
"ClassBody.body > PropertyDefinition[decorators.length > 0] > .key"
|
||||
],
|
||||
"SwitchCase": 1
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
3
.gitignore
vendored
3
.gitignore
vendored
|
|
@ -15,3 +15,6 @@ config.json
|
|||
|
||||
# Log files
|
||||
*.log
|
||||
|
||||
# IDEs
|
||||
.vscode/
|
||||
|
|
|
|||
1246
package-lock.json
generated
1246
package-lock.json
generated
File diff suppressed because it is too large
Load diff
|
|
@ -8,6 +8,7 @@
|
|||
"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",
|
||||
"lint": "eslint src/",
|
||||
"prettier": "prettier . -w",
|
||||
"fetchdata": "node build/tools/fetchdata.js",
|
||||
"cleardata": "rimraf data/bone data/meta data/mo3 data/textures"
|
||||
|
|
@ -26,6 +27,7 @@
|
|||
"@types/pako": "^1.0.3",
|
||||
"@types/uuid": "^8.3.4",
|
||||
"concurrently": "^7.0.0",
|
||||
"eslint": "^8.15.0",
|
||||
"nodemon": "^2.0.15",
|
||||
"prettier": "^2.6.0",
|
||||
"rimraf": "^3.0.2",
|
||||
|
|
|
|||
|
|
@ -98,6 +98,7 @@ export class Armory {
|
|||
layoutsDir: path.join(process.cwd(), "static"),
|
||||
defaultLayout: "layout.hbs",
|
||||
helpers: {
|
||||
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
||||
...require("handlebars-helpers")(),
|
||||
},
|
||||
}),
|
||||
|
|
@ -131,14 +132,14 @@ export class Armory {
|
|||
}),
|
||||
);
|
||||
|
||||
app.use("/js", express.static(`static/js`));
|
||||
app.use("/css", express.static(`static/css`));
|
||||
app.use("/img", express.static(`static/img`));
|
||||
app.use("/data/mo3", express.static(`data/mo3`));
|
||||
app.use("/data/meta", express.static(`data/meta`));
|
||||
app.use("/data/bone", express.static(`data/bone`));
|
||||
app.use("/data/textures", express.static(`data/textures`));
|
||||
app.use("/data/background.png", express.static(`data/modelviewer-background.png`));
|
||||
app.use("/js", express.static("static/js"));
|
||||
app.use("/css", express.static("static/css"));
|
||||
app.use("/img", express.static("static/img"));
|
||||
app.use("/data/mo3", express.static("data/mo3"));
|
||||
app.use("/data/meta", express.static("data/meta"));
|
||||
app.use("/data/bone", express.static("data/bone"));
|
||||
app.use("/data/textures", express.static("data/textures"));
|
||||
app.use("/data/background.png", express.static("data/modelviewer-background.png"));
|
||||
|
||||
const indexController = new IndexController(this);
|
||||
app.get("/", this.wrapRoute(indexController.index.bind(indexController)));
|
||||
|
|
@ -208,7 +209,7 @@ export class Armory {
|
|||
const db = this.getCharactersDb(realm);
|
||||
|
||||
if (!(realm in this.charsetCache)) {
|
||||
const [rows, fields] = await db.query({
|
||||
const [rows] = await db.query({
|
||||
sql: `
|
||||
SELECT CCSA.character_set_name AS charset FROM information_schema.\`TABLES\` T,
|
||||
information_schema.\`COLLATION_CHARACTER_SET_APPLICABILITY\` CCSA
|
||||
|
|
@ -235,7 +236,7 @@ export class Armory {
|
|||
}, 500);
|
||||
}
|
||||
|
||||
private wrapRoute(fn: (req: express.Request, res: express.Response, next: express.NextFunction) => Promise<any>) {
|
||||
private wrapRoute(fn: (req: express.Request, res: express.Response, next: express.NextFunction) => Promise<void>) {
|
||||
// Adds error handling for promise-based controller methods
|
||||
return async (req: express.Request, res: express.Response, next: express.NextFunction) => {
|
||||
try {
|
||||
|
|
|
|||
|
|
@ -36,8 +36,8 @@ export class Config {
|
|||
public worldDatabase: IDatabaseConfig;
|
||||
public dbQueryTimeout: number;
|
||||
|
||||
private static envPrefix: string = "ACORE_ARMORY";
|
||||
private static checkedMissingField: boolean = false;
|
||||
private static envPrefix = "ACORE_ARMORY";
|
||||
private static checkedMissingField = false;
|
||||
|
||||
public static async load(logger: winston.Logger): Promise<Config> {
|
||||
try {
|
||||
|
|
@ -71,13 +71,13 @@ export class Config {
|
|||
return config as Config;
|
||||
}
|
||||
|
||||
private static loadObjFromEnv(logger: winston.Logger, obj: object, model: object, parentName: string = "") {
|
||||
private static loadObjFromEnv(logger: winston.Logger, obj: object, model: object, parentName = "") {
|
||||
if (parentName !== "") {
|
||||
parentName += ".";
|
||||
}
|
||||
|
||||
for (const field in model) {
|
||||
if (!model.hasOwnProperty(field)) {
|
||||
if (!Object.hasOwnProperty.call(model, field)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
|
|
@ -86,9 +86,9 @@ export class Config {
|
|||
} else if (typeof model[field] === "object") {
|
||||
obj[field] = {};
|
||||
Config.loadObjFromEnv(logger, obj[field], model[field], parentName + field);
|
||||
} else if (!obj.hasOwnProperty(field)) {
|
||||
} else if (!Object.hasOwnProperty.call(obj, field)) {
|
||||
const key = Config.getEnvKey(parentName + field);
|
||||
if (process.env.hasOwnProperty(key)) {
|
||||
if (Object.hasOwnProperty.call(process.env, key)) {
|
||||
obj[field] = Config.parseEnvValue(process.env[key], model[field]);
|
||||
} else if (!Config.checkedMissingField) {
|
||||
logger.warn(`Config field ${key} is missing from .env!`);
|
||||
|
|
@ -97,14 +97,14 @@ export class Config {
|
|||
}
|
||||
}
|
||||
|
||||
private static loadArrayFromEnv(logger: winston.Logger, model: any, parentName: string = ""): any[] {
|
||||
private static loadArrayFromEnv(logger: winston.Logger, model, parentName = ""): unknown[] {
|
||||
if (parentName !== "") {
|
||||
parentName += ".";
|
||||
}
|
||||
|
||||
const arr = [];
|
||||
let i = 0;
|
||||
while (true) {
|
||||
for (;;) {
|
||||
const key = Config.getEnvKey(parentName + i);
|
||||
const found = Object.keys(process.env).some((k) => k.startsWith(key));
|
||||
if (!found) {
|
||||
|
|
@ -119,7 +119,7 @@ export class Config {
|
|||
if (Object.keys(obj).length) {
|
||||
arr.push(obj);
|
||||
}
|
||||
} else if (process.env.hasOwnProperty(key)) {
|
||||
} else if (Object.hasOwnProperty.call(process.env, key)) {
|
||||
arr.push(Config.parseEnvValue(process.env[key], model));
|
||||
} else {
|
||||
break;
|
||||
|
|
@ -142,7 +142,7 @@ export class Config {
|
|||
);
|
||||
}
|
||||
|
||||
private static parseEnvValue(value: string, model: any): any {
|
||||
private static parseEnvValue(value: string, model: boolean | number | string): boolean | number | string {
|
||||
const type = typeof model;
|
||||
const lower = value.toLowerCase();
|
||||
if (type === "boolean") {
|
||||
|
|
@ -154,7 +154,7 @@ export class Config {
|
|||
return value;
|
||||
}
|
||||
|
||||
private static checkAllMissingFields(logger: winston.Logger, obj: object, model: object, parentName: string = "") {
|
||||
private static checkAllMissingFields(logger: winston.Logger, obj: object, model: object, parentName = "") {
|
||||
const missing = Config.hasMissingFields(obj, model);
|
||||
if (parentName !== "") {
|
||||
parentName += ".";
|
||||
|
|
@ -163,7 +163,7 @@ export class Config {
|
|||
logger.warn(`Field ${parentName}${field} is missing from config.json!`);
|
||||
}
|
||||
for (const key of Object.keys(model)) {
|
||||
if (typeof model[key] === "object" && obj.hasOwnProperty(key)) {
|
||||
if (typeof model[key] === "object" && Object.hasOwnProperty.call(obj, key)) {
|
||||
Config.checkAllMissingFields(logger, obj[key], model[key], parentName + key);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,17 +1,17 @@
|
|||
import { Pool } from "mysql2/promise";
|
||||
import { Pool, RowDataPacket } from "mysql2/promise";
|
||||
import { Query } from "express-serve-static-core";
|
||||
|
||||
export interface IResult {
|
||||
recordsTotal: number;
|
||||
recordsFiltered: number;
|
||||
draw: number;
|
||||
data: any[][];
|
||||
data: unknown[][];
|
||||
}
|
||||
|
||||
export interface IColumnSettings {
|
||||
name: string;
|
||||
collation?: string;
|
||||
formatter?: (data: string | number | null, row: any) => string;
|
||||
formatter?: (data: string | number | null, row: unknown) => string;
|
||||
table?: string;
|
||||
database?: string;
|
||||
}
|
||||
|
|
@ -60,11 +60,11 @@ export class DataTablesSsp {
|
|||
private wheres: string[] = [];
|
||||
private filterBindings: (string | number)[] = [];
|
||||
private customBindings: (string | number)[] = [];
|
||||
private filterWhereSql: string = "1";
|
||||
private customWhereSql: string = "1";
|
||||
private limitSql: string = "";
|
||||
private orderSql: string = "";
|
||||
private joinSql: string = "";
|
||||
private filterWhereSql = "1";
|
||||
private customWhereSql = "1";
|
||||
private limitSql = "";
|
||||
private orderSql = "";
|
||||
private joinSql = "";
|
||||
|
||||
public constructor(query: Query, db: Pool, table: string, primaryKey: string, columnSettings: IColumnSettings[]) {
|
||||
this.start = parseInt(query.start as string, 10);
|
||||
|
|
@ -73,7 +73,9 @@ export class DataTablesSsp {
|
|||
this._order = (query.order as { column: string; dir: string }[]).map((order) => {
|
||||
return { column: parseInt(order.column, 10), dir: order.dir };
|
||||
});
|
||||
this.columns = (query.columns as { data: string; name: string; searchable: string; orderable: string; search: any }[]).map((column) => {
|
||||
this.columns = (
|
||||
query.columns as { data: string; name: string; searchable: string; orderable: string; search: { value: string; regex: string } }[]
|
||||
).map((column) => {
|
||||
return {
|
||||
data: parseInt(column.data, 10),
|
||||
name: column.name,
|
||||
|
|
@ -83,8 +85,8 @@ export class DataTablesSsp {
|
|||
};
|
||||
});
|
||||
this.search = {
|
||||
value: (query.search as any).value as string,
|
||||
regex: (query.search as any).regex === "true",
|
||||
value: (query.search as unknown)["value"] as string,
|
||||
regex: (query.search as unknown)["regex"] === "true",
|
||||
};
|
||||
|
||||
this.db = db;
|
||||
|
|
@ -197,32 +199,32 @@ export class DataTablesSsp {
|
|||
`;
|
||||
}
|
||||
|
||||
public async run(queryTimeout: number = 10_000): Promise<IResult> {
|
||||
public async run(queryTimeout = 10_000): Promise<IResult> {
|
||||
this.limit().order().join().filter();
|
||||
|
||||
const bindings = [...this.filterBindings, ...this.customBindings];
|
||||
|
||||
let [rows, fields] = await this.db.query({
|
||||
let [rows] = await this.db.query({
|
||||
sql: this.buildTotalCountSql(),
|
||||
values: this.customBindings,
|
||||
timeout: queryTimeout,
|
||||
});
|
||||
const recordsTotal = rows[0].count;
|
||||
|
||||
[rows, fields] = await this.db.query({
|
||||
[rows] = await this.db.query({
|
||||
sql: this.buildFilteredCountSql(),
|
||||
values: bindings,
|
||||
timeout: queryTimeout,
|
||||
});
|
||||
const recordsFiltered = rows[0].count;
|
||||
|
||||
[rows, fields] = await this.db.query({
|
||||
[rows] = await this.db.query({
|
||||
sql: this.sql(),
|
||||
rowsAsArray: true,
|
||||
values: bindings,
|
||||
timeout: queryTimeout,
|
||||
});
|
||||
rows = (rows as any[][]).map((row) => {
|
||||
rows = (rows as RowDataPacket[]).map((row) => {
|
||||
for (let i = 0; i < this.columnSettings.length; ++i) {
|
||||
const col = this.columnSettings[i];
|
||||
if (col.formatter !== undefined) {
|
||||
|
|
@ -236,7 +238,7 @@ export class DataTablesSsp {
|
|||
recordsTotal,
|
||||
recordsFiltered,
|
||||
draw: this.draw,
|
||||
data: rows,
|
||||
data: rows as unknown[][],
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -41,7 +41,16 @@ export class Utils {
|
|||
return [1, 3, 4, 7, 11].includes(race) ? EFaction.Alliance : EFaction.Horde;
|
||||
}
|
||||
|
||||
public static makeEmblemObject(obj: any, padWithZeroes: boolean = true): IEmblem {
|
||||
public static makeEmblemObject(
|
||||
obj: {
|
||||
background: number;
|
||||
emblemStyle: number;
|
||||
emblemColor: number;
|
||||
borderStyle: number;
|
||||
borderColor: number;
|
||||
},
|
||||
padWithZeroes = true,
|
||||
): IEmblem {
|
||||
const padLength = padWithZeroes ? 2 : 0;
|
||||
return {
|
||||
icon: obj.emblemStyle.toString().padStart(padLength, "0"),
|
||||
|
|
|
|||
|
|
@ -1,10 +1,10 @@
|
|||
import * as express from "express";
|
||||
import { RowDataPacket } from "mysql2/promise";
|
||||
|
||||
import { Utils } from "../Utils";
|
||||
import { Armory } from "../Armory";
|
||||
import { IRealmConfig } from "../Config";
|
||||
import { IAchievement } from "../data/DbcReader";
|
||||
import { IEmblem, Utils } from "../Utils";
|
||||
import { IAchievement as IAchievementDbc } from "../data/DbcReader";
|
||||
|
||||
interface ICharacterData {
|
||||
guid: number;
|
||||
|
|
@ -27,12 +27,14 @@ interface IEquipmentData {
|
|||
slot: number;
|
||||
itemEntry: number;
|
||||
flags: number;
|
||||
enchantments: string;
|
||||
enchantments: string | number[];
|
||||
randomPropertyId: number;
|
||||
classId: number;
|
||||
subclassId: number;
|
||||
quality: number;
|
||||
transmog?: number;
|
||||
icon?: number;
|
||||
gems?: number[];
|
||||
}
|
||||
|
||||
interface ICustomizationOption {
|
||||
|
|
@ -46,6 +48,30 @@ interface IMount {
|
|||
icon: string;
|
||||
}
|
||||
|
||||
interface IAchievement {
|
||||
id: number;
|
||||
category: number;
|
||||
title: string;
|
||||
description: string;
|
||||
points: number;
|
||||
icon: string;
|
||||
}
|
||||
|
||||
interface IArenaTeam {
|
||||
id: number;
|
||||
name: string;
|
||||
type: number;
|
||||
rating: number;
|
||||
seasonWins: number;
|
||||
seasonGames: number;
|
||||
background: number;
|
||||
emblemStyle: number;
|
||||
emblemColor: number;
|
||||
borderStyle: number;
|
||||
borderColor: number;
|
||||
emblem?: IEmblem;
|
||||
}
|
||||
|
||||
const ItemClassGem = 3;
|
||||
const SpellMechanicMounted = 21;
|
||||
const RaceDisplayName = {
|
||||
|
|
@ -82,7 +108,7 @@ export class CharacterController {
|
|||
private itemSocketBonuses: { [key: number]: number };
|
||||
private mountSpells: number[];
|
||||
private mountBySpellId: { [key: number]: IMount };
|
||||
private achievementById: { [key: number]: IAchievement };
|
||||
private achievementById: { [key: number]: IAchievementDbc };
|
||||
|
||||
public constructor(armory: Armory) {
|
||||
this.armory = armory;
|
||||
|
|
@ -121,7 +147,7 @@ export class CharacterController {
|
|||
}
|
||||
|
||||
this.itemSocketBonuses = {};
|
||||
const [rows, fields] = await this.armory.worldDb.query({
|
||||
const [rows] = await this.armory.worldDb.query({
|
||||
sql: "SELECT entry, socketBonus FROM item_template WHERE socketBonus <> 0",
|
||||
timeout: this.armory.config.dbQueryTimeout,
|
||||
});
|
||||
|
|
@ -175,9 +201,9 @@ export class CharacterController {
|
|||
const equipmentData = await this.getEquipmentData(realmName, charData.guid);
|
||||
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);
|
||||
(row as any).enchantments = this.filterEnchantments(row.itemEntry, row.enchantments);
|
||||
row.icon = this.itemIcons[row.itemEntry];
|
||||
row.gems = this.getGemsFromEnchantments(row.enchantments as string);
|
||||
row.enchantments = this.filterEnchantments(row.itemEntry, row.enchantments as string);
|
||||
return row;
|
||||
});
|
||||
const mounts = await this.getMounts(realmName, charData.guid);
|
||||
|
|
@ -314,7 +340,7 @@ export class CharacterController {
|
|||
|
||||
private async getCharacterData(realm: IRealmConfig, character: string | number): Promise<ICharacterData> {
|
||||
const where = typeof character === "string" ? "LOWER(`characters`.`name`) = LOWER(?)" : "`characters`.`guid` = ?";
|
||||
const [rows, fields] = await this.armory.getCharactersDb(realm.name).query({
|
||||
const [rows] = await this.armory.getCharactersDb(realm.name).query({
|
||||
sql: `
|
||||
SELECT \`characters\`.\`guid\`, \`characters\`.\`name\`, \`race\`, \`class\`, \`gender\`, \`level\`, \`skin\`, \`face\`, \`hairStyle\`, \`hairColor\`, \`facialStyle\`, \`playerFlags\`, \`online\`, \`guild\`.\`name\` AS \`guild\`
|
||||
FROM \`characters\`
|
||||
|
|
@ -337,8 +363,10 @@ export class CharacterController {
|
|||
|
||||
private async getEquipmentData(realm: string, charGuid: number): Promise<IEquipmentData[]> {
|
||||
const transmogSelect = this.armory.config.transmogModule ? ", custom_transmogrification.FakeEntry AS transmog" : "";
|
||||
const transmogJoin = this.armory.config.transmogModule ? "LEFT JOIN custom_transmogrification ON custom_transmogrification.GUID = item_instance.guid" : "";
|
||||
let [rows, fields] = await this.armory.getCharactersDb(realm).query({
|
||||
const transmogJoin = this.armory.config.transmogModule
|
||||
? "LEFT JOIN custom_transmogrification ON custom_transmogrification.GUID = item_instance.guid"
|
||||
: "";
|
||||
let [rows] = await this.armory.getCharactersDb(realm).query({
|
||||
sql: `
|
||||
SELECT
|
||||
character_inventory.slot, item_instance.itemEntry, item_instance.flags, item_instance.enchantments, item_instance.randomPropertyId
|
||||
|
|
@ -360,7 +388,7 @@ export class CharacterController {
|
|||
row.subclassId = item.subclassId;
|
||||
}
|
||||
|
||||
[rows, fields] = await this.armory.worldDb.query({
|
||||
[rows] = await this.armory.worldDb.query({
|
||||
sql: "SELECT entry, quality FROM item_template WHERE entry IN (?)",
|
||||
values: [data.map((row) => row.itemEntry)],
|
||||
timeout: this.armory.config.dbQueryTimeout,
|
||||
|
|
@ -374,7 +402,7 @@ export class CharacterController {
|
|||
}
|
||||
|
||||
private async getMounts(realm: string, charGuid: number): Promise<IMount[]> {
|
||||
const [rows, fields] = await this.armory.getCharactersDb(realm).query({
|
||||
const [rows] = await this.armory.getCharactersDb(realm).query({
|
||||
sql: `
|
||||
SELECT spell
|
||||
FROM character_spell
|
||||
|
|
@ -455,7 +483,7 @@ export class CharacterController {
|
|||
const data = this.armory.characterCustomization.getCharacterCustomizationData(charData.race, charData.gender);
|
||||
const options = [];
|
||||
const setOptionByChoiceIndex = (optionName: string, choiceIndex: number) => {
|
||||
const option = data.Options.find((opt) => opt.Name === optionName);
|
||||
const option = data["Options"].find((opt) => opt.Name === optionName);
|
||||
if (option !== undefined) {
|
||||
const choice = option.Choices.find((choice) => choice.OrderIndex === choiceIndex);
|
||||
if (choice !== undefined) {
|
||||
|
|
@ -464,7 +492,7 @@ export class CharacterController {
|
|||
}
|
||||
};
|
||||
const setOptionByChoiceName = (optionName: string, choiceName: string) => {
|
||||
const option = data.Options.find((opt) => opt.Name === optionName);
|
||||
const option = data["Options"].find((opt) => opt.Name === optionName);
|
||||
if (option !== undefined) {
|
||||
const choice = option.Choices.find((ch) => ch.Name === choiceName);
|
||||
if (choice !== undefined) {
|
||||
|
|
@ -473,7 +501,7 @@ export class CharacterController {
|
|||
}
|
||||
};
|
||||
const setOptionByChoiceId = (optionName: string, choiceId: number) => {
|
||||
const option = data.Options.find((opt) => opt.Name === optionName);
|
||||
const option = data["Options"].find((opt) => opt.Name === optionName);
|
||||
if (option !== undefined) {
|
||||
options.push({ optionId: option.Id, choiceId: choiceId });
|
||||
}
|
||||
|
|
@ -907,7 +935,7 @@ export class CharacterController {
|
|||
}
|
||||
|
||||
private async getTalents(realm: string, character: number): Promise<number[][]> {
|
||||
const [rows, fields] = await this.armory.getCharactersDb(realm).query({
|
||||
const [rows] = await this.armory.getCharactersDb(realm).query({
|
||||
sql: `
|
||||
SELECT spell, specMask
|
||||
FROM character_talent
|
||||
|
|
@ -959,8 +987,8 @@ export class CharacterController {
|
|||
return texturePath.toLowerCase().replace("interface\\icons\\", "").replace("interface\\spellbook\\", "").replace(/\.$/, "");
|
||||
}
|
||||
|
||||
private async getGlyphs(realm: string, character: number): Promise<any[][]> {
|
||||
const [rows, fields] = await this.armory.getCharactersDb(realm).query({
|
||||
private async getGlyphs(realm: string, character: number): Promise<number[][]> {
|
||||
const [rows] = await this.armory.getCharactersDb(realm).query({
|
||||
sql: `
|
||||
SELECT guid, talentGroup, glyph1, glyph2, glyph3, glyph4, glyph5, glyph6
|
||||
FROM character_glyphs
|
||||
|
|
@ -970,7 +998,7 @@ export class CharacterController {
|
|||
timeout: this.armory.config.dbQueryTimeout,
|
||||
});
|
||||
|
||||
const glyphs = [[], []];
|
||||
const glyphs: number[][] = [[], []];
|
||||
for (const row of rows as RowDataPacket[]) {
|
||||
const glyphIds = [row.glyph1, row.glyph2, row.glyph3, row.glyph4, row.glyph5, row.glyph6].filter((id) => id !== 0);
|
||||
for (const glyphId of glyphIds) {
|
||||
|
|
@ -985,7 +1013,10 @@ export class CharacterController {
|
|||
return glyphs;
|
||||
}
|
||||
|
||||
private async getAchievements(realm: string, charData: ICharacterData): Promise<{ achievements: any[]; earned: { [key: number]: any } }> {
|
||||
private async getAchievements(
|
||||
realm: string,
|
||||
charData: ICharacterData,
|
||||
): Promise<{ achievements: IAchievement[]; earned: { [key: number]: number } }> {
|
||||
const promises = await this.armory.dbc
|
||||
.achievement()
|
||||
.filter((ach) => ach.faction === -1 || ach.faction === Utils.getFactionFromRaceId(charData.race))
|
||||
|
|
@ -1003,7 +1034,7 @@ export class CharacterController {
|
|||
.toArray();
|
||||
const achievements = await Promise.all(promises);
|
||||
|
||||
const [rows, fields] = await this.armory.getCharactersDb(realm).query({
|
||||
const [rows] = await this.armory.getCharactersDb(realm).query({
|
||||
sql: `
|
||||
SELECT achievement, date
|
||||
FROM character_achievement
|
||||
|
|
@ -1012,11 +1043,9 @@ export class CharacterController {
|
|||
values: [charData.guid],
|
||||
timeout: this.armory.config.dbQueryTimeout,
|
||||
});
|
||||
const earned = {};
|
||||
const earned: { [key: number]: number } = {};
|
||||
for (const row of rows as RowDataPacket[]) {
|
||||
earned[row.achievement] = {
|
||||
date: row.date,
|
||||
};
|
||||
earned[row.achievement] = row.date;
|
||||
}
|
||||
|
||||
return {
|
||||
|
|
@ -1026,7 +1055,7 @@ export class CharacterController {
|
|||
}
|
||||
|
||||
private async getPvpKills(realm: string, charGuid: number): Promise<{ total: number; today: number; yesterday: number }> {
|
||||
const [rows, fields] = await this.armory.getCharactersDb(realm).query({
|
||||
const [rows] = await this.armory.getCharactersDb(realm).query({
|
||||
sql: `
|
||||
SELECT totalKills, todayKills, yesterdayKills
|
||||
FROM characters
|
||||
|
|
@ -1044,8 +1073,8 @@ export class CharacterController {
|
|||
};
|
||||
}
|
||||
|
||||
private async getArenaTeams(realm: string, charGuid: number): Promise<any[]> {
|
||||
const [rows, fields] = await this.armory.getCharactersDb(realm).query({
|
||||
private async getArenaTeams(realm: string, charGuid: number): Promise<IArenaTeam[]> {
|
||||
const [rows] = await this.armory.getCharactersDb(realm).query({
|
||||
sql: `
|
||||
SELECT
|
||||
arena_team.arenaTeamId AS id, arena_team.name, arena_team.type, arena_team.rating, arena_team.seasonWins, arena_team.seasonGames,
|
||||
|
|
@ -1059,7 +1088,7 @@ export class CharacterController {
|
|||
timeout: this.armory.config.dbQueryTimeout,
|
||||
});
|
||||
|
||||
return (rows as RowDataPacket[]).map((row) => {
|
||||
return (rows as IArenaTeam[]).map((row) => {
|
||||
row.emblem = Utils.makeEmblemObject(row, false);
|
||||
return row;
|
||||
});
|
||||
|
|
|
|||
|
|
@ -2,16 +2,25 @@ import * as express from "express";
|
|||
import { encode } from "html-entities";
|
||||
import { RowDataPacket } from "mysql2/promise";
|
||||
|
||||
import { Utils } from "../Utils";
|
||||
import { Armory } from "../Armory";
|
||||
import { IRealmConfig } from "../Config";
|
||||
import { DataTablesSsp } from "../DataTablesSsp";
|
||||
import { Utils, IEmblem, EFaction } from "../Utils";
|
||||
|
||||
interface IGuildRank {
|
||||
id: number;
|
||||
name: string;
|
||||
}
|
||||
|
||||
interface IGuildData {
|
||||
id: number;
|
||||
name: string;
|
||||
leader: string;
|
||||
faction: EFaction;
|
||||
emblem: IEmblem;
|
||||
membersCount: number;
|
||||
}
|
||||
|
||||
export class GuildController {
|
||||
private armory: Armory;
|
||||
|
||||
|
|
@ -87,17 +96,20 @@ export class GuildController {
|
|||
const result = await ssp.where("`guildid` = ?", guildId).where("`deleteInfos_Account` IS NULL").run(this.armory.config.dbQueryTimeout);
|
||||
|
||||
const ranks = await this.getGuildRanks(realm, guildId);
|
||||
(result as any).ranks = {};
|
||||
const ranksObj: { [key: number]: string } = {};
|
||||
for (const rank of ranks) {
|
||||
(result as any).ranks[rank.id] = encode(rank.name);
|
||||
ranksObj[rank.id] = encode(rank.name);
|
||||
}
|
||||
|
||||
res.json(result);
|
||||
res.json({
|
||||
...result,
|
||||
ranks: ranksObj,
|
||||
});
|
||||
}
|
||||
|
||||
private async getGuildData(realm: IRealmConfig, name: string): Promise<any> {
|
||||
private async getGuildData(realm: IRealmConfig, name: string): Promise<IGuildData> {
|
||||
const db = this.armory.getCharactersDb(realm.name);
|
||||
let [rows, fields] = await db.query({
|
||||
let [rows] = await db.query({
|
||||
sql: `
|
||||
SELECT guildid, name, leaderguid, EmblemStyle AS emblemStyle, EmblemColor AS emblemColor, BorderStyle AS borderStyle, BorderColor AS borderColor, BackgroundColor AS background
|
||||
FROM guild WHERE name = ?
|
||||
|
|
@ -110,7 +122,7 @@ export class GuildController {
|
|||
}
|
||||
const guild = rows[0];
|
||||
|
||||
[rows, fields] = await db.query({
|
||||
[rows] = await db.query({
|
||||
sql: `
|
||||
SELECT name, race FROM characters
|
||||
WHERE guid = ?
|
||||
|
|
@ -120,7 +132,7 @@ export class GuildController {
|
|||
});
|
||||
const leader = rows[0];
|
||||
|
||||
[rows, fields] = await db.query({
|
||||
[rows] = await db.query({
|
||||
sql: `
|
||||
SELECT COUNT(guid) AS \`count\` FROM guild_member
|
||||
WHERE guildid = ?
|
||||
|
|
@ -142,7 +154,7 @@ export class GuildController {
|
|||
|
||||
private async getGuildId(realm: IRealmConfig, name: string): Promise<number> {
|
||||
const db = this.armory.getCharactersDb(realm.name);
|
||||
const [rows, fields] = await db.query({
|
||||
const [rows] = await db.query({
|
||||
sql: `
|
||||
SELECT guildid
|
||||
FROM guild WHERE name = ?
|
||||
|
|
@ -159,7 +171,7 @@ export class GuildController {
|
|||
|
||||
private async guildExists(realm: IRealmConfig, id: number): Promise<boolean> {
|
||||
const db = this.armory.getCharactersDb(realm.name);
|
||||
const [rows, fields] = await db.query({
|
||||
const [rows] = await db.query({
|
||||
sql: `
|
||||
SELECT guildid
|
||||
FROM guild WHERE guildid
|
||||
|
|
@ -173,7 +185,7 @@ export class GuildController {
|
|||
|
||||
private async getGuildRanks(realm: IRealmConfig, id: number): Promise<IGuildRank[]> {
|
||||
const db = this.armory.getCharactersDb(realm.name);
|
||||
const [rows, fields] = await db.query({
|
||||
const [rows] = await db.query({
|
||||
sql: `
|
||||
SELECT rid AS id, rname AS name
|
||||
FROM guild_rank WHERE guildid = ?
|
||||
|
|
|
|||
|
|
@ -56,8 +56,10 @@ export class IndexController {
|
|||
}
|
||||
|
||||
const result = await ssp.where("`deleteInfos_Account` IS NULL").run(this.armory.config.dbQueryTimeout);
|
||||
(result as any).realm = realm.name;
|
||||
|
||||
res.json(result);
|
||||
res.json({
|
||||
...result,
|
||||
realm: realm.name,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ const fsp = fs.promises;
|
|||
import * as path from "path";
|
||||
|
||||
export class CharacterCustomization {
|
||||
private data: { [key: number]: { [key: number]: any } };
|
||||
private data: { [key: number]: { [key: number]: unknown } };
|
||||
|
||||
public async loadData(): Promise<void> {
|
||||
this.data = {};
|
||||
|
|
@ -21,7 +21,7 @@ export class CharacterCustomization {
|
|||
}
|
||||
}
|
||||
|
||||
public getCharacterCustomizationData(race: number, gender: number): any {
|
||||
public getCharacterCustomizationData(race: number, gender: number): unknown {
|
||||
return this.data[race][gender];
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -219,10 +219,13 @@ class DbcReader<T> {
|
|||
return;
|
||||
}
|
||||
|
||||
const headerCols = headerLine.value.map((header) => camelCase(header).replace(/[\[\]]/g, ""));
|
||||
const headerCols = headerLine.value.map((header) => camelCase(header).replace(/[[\]]/g, ""));
|
||||
|
||||
for await (const arr of itr) {
|
||||
const cols = arr.map((value) => (isNaN(value as any) ? value : parseInt(value, 10)));
|
||||
const cols = arr.map((value) => {
|
||||
const parsed = parseInt(value, 10);
|
||||
return isNaN(parsed) ? value : parsed;
|
||||
});
|
||||
const row = {};
|
||||
headerCols.forEach((header, headerIdx) => {
|
||||
if (this.fields.length === 0 || this.fields.includes(header)) {
|
||||
|
|
@ -243,8 +246,8 @@ class DbcReader<T> {
|
|||
const str = chunk.toString();
|
||||
// Iterate over each character, keep track of current column (of the returned array)
|
||||
for (let c = 0; c < str.length; ++c) {
|
||||
let ch = str[c],
|
||||
nch = str[c + 1]; // Current character, next character
|
||||
const ch = str[c];
|
||||
const nch = str[c + 1]; // Current character, next character
|
||||
if (!(col in arr)) {
|
||||
arr[col] = ""; // Create a new column (start with empty string) if necessary
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,9 +1,9 @@
|
|||
import "dotenv/config";
|
||||
import "source-map-support/register";
|
||||
|
||||
import { Armory } from "./Armory";
|
||||
|
||||
async function main(): Promise<void> {
|
||||
require("source-map-support").install();
|
||||
|
||||
const armory = new Armory();
|
||||
await armory.start();
|
||||
}
|
||||
|
|
|
|||
2
src/index.d.ts
vendored
2
src/index.d.ts
vendored
|
|
@ -1,4 +1,4 @@
|
|||
declare module Express {
|
||||
declare namespace Express {
|
||||
export interface Request {
|
||||
id: string;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,18 +3,16 @@ const fsp = fs.promises;
|
|||
import * as path from "path";
|
||||
|
||||
import * as pako from "pako";
|
||||
import fetch from "node-fetch";
|
||||
import * as mkdirp from "mkdirp";
|
||||
import * as glob from "glob-promise";
|
||||
import { Response } from "node-fetch";
|
||||
import "source-map-support/register";
|
||||
import * as prettyMs from "pretty-ms";
|
||||
import * as cliProgress from "cli-progress";
|
||||
import fetch, { Response } from "node-fetch";
|
||||
import promisepool = require("@supercharge/promise-pool");
|
||||
|
||||
import { DbcManager, IItemAppearanceDbc, IItemModifiedAppearanceDbc, IMountDbc, IMountXDisplayDbc } from "../armory/data/DbcReader";
|
||||
|
||||
require("source-map-support").install();
|
||||
|
||||
const baseUrl = "https://wow.zamimg.com/modelviewer/live";
|
||||
|
||||
class Stopwatch {
|
||||
|
|
@ -92,11 +90,10 @@ const modelsDownloadQueue = new Set<number>();
|
|||
const texturesDownloadQueue = new Set<number>();
|
||||
const bonesDownloadQueue = new Set<number>();
|
||||
|
||||
async function download(dir: string, file: string): Promise<string | any> {
|
||||
async function download(dir: string, file: string): Promise<string | unknown> {
|
||||
const dataDir = path.join(process.cwd(), "data");
|
||||
const fullPath = `${dir}/${file}`;
|
||||
|
||||
try {
|
||||
const res = await fetch(`${baseUrl}/${fullPath}`);
|
||||
if (res.status !== 200) {
|
||||
throw new HttpRequestError(res);
|
||||
|
|
@ -118,12 +115,9 @@ async function download(dir: string, file: string): Promise<string | any> {
|
|||
|
||||
return fileStream.path.toString();
|
||||
}
|
||||
} catch (err) {
|
||||
throw err;
|
||||
}
|
||||
}
|
||||
|
||||
function queueTexturesAndModels(item: any): void {
|
||||
function queueTexturesAndModels(item): void {
|
||||
if (item.TextureFiles !== null) {
|
||||
for (const file in Object.values(item.TextureFiles)) {
|
||||
if (file["FileDataId"] !== 0) {
|
||||
|
|
@ -172,10 +166,10 @@ async function downloadRaces(): Promise<void> {
|
|||
.withConcurrency(4)
|
||||
.process(async (race) => {
|
||||
const characterJson = await download("meta/character", `${race}.json`);
|
||||
modelsDownloadQueue.add(characterJson.Model);
|
||||
modelsDownloadQueue.add(characterJson["Model"]);
|
||||
|
||||
const customizationJson = await download("meta/charactercustomization2", `${characterJson.Race}_${characterJson.Gender}.json`);
|
||||
for (const option of customizationJson.Options) {
|
||||
const customizationJson = await download("meta/charactercustomization2", `${characterJson["Race"]}_${characterJson["Gender"]}.json`);
|
||||
for (const option of customizationJson["Options"]) {
|
||||
for (const choice of option.Choices) {
|
||||
for (const element of choice.Elements) {
|
||||
if (
|
||||
|
|
@ -192,7 +186,7 @@ async function downloadRaces(): Promise<void> {
|
|||
}
|
||||
}
|
||||
|
||||
const textureFiles = Object.values(customizationJson.TextureFiles).flat();
|
||||
const textureFiles = Object.values(customizationJson["TextureFiles"]).flat();
|
||||
for (const file of textureFiles) {
|
||||
texturesDownloadQueue.add(file["FileDataId"]);
|
||||
}
|
||||
|
|
@ -255,7 +249,7 @@ async function downloadWeapons(): Promise<void> {
|
|||
}
|
||||
const appearance = dbcItemAppearanceById[modifiedAppearance.itemAppearanceId];
|
||||
try {
|
||||
const itemJson = await download(`meta/item`, `${appearance.itemDisplayInfoId}.json`);
|
||||
const itemJson = await download("meta/item", `${appearance.itemDisplayInfoId}.json`);
|
||||
queueTexturesAndModels(itemJson);
|
||||
progress.increment();
|
||||
} catch (err) {
|
||||
|
|
|
|||
|
|
@ -117,8 +117,7 @@
|
|||
$achievement.find("a").attr("href", `{{aowow}}?achievement=${achievement.id}`);
|
||||
|
||||
if (isEarned) {
|
||||
const earned = achievementsData.earned[achievement.id];
|
||||
const date = new Date(earned.date * 1000);
|
||||
const date = new Date(achievementsData.earned[achievement.id] * 1000);
|
||||
$achievement.find(".earned-date").text(date.toLocaleDateString());
|
||||
$achievement.addClass("earned");
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue