feat: initial stats save #884
@ -5,6 +5,7 @@ import { Inventory } from "@/src/models/inventoryModels/inventoryModel";
 | 
			
		||||
import { Loadout } from "@/src/models/inventoryModels/loadoutModel";
 | 
			
		||||
import { PersonalRooms } from "@/src/models/personalRoomsModel";
 | 
			
		||||
import { Ship } from "@/src/models/shipModel";
 | 
			
		||||
import { Stats } from "@/src/models/statsModel";
 | 
			
		||||
 | 
			
		||||
export const deleteAccountController: RequestHandler = async (req, res) => {
 | 
			
		||||
    const accountId = await getAccountIdForRequest(req);
 | 
			
		||||
@ -13,7 +14,8 @@ export const deleteAccountController: RequestHandler = async (req, res) => {
 | 
			
		||||
        Inventory.deleteOne({ accountOwnerId: accountId }),
 | 
			
		||||
        Loadout.deleteOne({ loadoutOwnerId: accountId }),
 | 
			
		||||
        PersonalRooms.deleteOne({ personalRoomsOwnerId: accountId }),
 | 
			
		||||
        Ship.deleteOne({ ShipOwnerId: accountId })
 | 
			
		||||
        Ship.deleteOne({ ShipOwnerId: accountId }),
 | 
			
		||||
        Stats.deleteOne({ accountOwnerId: accountId })
 | 
			
		||||
    ]);
 | 
			
		||||
    res.end();
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
@ -1,6 +1,14 @@
 | 
			
		||||
import { getJSONfromString } from "@/src/helpers/stringHelpers";
 | 
			
		||||
import { getAccountIdForRequest } from "@/src/services/loginService";
 | 
			
		||||
import { getStats, uploadStats } from "@/src/services/statsService";
 | 
			
		||||
import { IStatsUpload } from "@/src/types/statTypes";
 | 
			
		||||
import { RequestHandler } from "express";
 | 
			
		||||
 | 
			
		||||
