feat(stats): log unknown categories in updateStats (#947)
Reviewed-on: OpenWF/SpaceNinjaServer#947 Reviewed-by: Sainan <sainan@calamity.inc> Co-authored-by: AMelonInsideLemon <166175391+AMelonInsideLemon@users.noreply.github.com> Co-committed-by: AMelonInsideLemon <166175391+AMelonInsideLemon@users.noreply.github.com>
This commit is contained in:
		
							parent
							
								
									cf50738d34
								
							
						
					
					
						commit
						2dade02f3e
					
				@ -1,14 +1,15 @@
 | 
				
			|||||||
import { getJSONfromString } from "@/src/helpers/stringHelpers";
 | 
					import { getJSONfromString } from "@/src/helpers/stringHelpers";
 | 
				
			||||||
import { getAccountIdForRequest } from "@/src/services/loginService";
 | 
					import { getAccountIdForRequest } from "@/src/services/loginService";
 | 
				
			||||||
import { getStats, uploadStats } from "@/src/services/statsService";
 | 
					import { getStats, updateStats } from "@/src/services/statsService";
 | 
				
			||||||
import { IStatsUpload } from "@/src/types/statTypes";
 | 
					import { IStatsUpdate } from "@/src/types/statTypes";
 | 
				
			||||||
import { RequestHandler } from "express";
 | 
					import { RequestHandler } from "express";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const uploadController: RequestHandler = async (req, res) => {
 | 
					const uploadController: RequestHandler = async (req, res) => {
 | 
				
			||||||
    const payload = getJSONfromString<IStatsUpload>(String(req.body));
 | 
					    // eslint-disable-next-line @typescript-eslint/no-unused-vars
 | 
				
			||||||
 | 
					    const { PS, ...payload } = getJSONfromString<IStatsUpdate>(String(req.body));
 | 
				
			||||||
    const accountId = await getAccountIdForRequest(req);
 | 
					    const accountId = await getAccountIdForRequest(req);
 | 
				
			||||||
    const playerStats = await getStats(accountId);
 | 
					    const playerStats = await getStats(accountId);
 | 
				
			||||||
    await uploadStats(playerStats, payload);
 | 
					    await updateStats(playerStats, payload);
 | 
				
			||||||
    res.status(200).end();
 | 
					    res.status(200).end();
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
@ -5,14 +5,14 @@ import allScans from "@/static/fixed_responses/allScans.json";
 | 
				
			|||||||
import { ExportEnemies } from "warframe-public-export-plus";
 | 
					import { ExportEnemies } from "warframe-public-export-plus";
 | 
				
			||||||
import { getInventory } from "@/src/services/inventoryService";
 | 
					import { getInventory } from "@/src/services/inventoryService";
 | 
				
			||||||
import { getStats } from "@/src/services/statsService";
 | 
					import { getStats } from "@/src/services/statsService";
 | 
				
			||||||
import { IStatsView } from "@/src/types/statTypes";
 | 
					import { IStatsClient } from "@/src/types/statTypes";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const viewController: RequestHandler = async (req, res) => {
 | 
					const viewController: RequestHandler = async (req, res) => {
 | 
				
			||||||
    const accountId = await getAccountIdForRequest(req);
 | 
					    const accountId = await getAccountIdForRequest(req);
 | 
				
			||||||
    const inventory = await getInventory(accountId, "XPInfo");
 | 
					    const inventory = await getInventory(accountId, "XPInfo");
 | 
				
			||||||
    const playerStats = await getStats(accountId);
 | 
					    const playerStats = await getStats(accountId);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    const responseJson: IStatsView = playerStats.toJSON();
 | 
					    const responseJson = playerStats.toJSON() as IStatsClient;
 | 
				
			||||||
    responseJson.Weapons ??= [];
 | 
					    responseJson.Weapons ??= [];
 | 
				
			||||||
    for (const item of inventory.XPInfo) {
 | 
					    for (const item of inventory.XPInfo) {
 | 
				
			||||||
        const weaponIndex = responseJson.Weapons.findIndex(element => element.type == item.ItemType);
 | 
					        const weaponIndex = responseJson.Weapons.findIndex(element => element.type == item.ItemType);
 | 
				
			||||||
 | 
				
			|||||||
@ -1,5 +1,5 @@
 | 
				
			|||||||
import { Document, Schema, Types, model } from "mongoose";
 | 
					import { Document, Schema, Types, model } from "mongoose";
 | 
				
			||||||
import { IEnemy, IMission, IScan, ITutorial, IAbility, IWeapon, IStatsDatabase } from "@/src/types/statTypes";
 | 
					import { IEnemy, IMission, IScan, ITutorial, IAbility, IWeapon, IStatsDatabase, IRace } from "@/src/types/statTypes";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const abilitySchema = new Schema<IAbility>(
 | 
					const abilitySchema = new Schema<IAbility>(
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
@ -58,6 +58,13 @@ const weaponSchema = new Schema<IWeapon>(
 | 
				
			|||||||
    { _id: false }
 | 
					    { _id: false }
 | 
				
			||||||
);
 | 
					);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const raceSchema = new Schema<IRace>(
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        highScore: Number
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    { _id: false }
 | 
				
			||||||
 | 
					);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const statsSchema = new Schema<IStatsDatabase>({
 | 
					const statsSchema = new Schema<IStatsDatabase>({
 | 
				
			||||||
    accountOwnerId: { type: Schema.Types.ObjectId, required: true },
 | 
					    accountOwnerId: { type: Schema.Types.ObjectId, required: true },
 | 
				
			||||||
    CiphersSolved: Number,
 | 
					    CiphersSolved: Number,
 | 
				
			||||||
@ -69,6 +76,8 @@ const statsSchema = new Schema<IStatsDatabase>({
 | 
				
			|||||||
    MissionsCompleted: Number,
 | 
					    MissionsCompleted: Number,
 | 
				
			||||||
    MissionsQuit: Number,
 | 
					    MissionsQuit: Number,
 | 
				
			||||||
    MissionsFailed: Number,
 | 
					    MissionsFailed: Number,
 | 
				
			||||||
 | 
					    MissionsInterrupted: Number,
 | 
				
			||||||
 | 
					    MissionsDumped: Number,
 | 
				
			||||||
    TimePlayedSec: Number,
 | 
					    TimePlayedSec: Number,
 | 
				
			||||||
    PickupCount: Number,
 | 
					    PickupCount: Number,
 | 
				
			||||||
    Tutorial: { type: Map, of: tutorialSchema, default: {} },
 | 
					    Tutorial: { type: Map, of: tutorialSchema, default: {} },
 | 
				
			||||||
@ -81,7 +90,8 @@ const statsSchema = new Schema<IStatsDatabase>({
 | 
				
			|||||||
    Missions: { type: [missionSchema], default: [] },
 | 
					    Missions: { type: [missionSchema], default: [] },
 | 
				
			||||||
    Deaths: Number,
 | 
					    Deaths: Number,
 | 
				
			||||||
    HealCount: Number,
 | 
					    HealCount: Number,
 | 
				
			||||||
    ReviveCount: Number
 | 
					    ReviveCount: Number,
 | 
				
			||||||
 | 
					    Races: { type: Map, of: raceSchema, default: {} }
 | 
				
			||||||
});
 | 
					});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
statsSchema.set("toJSON", {
 | 
					statsSchema.set("toJSON", {
 | 
				
			||||||
 | 
				
			|||||||
@ -1,5 +1,15 @@
 | 
				
			|||||||
import { Stats, TStatsDatabaseDocument } from "@/src/models/statsModel";
 | 
					import { Stats, TStatsDatabaseDocument } from "@/src/models/statsModel";
 | 
				
			||||||
import { IStatsUpload } from "@/src/types/statTypes";
 | 
					import {
 | 
				
			||||||
 | 
					    IEnemy,
 | 
				
			||||||
 | 
					    IStatsAdd,
 | 
				
			||||||
 | 
					    IStatsMax,
 | 
				
			||||||
 | 
					    IStatsSet,
 | 
				
			||||||
 | 
					    IStatsTimers,
 | 
				
			||||||
 | 
					    IStatsUpdate,
 | 
				
			||||||
 | 
					    IUploadEntry,
 | 
				
			||||||
 | 
					    IWeapon
 | 
				
			||||||
 | 
					} from "@/src/types/statTypes";
 | 
				
			||||||
 | 
					import { logger } from "../utils/logger";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export const createStats = async (accountId: string): Promise<TStatsDatabaseDocument> => {
 | 
					export const createStats = async (accountId: string): Promise<TStatsDatabaseDocument> => {
 | 
				
			||||||
    const stats = new Stats({ accountOwnerId: accountId });
 | 
					    const stats = new Stats({ accountOwnerId: accountId });
 | 
				
			||||||
@ -15,269 +25,383 @@ export const getStats = async (accountOwnerId: string): Promise<TStatsDatabaseDo
 | 
				
			|||||||
    return stats;
 | 
					    return stats;
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export const uploadStats = async (playerStats: TStatsDatabaseDocument, payload: IStatsUpload): Promise<void> => {
 | 
					export const updateStats = async (playerStats: TStatsDatabaseDocument, payload: IStatsUpdate): Promise<void> => {
 | 
				
			||||||
    if (payload.add) {
 | 
					    const unknownCategories: Record<string, string[]> = {};
 | 
				
			||||||
        const {
 | 
					 | 
				
			||||||
            MISSION_COMPLETE,
 | 
					 | 
				
			||||||
            PICKUP_ITEM,
 | 
					 | 
				
			||||||
            SCAN,
 | 
					 | 
				
			||||||
            USE_ABILITY,
 | 
					 | 
				
			||||||
            FIRE_WEAPON,
 | 
					 | 
				
			||||||
            HIT_ENTITY_ITEM,
 | 
					 | 
				
			||||||
            HEADSHOT_ITEM,
 | 
					 | 
				
			||||||
            KILL_ENEMY_ITEM,
 | 
					 | 
				
			||||||
            KILL_ENEMY,
 | 
					 | 
				
			||||||
            EXECUTE_ENEMY,
 | 
					 | 
				
			||||||
            HEADSHOT,
 | 
					 | 
				
			||||||
            DIE,
 | 
					 | 
				
			||||||
            MELEE_KILL,
 | 
					 | 
				
			||||||
            INCOME,
 | 
					 | 
				
			||||||
            CIPHER
 | 
					 | 
				
			||||||
        } = payload.add;
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
        if (MISSION_COMPLETE) {
 | 
					    for (const [action, actionData] of Object.entries(payload)) {
 | 
				
			||||||
            for (const [key, value] of Object.entries(MISSION_COMPLETE)) {
 | 
					        switch (action) {
 | 
				
			||||||
                switch (key) {
 | 
					            case "add":
 | 
				
			||||||
                    case "GS_SUCCESS":
 | 
					                for (const [category, data] of Object.entries(actionData as IStatsAdd)) {
 | 
				
			||||||
                        playerStats.MissionsCompleted ??= 0;
 | 
					                    switch (category) {
 | 
				
			||||||
                        playerStats.MissionsCompleted += value;
 | 
					                        case "MISSION_COMPLETE":
 | 
				
			||||||
                        break;
 | 
					                            for (const [key, value] of Object.entries(data as IUploadEntry)) {
 | 
				
			||||||
                    case "GS_QUIT":
 | 
					                                switch (key) {
 | 
				
			||||||
                        playerStats.MissionsQuit ??= 0;
 | 
					                                    case "GS_SUCCESS":
 | 
				
			||||||
                        playerStats.MissionsQuit += value;
 | 
					                                        playerStats.MissionsCompleted ??= 0;
 | 
				
			||||||
                        break;
 | 
					                                        playerStats.MissionsCompleted += value;
 | 
				
			||||||
                    case "GS_FAILURE":
 | 
					                                        break;
 | 
				
			||||||
                        playerStats.MissionsFailed ??= 0;
 | 
					                                    case "GS_QUIT":
 | 
				
			||||||
                        playerStats.MissionsFailed += value;
 | 
					                                        playerStats.MissionsQuit ??= 0;
 | 
				
			||||||
                        break;
 | 
					                                        playerStats.MissionsQuit += value;
 | 
				
			||||||
 | 
					                                        break;
 | 
				
			||||||
 | 
					                                    case "GS_FAILURE":
 | 
				
			||||||
 | 
					                                        playerStats.MissionsFailed ??= 0;
 | 
				
			||||||
 | 
					                                        playerStats.MissionsFailed += value;
 | 
				
			||||||
 | 
					                                        break;
 | 
				
			||||||
 | 
					                                    case "GS_INTERRUPTED":
 | 
				
			||||||
 | 
					                                        playerStats.MissionsInterrupted ??= 0;
 | 
				
			||||||
 | 
					                                        playerStats.MissionsInterrupted += value;
 | 
				
			||||||
 | 
					                                        break;
 | 
				
			||||||
 | 
					                                    case "GS_DUMPED":
 | 
				
			||||||
 | 
					                                        playerStats.MissionsDumped ??= 0;
 | 
				
			||||||
 | 
					                                        playerStats.MissionsDumped += value;
 | 
				
			||||||
 | 
					                                        break;
 | 
				
			||||||
 | 
					                                    default:
 | 
				
			||||||
 | 
					                                        if (!ignoredCategories.includes(category)) {
 | 
				
			||||||
 | 
					                                            if (!unknownCategories[action]) {
 | 
				
			||||||
 | 
					                                                unknownCategories[action] = [];
 | 
				
			||||||
 | 
					                                            }
 | 
				
			||||||
 | 
					                                            unknownCategories[action].push(category);
 | 
				
			||||||
 | 
					                                        }
 | 
				
			||||||
 | 
					                                        break;
 | 
				
			||||||
 | 
					                                }
 | 
				
			||||||
 | 
					                            }
 | 
				
			||||||
 | 
					                            break;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                        case "PICKUP_ITEM":
 | 
				
			||||||
 | 
					                            playerStats.PickupCount ??= 0;
 | 
				
			||||||
 | 
					                            // eslint-disable-next-line @typescript-eslint/no-unused-vars
 | 
				
			||||||
 | 
					                            for (const [_key, value] of Object.entries(data as IUploadEntry)) {
 | 
				
			||||||
 | 
					                                playerStats.PickupCount += value;
 | 
				
			||||||
 | 
					                            }
 | 
				
			||||||
 | 
					                            break;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                        case "SCAN":
 | 
				
			||||||
 | 
					                            playerStats.Scans ??= [];
 | 
				
			||||||
 | 
					                            for (const [type, scans] of Object.entries(data as IUploadEntry)) {
 | 
				
			||||||
 | 
					                                const scan = playerStats.Scans.find(element => element.type === type);
 | 
				
			||||||
 | 
					                                if (scan) {
 | 
				
			||||||
 | 
					                                    scan.scans ??= 0;
 | 
				
			||||||
 | 
					                                    scan.scans += scans;
 | 
				
			||||||
 | 
					                                } else {
 | 
				
			||||||
 | 
					                                    playerStats.Scans.push({ type: type, scans });
 | 
				
			||||||
 | 
					                                }
 | 
				
			||||||
 | 
					                            }
 | 
				
			||||||
 | 
					                            break;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                        case "USE_ABILITY":
 | 
				
			||||||
 | 
					                            playerStats.Abilities ??= [];
 | 
				
			||||||
 | 
					                            for (const [type, used] of Object.entries(data as IUploadEntry)) {
 | 
				
			||||||
 | 
					                                const ability = playerStats.Abilities.find(element => element.type === type);
 | 
				
			||||||
 | 
					                                if (ability) {
 | 
				
			||||||
 | 
					                                    ability.used ??= 0;
 | 
				
			||||||
 | 
					                                    ability.used += used;
 | 
				
			||||||
 | 
					                                } else {
 | 
				
			||||||
 | 
					                                    playerStats.Abilities.push({ type: type, used });
 | 
				
			||||||
 | 
					                                }
 | 
				
			||||||
 | 
					                            }
 | 
				
			||||||
 | 
					                            break;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                        case "FIRE_WEAPON":
 | 
				
			||||||
 | 
					                        case "HIT_ENTITY_ITEM":
 | 
				
			||||||
 | 
					                        case "HEADSHOT_ITEM":
 | 
				
			||||||
 | 
					                        case "KILL_ENEMY_ITEM":
 | 
				
			||||||
 | 
					                            playerStats.Weapons ??= [];
 | 
				
			||||||
 | 
					                            const statKey = {
 | 
				
			||||||
 | 
					                                FIRE_WEAPON: "fired",
 | 
				
			||||||
 | 
					                                HIT_ENTITY_ITEM: "hits",
 | 
				
			||||||
 | 
					                                HEADSHOT_ITEM: "headshots",
 | 
				
			||||||
 | 
					                                KILL_ENEMY_ITEM: "kills"
 | 
				
			||||||
 | 
					                            }[category] as "fired" | "hits" | "headshots" | "kills";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                            for (const [type, count] of Object.entries(data as IUploadEntry)) {
 | 
				
			||||||
 | 
					                                const weapon = playerStats.Weapons.find(element => element.type === type);
 | 
				
			||||||
 | 
					                                if (weapon) {
 | 
				
			||||||
 | 
					                                    weapon[statKey] ??= 0;
 | 
				
			||||||
 | 
					                                    weapon[statKey] += count;
 | 
				
			||||||
 | 
					                                } else {
 | 
				
			||||||
 | 
					                                    const newWeapon: IWeapon = { type: type };
 | 
				
			||||||
 | 
					                                    newWeapon[statKey] = count;
 | 
				
			||||||
 | 
					                                    playerStats.Weapons.push(newWeapon);
 | 
				
			||||||
 | 
					                                }
 | 
				
			||||||
 | 
					                            }
 | 
				
			||||||
 | 
					                            break;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                        case "KILL_ENEMY":
 | 
				
			||||||
 | 
					                        case "EXECUTE_ENEMY":
 | 
				
			||||||
 | 
					                        case "HEADSHOT":
 | 
				
			||||||
 | 
					                            playerStats.Enemies ??= [];
 | 
				
			||||||
 | 
					                            const enemyStatKey = {
 | 
				
			||||||
 | 
					                                KILL_ENEMY: "kills",
 | 
				
			||||||
 | 
					                                EXECUTE_ENEMY: "executions",
 | 
				
			||||||
 | 
					                                HEADSHOT: "headshots"
 | 
				
			||||||
 | 
					                            }[category] as "kills" | "executions" | "headshots";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                            for (const [type, count] of Object.entries(data as IUploadEntry)) {
 | 
				
			||||||
 | 
					                                const enemy = playerStats.Enemies.find(element => element.type === type);
 | 
				
			||||||
 | 
					                                if (enemy) {
 | 
				
			||||||
 | 
					                                    enemy[enemyStatKey] ??= 0;
 | 
				
			||||||
 | 
					                                    enemy[enemyStatKey] += count;
 | 
				
			||||||
 | 
					                                } else {
 | 
				
			||||||
 | 
					                                    const newEnemy: IEnemy = { type: type };
 | 
				
			||||||
 | 
					                                    newEnemy[enemyStatKey] = count;
 | 
				
			||||||
 | 
					                                    playerStats.Enemies.push(newEnemy);
 | 
				
			||||||
 | 
					                                }
 | 
				
			||||||
 | 
					                            }
 | 
				
			||||||
 | 
					                            break;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                        case "DIE":
 | 
				
			||||||
 | 
					                            playerStats.Enemies ??= [];
 | 
				
			||||||
 | 
					                            playerStats.Deaths ??= 0;
 | 
				
			||||||
 | 
					                            for (const [type, deaths] of Object.entries(data as IUploadEntry)) {
 | 
				
			||||||
 | 
					                                playerStats.Deaths += deaths;
 | 
				
			||||||
 | 
					                                const enemy = playerStats.Enemies.find(element => element.type === type);
 | 
				
			||||||
 | 
					                                if (enemy) {
 | 
				
			||||||
 | 
					                                    enemy.deaths ??= 0;
 | 
				
			||||||
 | 
					                                    enemy.deaths += deaths;
 | 
				
			||||||
 | 
					                                } else {
 | 
				
			||||||
 | 
					                                    playerStats.Enemies.push({ type: type, deaths });
 | 
				
			||||||
 | 
					                                }
 | 
				
			||||||
 | 
					                            }
 | 
				
			||||||
 | 
					                            break;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                        case "MELEE_KILL":
 | 
				
			||||||
 | 
					                            playerStats.MeleeKills ??= 0;
 | 
				
			||||||
 | 
					                            // eslint-disable-next-line @typescript-eslint/no-unused-vars
 | 
				
			||||||
 | 
					                            for (const [_key, kills] of Object.entries(data as IUploadEntry)) {
 | 
				
			||||||
 | 
					                                playerStats.MeleeKills += kills;
 | 
				
			||||||
 | 
					                            }
 | 
				
			||||||
 | 
					                            break;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                        case "INCOME":
 | 
				
			||||||
 | 
					                            playerStats.Income ??= 0;
 | 
				
			||||||
 | 
					                            playerStats.Income += data;
 | 
				
			||||||
 | 
					                            break;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                        case "CIPHER":
 | 
				
			||||||
 | 
					                            if (data["0"] > 0) {
 | 
				
			||||||
 | 
					                                playerStats.CiphersFailed ??= 0;
 | 
				
			||||||
 | 
					                                playerStats.CiphersFailed += data["0"];
 | 
				
			||||||
 | 
					                            }
 | 
				
			||||||
 | 
					                            if (data["1"] > 0) {
 | 
				
			||||||
 | 
					                                playerStats.CiphersSolved ??= 0;
 | 
				
			||||||
 | 
					                                playerStats.CiphersSolved += data["1"];
 | 
				
			||||||
 | 
					                            }
 | 
				
			||||||
 | 
					                            break;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                        default:
 | 
				
			||||||
 | 
					                            if (!ignoredCategories.includes(category)) {
 | 
				
			||||||
 | 
					                                if (!unknownCategories[action]) {
 | 
				
			||||||
 | 
					                                    unknownCategories[action] = [];
 | 
				
			||||||
 | 
					                                }
 | 
				
			||||||
 | 
					                                unknownCategories[action].push(category);
 | 
				
			||||||
 | 
					                            }
 | 
				
			||||||
 | 
					                            break;
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
            }
 | 
					                break;
 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
        if (PICKUP_ITEM) {
 | 
					            case "timers":
 | 
				
			||||||
            for (const value of Object.values(PICKUP_ITEM)) {
 | 
					                for (const [category, data] of Object.entries(actionData as IStatsTimers)) {
 | 
				
			||||||
                playerStats.PickupCount ??= 0;
 | 
					                    switch (category) {
 | 
				
			||||||
                playerStats.PickupCount += value;
 | 
					                        case "EQUIP_WEAPON":
 | 
				
			||||||
            }
 | 
					                            playerStats.Weapons ??= [];
 | 
				
			||||||
        }
 | 
					                            for (const [type, equipTime] of Object.entries(data as IUploadEntry)) {
 | 
				
			||||||
 | 
					                                const weapon = playerStats.Weapons.find(element => element.type === type);
 | 
				
			||||||
 | 
					                                if (weapon) {
 | 
				
			||||||
 | 
					                                    weapon.equipTime ??= 0;
 | 
				
			||||||
 | 
					                                    weapon.equipTime += equipTime;
 | 
				
			||||||
 | 
					                                } else {
 | 
				
			||||||
 | 
					                                    playerStats.Weapons.push({ type: type, equipTime });
 | 
				
			||||||
 | 
					                                }
 | 
				
			||||||
 | 
					                            }
 | 
				
			||||||
 | 
					                            break;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        if (SCAN) {
 | 
					                        case "CURRENT_MISSION_TIME":
 | 
				
			||||||
            playerStats.Scans ??= [];
 | 
					                            playerStats.TimePlayedSec ??= 0;
 | 
				
			||||||
            for (const [key, scans] of Object.entries(SCAN)) {
 | 
					                            playerStats.TimePlayedSec += data;
 | 
				
			||||||
                const scan = playerStats.Scans.find(element => element.type === key);
 | 
					                            break;
 | 
				
			||||||
                if (scan) {
 | 
					
 | 
				
			||||||
                    scan.scans ??= 0;
 | 
					                        case "CIPHER_TIME":
 | 
				
			||||||
                    scan.scans += scans;
 | 
					                            playerStats.CipherTime ??= 0;
 | 
				
			||||||
                } else {
 | 
					                            playerStats.CipherTime += data;
 | 
				
			||||||
                    playerStats.Scans.push({ type: key, scans });
 | 
					                            break;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                        default:
 | 
				
			||||||
 | 
					                            if (!ignoredCategories.includes(category)) {
 | 
				
			||||||
 | 
					                                if (!unknownCategories[action]) {
 | 
				
			||||||
 | 
					                                    unknownCategories[action] = [];
 | 
				
			||||||
 | 
					                                }
 | 
				
			||||||
 | 
					                                unknownCategories[action].push(category);
 | 
				
			||||||
 | 
					                            }
 | 
				
			||||||
 | 
					                            break;
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
            }
 | 
					                break;
 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
        if (USE_ABILITY) {
 | 
					            case "max":
 | 
				
			||||||
            playerStats.Abilities ??= [];
 | 
					                for (const [category, data] of Object.entries(actionData as IStatsMax)) {
 | 
				
			||||||
            for (const [key, used] of Object.entries(USE_ABILITY)) {
 | 
					                    switch (category) {
 | 
				
			||||||
                const ability = playerStats.Abilities.find(element => element.type === key);
 | 
					                        case "WEAPON_XP":
 | 
				
			||||||
                if (ability) {
 | 
					                            playerStats.Weapons ??= [];
 | 
				
			||||||
                    ability.used ??= 0;
 | 
					                            for (const [type, xp] of Object.entries(data as IUploadEntry)) {
 | 
				
			||||||
                    ability.used += used;
 | 
					                                const weapon = playerStats.Weapons.find(element => element.type === type);
 | 
				
			||||||
                } else {
 | 
					                                if (weapon) {
 | 
				
			||||||
                    playerStats.Abilities.push({ type: key, used });
 | 
					                                    if (xp > (weapon.xp ?? 0)) {
 | 
				
			||||||
 | 
					                                        weapon.xp = xp;
 | 
				
			||||||
 | 
					                                    }
 | 
				
			||||||
 | 
					                                } else {
 | 
				
			||||||
 | 
					                                    playerStats.Weapons.push({ type: type, xp });
 | 
				
			||||||
 | 
					                                }
 | 
				
			||||||
 | 
					                            }
 | 
				
			||||||
 | 
					                            break;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                        case "MISSION_SCORE":
 | 
				
			||||||
 | 
					                            playerStats.Missions ??= [];
 | 
				
			||||||
 | 
					                            for (const [type, highScore] of Object.entries(data as IUploadEntry)) {
 | 
				
			||||||
 | 
					                                const mission = playerStats.Missions.find(element => element.type === type);
 | 
				
			||||||
 | 
					                                if (mission) {
 | 
				
			||||||
 | 
					                                    if (highScore > mission.highScore) {
 | 
				
			||||||
 | 
					                                        mission.highScore = highScore;
 | 
				
			||||||
 | 
					                                    }
 | 
				
			||||||
 | 
					                                } else {
 | 
				
			||||||
 | 
					                                    playerStats.Missions.push({ type: type, highScore });
 | 
				
			||||||
 | 
					                                }
 | 
				
			||||||
 | 
					                            }
 | 
				
			||||||
 | 
					                            break;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                        case "RACE_SCORE":
 | 
				
			||||||
 | 
					                            playerStats.Races ??= new Map();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                            for (const [race, highScore] of Object.entries(data as Record<string, number>)) {
 | 
				
			||||||
 | 
					                                const currentRace = playerStats.Races.get(race);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                                if (currentRace) {
 | 
				
			||||||
 | 
					                                    if (highScore > currentRace.highScore) {
 | 
				
			||||||
 | 
					                                        playerStats.Races.set(race, { highScore });
 | 
				
			||||||
 | 
					                                    }
 | 
				
			||||||
 | 
					                                } else {
 | 
				
			||||||
 | 
					                                    playerStats.Races.set(race, { highScore });
 | 
				
			||||||
 | 
					                                }
 | 
				
			||||||
 | 
					                            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                            break;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                        default:
 | 
				
			||||||
 | 
					                            if (!ignoredCategories.includes(category)) {
 | 
				
			||||||
 | 
					                                if (!unknownCategories[action]) {
 | 
				
			||||||
 | 
					                                    unknownCategories[action] = [];
 | 
				
			||||||
 | 
					                                }
 | 
				
			||||||
 | 
					                                unknownCategories[action].push(category);
 | 
				
			||||||
 | 
					                            }
 | 
				
			||||||
 | 
					                            break;
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
            }
 | 
					                break;
 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
        if (FIRE_WEAPON) {
 | 
					            case "set":
 | 
				
			||||||
            playerStats.Weapons ??= [];
 | 
					                for (const [category, value] of Object.entries(actionData as IStatsSet)) {
 | 
				
			||||||
            for (const [key, fired] of Object.entries(FIRE_WEAPON)) {
 | 
					                    switch (category) {
 | 
				
			||||||
                const weapon = playerStats.Weapons.find(element => element.type === key);
 | 
					                        case "ELO_RATING":
 | 
				
			||||||
                if (weapon) {
 | 
					                            playerStats.Rating = value;
 | 
				
			||||||
                    weapon.fired ??= 0;
 | 
					                            break;
 | 
				
			||||||
                    weapon.fired += fired;
 | 
					
 | 
				
			||||||
                } else {
 | 
					                        case "RANK":
 | 
				
			||||||
                    playerStats.Weapons.push({ type: key, fired });
 | 
					                            playerStats.Rank = value;
 | 
				
			||||||
 | 
					                            break;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                        case "PLAYER_LEVEL":
 | 
				
			||||||
 | 
					                            playerStats.PlayerLevel = value;
 | 
				
			||||||
 | 
					                            break;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                        default:
 | 
				
			||||||
 | 
					                            if (!ignoredCategories.includes(category)) {
 | 
				
			||||||
 | 
					                                if (!unknownCategories[action]) {
 | 
				
			||||||
 | 
					                                    unknownCategories[action] = [];
 | 
				
			||||||
 | 
					                                }
 | 
				
			||||||
 | 
					                                unknownCategories[action].push(category);
 | 
				
			||||||
 | 
					                            }
 | 
				
			||||||
 | 
					                            break;
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
            }
 | 
					                break;
 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
        if (HIT_ENTITY_ITEM) {
 | 
					            case "displayName":
 | 
				
			||||||
            playerStats.Weapons ??= [];
 | 
					            case "guildId":
 | 
				
			||||||
            for (const [key, hits] of Object.entries(HIT_ENTITY_ITEM)) {
 | 
					                break;
 | 
				
			||||||
                const weapon = playerStats.Weapons.find(element => element.type === key);
 | 
					 | 
				
			||||||
                if (weapon) {
 | 
					 | 
				
			||||||
                    weapon.hits ??= 0;
 | 
					 | 
				
			||||||
                    weapon.hits += hits;
 | 
					 | 
				
			||||||
                } else {
 | 
					 | 
				
			||||||
                    playerStats.Weapons.push({ type: key, hits });
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
        if (HEADSHOT_ITEM) {
 | 
					            default:
 | 
				
			||||||
            playerStats.Weapons ??= [];
 | 
					                logger.debug(`Unknown updateStats action: ${action}`);
 | 
				
			||||||
            for (const [key, headshots] of Object.entries(HEADSHOT_ITEM)) {
 | 
					                break;
 | 
				
			||||||
                const weapon = playerStats.Weapons.find(element => element.type === key);
 | 
					 | 
				
			||||||
                if (weapon) {
 | 
					 | 
				
			||||||
                    weapon.headshots ??= 0;
 | 
					 | 
				
			||||||
                    weapon.headshots += headshots;
 | 
					 | 
				
			||||||
                } else {
 | 
					 | 
				
			||||||
                    playerStats.Weapons.push({ type: key, headshots });
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        if (KILL_ENEMY_ITEM) {
 | 
					 | 
				
			||||||
            playerStats.Weapons ??= [];
 | 
					 | 
				
			||||||
            for (const [key, kills] of Object.entries(KILL_ENEMY_ITEM)) {
 | 
					 | 
				
			||||||
                const weapon = playerStats.Weapons.find(element => element.type === key);
 | 
					 | 
				
			||||||
                if (weapon) {
 | 
					 | 
				
			||||||
                    weapon.kills ??= 0;
 | 
					 | 
				
			||||||
                    weapon.kills += kills;
 | 
					 | 
				
			||||||
                } else {
 | 
					 | 
				
			||||||
                    playerStats.Weapons.push({ type: key, kills });
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        if (KILL_ENEMY) {
 | 
					 | 
				
			||||||
            playerStats.Enemies ??= [];
 | 
					 | 
				
			||||||
            for (const [key, kills] of Object.entries(KILL_ENEMY)) {
 | 
					 | 
				
			||||||
                const enemy = playerStats.Enemies.find(element => element.type === key);
 | 
					 | 
				
			||||||
                if (enemy) {
 | 
					 | 
				
			||||||
                    enemy.kills ??= 0;
 | 
					 | 
				
			||||||
                    enemy.kills += kills;
 | 
					 | 
				
			||||||
                } else {
 | 
					 | 
				
			||||||
                    playerStats.Enemies.push({ type: key, kills });
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        if (EXECUTE_ENEMY) {
 | 
					 | 
				
			||||||
            playerStats.Enemies ??= [];
 | 
					 | 
				
			||||||
            for (const [key, executions] of Object.entries(EXECUTE_ENEMY)) {
 | 
					 | 
				
			||||||
                const enemy = playerStats.Enemies.find(element => element.type === key);
 | 
					 | 
				
			||||||
                if (enemy) {
 | 
					 | 
				
			||||||
                    enemy.executions ??= 0;
 | 
					 | 
				
			||||||
                    enemy.executions += executions;
 | 
					 | 
				
			||||||
                } else {
 | 
					 | 
				
			||||||
                    playerStats.Enemies.push({ type: key, executions });
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        if (HEADSHOT) {
 | 
					 | 
				
			||||||
            playerStats.Enemies ??= [];
 | 
					 | 
				
			||||||
            for (const [key, headshots] of Object.entries(HEADSHOT)) {
 | 
					 | 
				
			||||||
                const enemy = playerStats.Enemies.find(element => element.type === key);
 | 
					 | 
				
			||||||
                if (enemy) {
 | 
					 | 
				
			||||||
                    enemy.headshots ??= 0;
 | 
					 | 
				
			||||||
                    enemy.headshots += headshots;
 | 
					 | 
				
			||||||
                } else {
 | 
					 | 
				
			||||||
                    playerStats.Enemies.push({ type: key, headshots });
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        if (DIE) {
 | 
					 | 
				
			||||||
            playerStats.Enemies ??= [];
 | 
					 | 
				
			||||||
            for (const [key, deaths] of Object.entries(DIE)) {
 | 
					 | 
				
			||||||
                playerStats.Deaths ??= 0;
 | 
					 | 
				
			||||||
                playerStats.Deaths += deaths;
 | 
					 | 
				
			||||||
                const enemy = playerStats.Enemies.find(element => element.type === key);
 | 
					 | 
				
			||||||
                if (enemy) {
 | 
					 | 
				
			||||||
                    enemy.deaths ??= 0;
 | 
					 | 
				
			||||||
                    enemy.deaths += deaths;
 | 
					 | 
				
			||||||
                } else {
 | 
					 | 
				
			||||||
                    playerStats.Enemies.push({ type: key, deaths });
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        if (MELEE_KILL) {
 | 
					 | 
				
			||||||
            playerStats.MeleeKills ??= 0;
 | 
					 | 
				
			||||||
            for (const kills of Object.values(MELEE_KILL)) {
 | 
					 | 
				
			||||||
                playerStats.MeleeKills += kills;
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        if (INCOME) {
 | 
					 | 
				
			||||||
            playerStats.Income ??= 0;
 | 
					 | 
				
			||||||
            playerStats.Income += INCOME;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        if (CIPHER) {
 | 
					 | 
				
			||||||
            if (CIPHER["0"] > 0) {
 | 
					 | 
				
			||||||
                playerStats.CiphersFailed ??= 0;
 | 
					 | 
				
			||||||
                playerStats.CiphersFailed += CIPHER["0"];
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
            if (CIPHER["1"] > 0) {
 | 
					 | 
				
			||||||
                playerStats.CiphersSolved ??= 0;
 | 
					 | 
				
			||||||
                playerStats.CiphersSolved += CIPHER["1"];
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if (payload.timers) {
 | 
					    for (const [action, categories] of Object.entries(unknownCategories)) {
 | 
				
			||||||
        const { EQUIP_WEAPON, CURRENT_MISSION_TIME, CIPHER_TIME } = payload.timers;
 | 
					        logger.debug(`Unknown updateStats ${action} action categories: ${categories.join(", ")}`);
 | 
				
			||||||
 | 
					 | 
				
			||||||
        if (EQUIP_WEAPON) {
 | 
					 | 
				
			||||||
            playerStats.Weapons ??= [];
 | 
					 | 
				
			||||||
            for (const [key, equipTime] of Object.entries(EQUIP_WEAPON)) {
 | 
					 | 
				
			||||||
                const weapon = playerStats.Weapons.find(element => element.type === key);
 | 
					 | 
				
			||||||
                if (weapon) {
 | 
					 | 
				
			||||||
                    weapon.equipTime ??= 0;
 | 
					 | 
				
			||||||
                    weapon.equipTime += equipTime;
 | 
					 | 
				
			||||||
                } else {
 | 
					 | 
				
			||||||
                    playerStats.Weapons.push({ type: key, equipTime });
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        if (CURRENT_MISSION_TIME) {
 | 
					 | 
				
			||||||
            playerStats.TimePlayedSec ??= 0;
 | 
					 | 
				
			||||||
            playerStats.TimePlayedSec += CURRENT_MISSION_TIME;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        if (CIPHER_TIME) {
 | 
					 | 
				
			||||||
            playerStats.CipherTime ??= 0;
 | 
					 | 
				
			||||||
            playerStats.CipherTime += CIPHER_TIME;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    if (payload.max) {
 | 
					 | 
				
			||||||
        const { WEAPON_XP, MISSION_SCORE } = payload.max;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        if (WEAPON_XP) {
 | 
					 | 
				
			||||||
            playerStats.Weapons ??= [];
 | 
					 | 
				
			||||||
            for (const [key, xp] of Object.entries(WEAPON_XP)) {
 | 
					 | 
				
			||||||
                const weapon = playerStats.Weapons.find(element => element.type === key);
 | 
					 | 
				
			||||||
                if (weapon) {
 | 
					 | 
				
			||||||
                    weapon.xp = xp;
 | 
					 | 
				
			||||||
                } else {
 | 
					 | 
				
			||||||
                    playerStats.Weapons.push({ type: key, xp });
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        if (MISSION_SCORE) {
 | 
					 | 
				
			||||||
            playerStats.Missions ??= [];
 | 
					 | 
				
			||||||
            for (const [key, highScore] of Object.entries(MISSION_SCORE)) {
 | 
					 | 
				
			||||||
                const mission = playerStats.Missions.find(element => element.type === key);
 | 
					 | 
				
			||||||
                if (mission) {
 | 
					 | 
				
			||||||
                    mission.highScore = highScore;
 | 
					 | 
				
			||||||
                } else {
 | 
					 | 
				
			||||||
                    playerStats.Missions.push({ type: key, highScore });
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    if (payload.set) {
 | 
					 | 
				
			||||||
        const { ELO_RATING, RANK, PLAYER_LEVEL } = payload.set;
 | 
					 | 
				
			||||||
        if (ELO_RATING) playerStats.Rating = ELO_RATING;
 | 
					 | 
				
			||||||
        if (RANK) playerStats.Rank = RANK;
 | 
					 | 
				
			||||||
        if (PLAYER_LEVEL) playerStats.PlayerLevel = PLAYER_LEVEL;
 | 
					 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    await playerStats.save();
 | 
					    await playerStats.save();
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const ignoredCategories = [
 | 
				
			||||||
 | 
					    //add action
 | 
				
			||||||
 | 
					    "MISSION_STARTED",
 | 
				
			||||||
 | 
					    "HOST_OS",
 | 
				
			||||||
 | 
					    "CPU_CORES",
 | 
				
			||||||
 | 
					    "CPU_MODEL",
 | 
				
			||||||
 | 
					    "CPU_VENDOR",
 | 
				
			||||||
 | 
					    "GPU_CLASS",
 | 
				
			||||||
 | 
					    "GFX_DRIVER",
 | 
				
			||||||
 | 
					    "GFX_RESOLUTION",
 | 
				
			||||||
 | 
					    "GFX_ASPECT",
 | 
				
			||||||
 | 
					    "GFX_WINDOW",
 | 
				
			||||||
 | 
					    "GPU_VENDOR",
 | 
				
			||||||
 | 
					    "GFX_HDR",
 | 
				
			||||||
 | 
					    "SPEAKER_COUNT",
 | 
				
			||||||
 | 
					    "MISSION_MATCHMAKING",
 | 
				
			||||||
 | 
					    "PLAYER_COUNT",
 | 
				
			||||||
 | 
					    "HOST_MIGRATION",
 | 
				
			||||||
 | 
					    "DESTROY_DECORATION",
 | 
				
			||||||
 | 
					    "MOVEMENT",
 | 
				
			||||||
 | 
					    "RECEIVE_UPGRADE",
 | 
				
			||||||
 | 
					    "EQUIP_COSMETIC",
 | 
				
			||||||
 | 
					    "EQUIP_UPGRADE",
 | 
				
			||||||
 | 
					    "MISSION_TYPE",
 | 
				
			||||||
 | 
					    "MISSION_FACTION",
 | 
				
			||||||
 | 
					    "MISSION_PLAYED",
 | 
				
			||||||
 | 
					    "MISSION_PLAYED_TIME",
 | 
				
			||||||
 | 
					    "CPU_CLOCK",
 | 
				
			||||||
 | 
					    "CPU_FEATURE",
 | 
				
			||||||
 | 
					    "RAM",
 | 
				
			||||||
 | 
					    "ADDR_SPACE",
 | 
				
			||||||
 | 
					    "GFX_SCALE",
 | 
				
			||||||
 | 
					    "LOGINS",
 | 
				
			||||||
 | 
					    "GPU_MODEL",
 | 
				
			||||||
 | 
					    "MEDALS_TOP",
 | 
				
			||||||
 | 
					    "STATS_TIMERS_RESET",
 | 
				
			||||||
 | 
					    "INPUT_ACTIVITY_TIME",
 | 
				
			||||||
 | 
					    "LOGINS_ITEM",
 | 
				
			||||||
 | 
					    "TAKE_DAMAGE",
 | 
				
			||||||
 | 
					    "SQUAD_KILL_ENEMY",
 | 
				
			||||||
 | 
					    "SQUAD_HEADSHOT",
 | 
				
			||||||
 | 
					    "SQUAD_MELEE_KILL",
 | 
				
			||||||
 | 
					    "MELEE_KILL_ITEM",
 | 
				
			||||||
 | 
					    "TAKE_DAMAGE_ITEM",
 | 
				
			||||||
 | 
					    "SQUAD_KILL_ENEMY_ITEM",
 | 
				
			||||||
 | 
					    "SQUAD_HEADSHOT_ITEM",
 | 
				
			||||||
 | 
					    "SQUAD_MELEE_KILL_ITEM",
 | 
				
			||||||
 | 
					    "PRE_DIE",
 | 
				
			||||||
 | 
					    "PRE_DIE_ITEM",
 | 
				
			||||||
 | 
					    "GEAR_USED",
 | 
				
			||||||
 | 
					    "DIE_ITEM",
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // timers action
 | 
				
			||||||
 | 
					    "IN_SHIP_TIME",
 | 
				
			||||||
 | 
					    "IN_SHIP_VIEW_TIME",
 | 
				
			||||||
 | 
					    "MISSION_LOAD_TIME",
 | 
				
			||||||
 | 
					    "MISSION_TIME",
 | 
				
			||||||
 | 
					    "REGION_TIME",
 | 
				
			||||||
 | 
					    "PLATFORM_TIME",
 | 
				
			||||||
 | 
					    "PRE_DIE_TIME",
 | 
				
			||||||
 | 
					    "VEHICLE_TIME"
 | 
				
			||||||
 | 
					];
 | 
				
			||||||
 | 
				
			|||||||
@ -1,6 +1,6 @@
 | 
				
			|||||||
import { Types } from "mongoose";
 | 
					import { Types } from "mongoose";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export interface IStatsView {
 | 
					export interface IStatsClient {
 | 
				
			||||||
    CiphersSolved?: number;
 | 
					    CiphersSolved?: number;
 | 
				
			||||||
    CiphersFailed?: number;
 | 
					    CiphersFailed?: number;
 | 
				
			||||||
    CipherTime?: number;
 | 
					    CipherTime?: number;
 | 
				
			||||||
@ -10,9 +10,11 @@ export interface IStatsView {
 | 
				
			|||||||
    MissionsCompleted?: number;
 | 
					    MissionsCompleted?: number;
 | 
				
			||||||
    MissionsQuit?: number;
 | 
					    MissionsQuit?: number;
 | 
				
			||||||
    MissionsFailed?: number;
 | 
					    MissionsFailed?: number;
 | 
				
			||||||
 | 
					    MissionsInterrupted?: number;
 | 
				
			||||||
 | 
					    MissionsDumped?: number;
 | 
				
			||||||
    TimePlayedSec?: number;
 | 
					    TimePlayedSec?: number;
 | 
				
			||||||
    PickupCount?: number;
 | 
					    PickupCount?: number;
 | 
				
			||||||
    Tutorial?: { [key: string]: ITutorial };
 | 
					    Tutorial?: Map<string, ITutorial>;
 | 
				
			||||||
    Abilities?: IAbility[];
 | 
					    Abilities?: IAbility[];
 | 
				
			||||||
    Rating?: number;
 | 
					    Rating?: number;
 | 
				
			||||||
    Income?: number;
 | 
					    Income?: number;
 | 
				
			||||||
@ -23,9 +25,10 @@ export interface IStatsView {
 | 
				
			|||||||
    Deaths?: number;
 | 
					    Deaths?: number;
 | 
				
			||||||
    HealCount?: number;
 | 
					    HealCount?: number;
 | 
				
			||||||
    ReviveCount?: number;
 | 
					    ReviveCount?: number;
 | 
				
			||||||
 | 
					    Races?: Map<string, IRace>;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export interface IStatsDatabase extends IStatsView {
 | 
					export interface IStatsDatabase extends IStatsClient {
 | 
				
			||||||
    accountOwnerId: Types.ObjectId;
 | 
					    accountOwnerId: Types.ObjectId;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -68,7 +71,11 @@ export interface IWeapon {
 | 
				
			|||||||
    fired?: number;
 | 
					    fired?: number;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export interface IStatsUpload {
 | 
					export interface IRace {
 | 
				
			||||||
 | 
					    highScore: number;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export interface IStatsUpdate {
 | 
				
			||||||
    displayName: string;
 | 
					    displayName: string;
 | 
				
			||||||
    guildId?: string;
 | 
					    guildId?: string;
 | 
				
			||||||
    PS?: string;
 | 
					    PS?: string;
 | 
				
			||||||
@ -128,6 +135,7 @@ export interface IUploadEntry {
 | 
				
			|||||||
export interface IStatsMax {
 | 
					export interface IStatsMax {
 | 
				
			||||||
    WEAPON_XP?: IUploadEntry;
 | 
					    WEAPON_XP?: IUploadEntry;
 | 
				
			||||||
    MISSION_SCORE?: IUploadEntry;
 | 
					    MISSION_SCORE?: IUploadEntry;
 | 
				
			||||||
 | 
					    RACE_SCORE?: IUploadEntry;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export interface IStatsSet {
 | 
					export interface IStatsSet {
 | 
				
			||||||
 | 
				
			|||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user