const uploadController: RequestHandler = (_req, res) => {
 | 
			
		||||
const uploadController: RequestHandler = async (req, res) => {
 | 
			
		||||
    const payload = getJSONfromString<IStatsUpload>(String(req.body));
 | 
			
		||||
    const accountId = await getAccountIdForRequest(req);
 | 
			
		||||
    const playerStats = await getStats(accountId);
 | 
			
		||||
    await uploadStats(playerStats, payload);
 | 
			
		||||
    res.status(200).end();
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -1,31 +1,33 @@
 | 
			
		||||
import { RequestHandler } from "express";
 | 
			
		||||
import { getAccountIdForRequest } from "@/src/services/loginService";
 | 
			
		||||
import { IStatsView } from "@/src/types/statTypes";
 | 
			
		||||
import { config } from "@/src/services/configService";
 | 
			
		||||
import allScans from "@/static/fixed_responses/allScans.json";
 | 
			
		||||
import { ExportEnemies } from "warframe-public-export-plus";
 | 
			
		||||
import { getInventory } from "@/src/services/inventoryService";
 | 
			
		||||
import { getStats } from "@/src/services/statsService";
 | 
			
		||||
import { IStatsView } from "@/src/types/statTypes";
 | 
			
		||||
 | 
			
		||||
const viewController: RequestHandler = async (req, res) => {
 | 
			
		||||
    const accountId = await getAccountIdForRequest(req);
 | 
			
		||||
    const inventory = await getInventory(accountId, "XPInfo");
 | 
			
		||||
    const playerStats = await getStats(accountId);
 | 
			
		||||
 | 
			
		||||
    const responseJson: IStatsView = {};
 | 
			
		||||
    responseJson.Weapons = [];
 | 
			
		||||
    const responseJson: IStatsView = playerStats.toJSON();
 | 
			
		||||
    responseJson.Weapons ??= [];
 | 
			
		||||
    for (const item of inventory.XPInfo) {
 | 
			
		||||
        responseJson.Weapons.push({
 | 
			
		||||
            type: item.ItemType,
 | 
			
		||||
            xp: item.XP
 | 
			
		||||
        });
 | 
			
		||||
        const weaponIndex = responseJson.Weapons.findIndex(element => element.type == item.ItemType);
 | 
			
		||||
        if (weaponIndex !== -1) {
 | 
			
		||||
            responseJson.Weapons[weaponIndex].xp == item.XP;
 | 
			
		||||
        } else {
 | 
			
		||||
            responseJson.Weapons.push({ type: item.ItemType, xp: item.XP });
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    if (config.unlockAllScans) {
 | 
			
		||||
        const scans = new Set(allScans);
 | 
			
		||||
        for (const type of Object.keys(ExportEnemies.avatars)) {
 | 
			
		||||
            if (!scans.has(type)) {
 | 
			
		||||
                scans.add(type);
 | 
			
		||||
            }
 | 
			
		||||
            if (!scans.has(type)) scans.add(type);
 | 
			
		||||
        }
 | 
			
		||||
        responseJson.Scans = [];
 | 
			
		||||
        responseJson.Scans ??= [];
 | 
			
		||||
        for (const type of scans) {
 | 
			
		||||
            responseJson.Scans.push({ type: type, scans: 9999 });
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										101
									
								
								src/models/statsModel.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										101
									
								
								src/models/statsModel.ts
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,101 @@
 | 
			
		||||
import { Document, Schema, Types, model } from "mongoose";
 | 
			
		||||
import { IEnemy, IMission, IScan, ITutorial, IAbility, IWeapon, IStatsDatabase } from "@/src/types/statTypes";
 | 
			
		||||
 | 
			
		||||
const abilitySchema = new Schema<IAbility>(
 | 
			
		||||
    {
 | 
			
		||||
        type: { type: String, required: true },
 | 
			
		||||
        used: Number
 | 
			
		||||
    },
 | 
			
		||||
    { _id: false }
 | 
			
		||||
);
 | 
			
		||||
 | 
			
		||||
const enemySchema = new Schema<IEnemy>(
 | 
			
		||||
    {
 | 
			
		||||
        type: { type: String, required: true },
 | 
			
		||||
        executions: Number,
 | 
			
		||||
        headshots: Number,
 | 
			
		||||
        kills: Number,
 | 
			
		||||
        assists: Number,
 | 
			
		||||
        deaths: Number
 | 
			
		||||
    },
 | 
			
		||||
    { _id: false }
 | 
			
		||||
);
 | 
			
		||||
 | 
			
		||||
const missionSchema = new Schema<IMission>(
 | 
			
		||||
    {
 | 
			
		||||
        type: { type: String, required: true },
 | 
			
		||||
        highScore: Number
 | 
			
		||||
    },
 | 
			
		||||
    { _id: false }
 | 
			
		||||
);
 | 
			
		||||
 | 
			
		||||
const scanSchema = new Schema<IScan>(
 | 
			
		||||
    {
 | 
			
		||||
        type: { type: String, required: true },
 | 
			
		||||
        scans: Number
 | 
			
		||||
    },
 | 
			
		||||
    { _id: false }
 | 
			
		||||
);
 | 
			
		||||
 | 
			
		||||
const tutorialSchema = new Schema<ITutorial>(
 | 
			
		||||
    {
 | 
			
		||||
        stage: Number
 | 
			
		||||
    },
 | 
			
		||||
    { _id: false }
 | 
			
		||||
);
 | 
			
		||||
 | 
			
		||||
const weaponSchema = new Schema<IWeapon>(
 | 
			
		||||
    {
 | 
			
		||||
        type: { type: String, required: true },
 | 
			
		||||
        equipTime: Number,
 | 
			
		||||
        hits: Number,
 | 
			
		||||
        kills: Number,
 | 
			
		||||
        xp: Number,
 | 
			
		||||
        assists: Number,
 | 
			
		||||
        headshots: Number,
 | 
			
		||||
        fired: Number
 | 
			
		||||
    },
 | 
			
		||||
    { _id: false }
 | 
			
		||||
);
 | 
			
		||||
 | 
			
		||||
const statsSchema = new Schema<IStatsDatabase>({
 | 
			
		||||
    accountOwnerId: { type: Schema.Types.ObjectId, required: true },
 | 
			
		||||
    CiphersSolved: Number,
 | 
			
		||||
    CiphersFailed: Number,
 | 
			
		||||
    CipherTime: Number,
 | 
			
		||||
    Weapons: { type: [weaponSchema], default: [] },
 | 
			
		||||
    Enemies: { type: [enemySchema], default: [] },
 | 
			
		||||
    MeleeKills: Number,
 | 
			
		||||
    MissionsCompleted: Number,
 | 
			
		||||
    MissionsQuit: Number,
 | 
			
		||||
    MissionsFailed: Number,
 | 
			
		||||
    TimePlayedSec: Number,
 | 
			
		||||
    PickupCount: Number,
 | 
			
		||||
    Tutorial: { type: Map, of: tutorialSchema, default: {} },
 | 
			
		||||
    Abilities: { type: [abilitySchema], default: [] },
 | 
			
		||||
    Rating: Number,
 | 
			
		||||
    Income: Number,
 | 
			
		||||
    Rank: Number,
 | 
			
		||||
    PlayerLevel: Number,
 | 
			
		||||
    Scans: { type: [scanSchema], default: [] },
 | 
			
		||||
    Missions: { type: [missionSchema], default: [] },
 | 
			
		||||
    Deaths: Number,
 | 
			
		||||
    HealCount: Number,
 | 
			
		||||
    ReviveCount: Number
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
statsSchema.set("toJSON", {
 | 
			
		||||
    transform(_document, returnedObject) {
 | 
			
		||||
        delete returnedObject._id;
 | 
			
		||||
        delete returnedObject.__v;
 | 
			
		||||
        delete returnedObject.accountOwnerId;
 | 
			
		||||
    }
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
export const Stats = model<IStatsDatabase>("Stats", statsSchema);
 | 
			
		||||
 | 
			
		||||
// eslint-disable-next-line @typescript-eslint/ban-types
 | 
			
		||||
export type TStatsDatabaseDocument = Document<unknown, {}, IStatsDatabase> & {
 | 
			
		||||
    _id: Types.ObjectId;
 | 
			
		||||
    __v: number;
 | 
			
		||||
} & IStatsDatabase;
 | 
			
		||||
@ -7,6 +7,7 @@ import { Loadout } from "@/src/models/inventoryModels/loadoutModel";
 | 
			
		||||
import { PersonalRooms } from "@/src/models/personalRoomsModel";
 | 
			
		||||
import { Request } from "express";
 | 
			
		||||
import { config } from "@/src/services/configService";
 | 
			
		||||
import { createStats } from "@/src/services/statsService";
 | 
			
		||||
 | 
			
		||||
export const isCorrectPassword = (requestPassword: string, databasePassword: string): boolean => {
 | 
			
		||||
    return requestPassword === databasePassword;
 | 
			
		||||
@ -24,6 +25,7 @@ export const createAccount = async (accountData: IDatabaseAccount): Promise<IDat
 | 
			
		||||
        const shipId = await createShip(account._id);
 | 
			
		||||
        await createInventory(account._id, { loadOutPresetId: loadoutId, ship: shipId });
 | 
			
		||||
        await createPersonalRooms(account._id, shipId);
 | 
			
		||||
        await createStats(account._id.toString());
 | 
			
		||||
        return account.toJSON();
 | 
			
		||||
    } catch (error) {
 | 
			
		||||
        if (error instanceof Error) {
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										283
									
								
								src/services/statsService.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										283
									
								
								src/services/statsService.ts
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,283 @@
 | 
			
		||||
import { Stats, TStatsDatabaseDocument } from "@/src/models/statsModel";
 | 
			
		||||
import { IStatsUpload } from "@/src/types/statTypes";
 | 
			
		||||
 | 
			
		||||
export const createStats = async (accountId: string): Promise<TStatsDatabaseDocument> => {
 | 
			
		||||
    const stats = new Stats({ accountOwnerId: accountId });
 | 
			
		||||
    await stats.save();
 | 
			
		||||
    return stats;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
export const getStats = async (accountOwnerId: string): Promise<TStatsDatabaseDocument> => {
 | 
			
		||||
    let stats = await Stats.findOne({ accountOwnerId: accountOwnerId });
 | 
			
		||||
 | 
			
		||||
    if (!stats) stats = await createStats(accountOwnerId);
 | 
			
		||||
 | 
			
		||||
    return stats;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
export const uploadStats = async (playerStats: TStatsDatabaseDocument, payload: IStatsUpload): Promise<void> => {
 | 
			
		||||
| 
					
	
	
	
	
	
	
	
	 | 
			||||
    if (payload.add) {
 | 
			
		||||
        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 [key, value] of Object.entries(MISSION_COMPLETE)) {
 | 
			
		||||
                switch (key) {
 | 
			
		||||
                    case "GS_SUCCESS":
 | 
			
		||||
                        playerStats.MissionsCompleted ??= 0;
 | 
			
		||||
                        playerStats.MissionsCompleted += value;
 | 
			
		||||
                        break;
 | 
			
		||||
                    case "GS_QUIT":
 | 
			
		||||
                        playerStats.MissionsQuit ??= 0;
 | 
			
		||||
                        playerStats.MissionsQuit += value;
 | 
			
		||||
                        break;
 | 
			
		||||
                    case "GS_FAILURE":
 | 
			
		||||
                        playerStats.MissionsFailed ??= 0;
 | 
			
		||||
                        playerStats.MissionsFailed += value;
 | 
			
		||||
                        break;
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (PICKUP_ITEM) {
 | 
			
		||||
            for (const value of Object.values(PICKUP_ITEM)) {
 | 
			
		||||
                playerStats.PickupCount ??= 0;
 | 
			
		||||
                playerStats.PickupCount += value;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (SCAN) {
 | 
			
		||||
            playerStats.Scans ??= [];
 | 
			
		||||
            for (const [key, scans] of Object.entries(SCAN)) {
 | 
			
		||||
                const scan = playerStats.Scans.find(element => element.type === key);
 | 
			
		||||
                if (scan) {
 | 
			
		||||
                    scan.scans ??= 0;
 | 
			
		||||
                    scan.scans += scans;
 | 
			
		||||
                } else {
 | 
			
		||||
                    playerStats.Scans.push({ type: key, scans });
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (USE_ABILITY) {
 | 
			
		||||
            playerStats.Abilities ??= [];
 | 
			
		||||
            for (const [key, used] of Object.entries(USE_ABILITY)) {
 | 
			
		||||
                const ability = playerStats.Abilities.find(element => element.type === key);
 | 
			
		||||
                if (ability) {
 | 
			
		||||
                    ability.used ??= 0;
 | 
			
		||||
                    ability.used += used;
 | 
			
		||||
                } else {
 | 
			
		||||
                    playerStats.Abilities.push({ type: key, used });
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (FIRE_WEAPON) {
 | 
			
		||||
            playerStats.Weapons ??= [];
 | 
			
		||||
            for (const [key, fired] of Object.entries(FIRE_WEAPON)) {
 | 
			
		||||
                const weapon = playerStats.Weapons.find(element => element.type === key);
 | 
			
		||||
                if (weapon) {
 | 
			
		||||
                    weapon.fired ??= 0;
 | 
			
		||||
                    weapon.fired += fired;
 | 
			
		||||
                } else {
 | 
			
		||||
                    playerStats.Weapons.push({ type: key, fired });
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (HIT_ENTITY_ITEM) {
 | 
			
		||||
            playerStats.Weapons ??= [];
 | 
			
		||||
            for (const [key, hits] of Object.entries(HIT_ENTITY_ITEM)) {
 | 
			
		||||
                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) {
 | 
			
		||||
            playerStats.Weapons ??= [];
 | 
			
		||||
            for (const [key, headshots] of Object.entries(HEADSHOT_ITEM)) {
 | 
			
		||||
                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) {
 | 
			
		||||
        const { EQUIP_WEAPON, CURRENT_MISSION_TIME, CIPHER_TIME } = payload.timers;
 | 
			
		||||
 | 
			
		||||
        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();
 | 
			
		||||
};
 | 
			
		||||
@ -1,3 +1,5 @@
 | 
			
		||||
import { Types } from "mongoose";
 | 
			
		||||
 | 
			
		||||
export interface IStatsView {
 | 
			
		||||
    CiphersSolved?: number;
 | 
			
		||||
    CiphersFailed?: number;
 | 
			
		||||
@ -23,28 +25,32 @@ export interface IStatsView {
 | 
			
		||||
    ReviveCount?: number;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export interface IStatsDatabase extends IStatsView {
 | 
			
		||||
| 
					
	
	
	
	
	
	
	
	 
				
					
						OrdisPrime
						commented  
			
		IStatsView is the Client version of IStatsDatabase right? IStatsView is the Client version of IStatsDatabase right?
It should be IStatsClient then 
			
			
		 | 
			||||
    accountOwnerId: Types.ObjectId;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export interface IAbility {
 | 
			
		||||
    used: number;
 | 
			
		||||
    type: string;
 | 
			
		||||
    used: number;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export interface IEnemy {
 | 
			
		||||
    type: string;
 | 
			
		||||
    executions?: number;
 | 
			
		||||
    headshots?: number;
 | 
			
		||||
    kills?: number;
 | 
			
		||||
    type: string;
 | 
			
		||||
    assists?: number;
 | 
			
		||||
    deaths?: number;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export interface IMission {
 | 
			
		||||
    highScore: number;
 | 
			
		||||
    type: string;
 | 
			
		||||
    highScore: number;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export interface IScan {
 | 
			
		||||
    scans: number;
 | 
			
		||||
    type: string;
 | 
			
		||||
    scans: number;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export interface ITutorial {
 | 
			
		||||
@ -52,12 +58,91 @@ export interface ITutorial {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export interface IWeapon {
 | 
			
		||||
    type: string;
 | 
			
		||||
    equipTime?: number;
 | 
			
		||||
    hits?: number;
 | 
			
		||||
    kills?: number;
 | 
			
		||||
    xp?: number;
 | 
			
		||||
    assists?: number;
 | 
			
		||||
    type: string;
 | 
			
		||||
    headshots?: number;
 | 
			
		||||
    fired?: number;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export interface IStatsUpload {
 | 
			
		||||
    displayName: string;
 | 
			
		||||
    guildId?: string;
 | 
			
		||||
    PS?: string;
 | 
			
		||||
    add?: IStatsAdd;
 | 
			
		||||
    set?: IStatsSet;
 | 
			
		||||
    max?: IStatsMax;
 | 
			
		||||
    timers?: IStatsTimers;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export interface IStatsAdd {
 | 
			
		||||
    GEAR_USED?: IUploadEntry;
 | 
			
		||||
    SCAN?: IUploadEntry;
 | 
			
		||||
    MISSION_COMPLETE?: IUploadEntry;
 | 
			
		||||
    HEADSHOT_ITEM?: IUploadEntry;
 | 
			
		||||
    HEADSHOT?: IUploadEntry;
 | 
			
		||||
    PLAYER_COUNT?: IUploadEntry;
 | 
			
		||||
    HOST_MIGRATION?: IUploadEntry;
 | 
			
		||||
    PICKUP_ITEM?: IUploadEntry;
 | 
			
		||||
    FIRE_WEAPON?: IUploadEntry;
 | 
			
		||||
    HIT_ENTITY_ITEM?: IUploadEntry;
 | 
			
		||||
    DESTROY_DECORATION?: IUploadEntry;
 | 
			
		||||
    KILL_ENEMY?: IUploadEntry;
 | 
			
		||||
    TAKE_DAMAGE?: IUploadEntry;
 | 
			
		||||
    SQUAD_KILL_ENEMY?: IUploadEntry;
 | 
			
		||||
    RECEIVE_UPGRADE?: IUploadEntry;
 | 
			
		||||
    USE_ABILITY?: IUploadEntry;
 | 
			
		||||
    SQUAD_VIP_KILL?: IUploadEntry;
 | 
			
		||||
    HEAL_BUDDY?: IUploadEntry;
 | 
			
		||||
    INCOME?: number;
 | 
			
		||||
    CIPHER?: IUploadEntry;
 | 
			
		||||
    EQUIP_COSMETIC?: IUploadEntry;
 | 
			
		||||
    EQUIP_UPGRADE?: IUploadEntry;
 | 
			
		||||
    KILL_BOSS?: IUploadEntry;
 | 
			
		||||
    MISSION_TYPE?: IUploadEntry;
 | 
			
		||||
    MISSION_FACTION?: IUploadEntry;
 | 
			
		||||
    MISSION_PLAYED?: IUploadEntry;
 | 
			
		||||
    MISSION_PLAYED_TIME?: IUploadEntry;
 | 
			
		||||
    MEDALS_TOP?: IUploadEntry;
 | 
			
		||||
    INPUT_ACTIVITY_TIME?: IUploadEntry;
 | 
			
		||||
    KILL_ENEMY_ITEM?: IUploadEntry;
 | 
			
		||||
    TAKE_DAMAGE_ITEM?: IUploadEntry;
 | 
			
		||||
    SQUAD_KILL_ENEMY_ITEM?: IUploadEntry;
 | 
			
		||||
    MELEE_KILL?: IUploadEntry;
 | 
			
		||||
    SQUAD_MELEE_KILL?: IUploadEntry;
 | 
			
		||||
    MELEE_KILL_ITEM?: IUploadEntry;
 | 
			
		||||
    SQUAD_MELEE_KILL_ITEM?: IUploadEntry;
 | 
			
		||||
    DIE?: IUploadEntry;
 | 
			
		||||
    DIE_ITEM?: IUploadEntry;
 | 
			
		||||
    EXECUTE_ENEMY?: IUploadEntry;
 | 
			
		||||
    EXECUTE_ENEMY_ITEM?: IUploadEntry;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export interface IUploadEntry {
 | 
			
		||||
    [key: string]: number;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export interface IStatsMax {
 | 
			
		||||
    WEAPON_XP?: IUploadEntry;
 | 
			
		||||
    MISSION_SCORE?: IUploadEntry;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export interface IStatsSet {
 | 
			
		||||
    ELO_RATING?: number;
 | 
			
		||||
    RANK?: number;
 | 
			
		||||
    PLAYER_LEVEL?: number;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export interface IStatsTimers {
 | 
			
		||||
    IN_SHIP_TIME?: number;
 | 
			
		||||
    IN_SHIP_VIEW_TIME?: IUploadEntry;
 | 
			
		||||
    EQUIP_WEAPON?: IUploadEntry;
 | 
			
		||||
    MISSION_TIME?: IUploadEntry;
 | 
			
		||||
    REGION_TIME?: IUploadEntry;
 | 
			
		||||
    PLATFORM_TIME?: IUploadEntry;
 | 
			
		||||
    CURRENT_MISSION_TIME?: number;
 | 
			
		||||
    CIPHER_TIME?: number;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user
	
the function does not upload right? lets rename it