forked from OpenWF/SpaceNinjaServer
		
	feat: initial stats save (#884)
Closes #203 Reviewed-on: http://209.141.38.3/OpenWF/SpaceNinjaServer/pulls/884 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
							
								
									8175deb023
								
							
						
					
					
						commit
						13c68a75c1
					
				@ -6,6 +6,7 @@ import { Inventory } from "@/src/models/inventoryModels/inventoryModel";
 | 
				
			|||||||
import { Loadout } from "@/src/models/inventoryModels/loadoutModel";
 | 
					import { Loadout } from "@/src/models/inventoryModels/loadoutModel";
 | 
				
			||||||
import { PersonalRooms } from "@/src/models/personalRoomsModel";
 | 
					import { PersonalRooms } from "@/src/models/personalRoomsModel";
 | 
				
			||||||
import { Ship } from "@/src/models/shipModel";
 | 
					import { Ship } from "@/src/models/shipModel";
 | 
				
			||||||
 | 
					import { Stats } from "@/src/models/statsModel";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export const deleteAccountController: RequestHandler = async (req, res) => {
 | 
					export const deleteAccountController: RequestHandler = async (req, res) => {
 | 
				
			||||||
    const accountId = await getAccountIdForRequest(req);
 | 
					    const accountId = await getAccountIdForRequest(req);
 | 
				
			||||||
@ -15,7 +16,8 @@ export const deleteAccountController: RequestHandler = async (req, res) => {
 | 
				
			|||||||
        Inventory.deleteOne({ accountOwnerId: accountId }),
 | 
					        Inventory.deleteOne({ accountOwnerId: accountId }),
 | 
				
			||||||
        Loadout.deleteOne({ loadoutOwnerId: accountId }),
 | 
					        Loadout.deleteOne({ loadoutOwnerId: accountId }),
 | 
				
			||||||
        PersonalRooms.deleteOne({ personalRoomsOwnerId: accountId }),
 | 
					        PersonalRooms.deleteOne({ personalRoomsOwnerId: accountId }),
 | 
				
			||||||
        Ship.deleteOne({ ShipOwnerId: accountId })
 | 
					        Ship.deleteOne({ ShipOwnerId: accountId }),
 | 
				
			||||||
 | 
					        Stats.deleteOne({ accountOwnerId: accountId })
 | 
				
			||||||
    ]);
 | 
					    ]);
 | 
				
			||||||
    res.end();
 | 
					    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";
 | 
					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();
 | 
					    res.status(200).end();
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
@ -1,31 +1,33 @@
 | 
				
			|||||||
import { RequestHandler } from "express";
 | 
					import { RequestHandler } from "express";
 | 
				
			||||||
import { getAccountIdForRequest } from "@/src/services/loginService";
 | 
					import { getAccountIdForRequest } from "@/src/services/loginService";
 | 
				
			||||||
import { IStatsView } from "@/src/types/statTypes";
 | 
					 | 
				
			||||||
import { config } from "@/src/services/configService";
 | 
					import { config } from "@/src/services/configService";
 | 
				
			||||||
import allScans from "@/static/fixed_responses/allScans.json";
 | 
					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 { IStatsView } 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 responseJson: IStatsView = {};
 | 
					    const responseJson: IStatsView = playerStats.toJSON();
 | 
				
			||||||
    responseJson.Weapons = [];
 | 
					    responseJson.Weapons ??= [];
 | 
				
			||||||
    for (const item of inventory.XPInfo) {
 | 
					    for (const item of inventory.XPInfo) {
 | 
				
			||||||
        responseJson.Weapons.push({
 | 
					        const weaponIndex = responseJson.Weapons.findIndex(element => element.type == item.ItemType);
 | 
				
			||||||
            type: item.ItemType,
 | 
					        if (weaponIndex !== -1) {
 | 
				
			||||||
            xp: item.XP
 | 
					            responseJson.Weapons[weaponIndex].xp == item.XP;
 | 
				
			||||||
        });
 | 
					        } else {
 | 
				
			||||||
 | 
					            responseJson.Weapons.push({ type: item.ItemType, xp: item.XP });
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    if (config.unlockAllScans) {
 | 
					    if (config.unlockAllScans) {
 | 
				
			||||||
        const scans = new Set(allScans);
 | 
					        const scans = new Set(allScans);
 | 
				
			||||||
        for (const type of Object.keys(ExportEnemies.avatars)) {
 | 
					        for (const type of Object.keys(ExportEnemies.avatars)) {
 | 
				
			||||||
            if (!scans.has(type)) {
 | 
					            if (!scans.has(type)) scans.add(type);
 | 
				
			||||||
                scans.add(type);
 | 
					 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        }
 | 
					        responseJson.Scans ??= [];
 | 
				
			||||||
        responseJson.Scans = [];
 | 
					 | 
				
			||||||
        for (const type of scans) {
 | 
					        for (const type of scans) {
 | 
				
			||||||
            responseJson.Scans.push({ type: type, scans: 9999 });
 | 
					            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 { PersonalRooms } from "@/src/models/personalRoomsModel";
 | 
				
			||||||
import { Request } from "express";
 | 
					import { Request } from "express";
 | 
				
			||||||
import { config } from "@/src/services/configService";
 | 
					import { config } from "@/src/services/configService";
 | 
				
			||||||
 | 
					import { createStats } from "@/src/services/statsService";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export const isCorrectPassword = (requestPassword: string, databasePassword: string): boolean => {
 | 
					export const isCorrectPassword = (requestPassword: string, databasePassword: string): boolean => {
 | 
				
			||||||
    return requestPassword === databasePassword;
 | 
					    return requestPassword === databasePassword;
 | 
				
			||||||
@ -24,6 +25,7 @@ export const createAccount = async (accountData: IDatabaseAccount): Promise<IDat
 | 
				
			|||||||
        const shipId = await createShip(account._id);
 | 
					        const shipId = await createShip(account._id);
 | 
				
			||||||
        await createInventory(account._id, { loadOutPresetId: loadoutId, ship: shipId });
 | 
					        await createInventory(account._id, { loadOutPresetId: loadoutId, ship: shipId });
 | 
				
			||||||
        await createPersonalRooms(account._id, shipId);
 | 
					        await createPersonalRooms(account._id, shipId);
 | 
				
			||||||
 | 
					        await createStats(account._id.toString());
 | 
				
			||||||
        return account.toJSON();
 | 
					        return account.toJSON();
 | 
				
			||||||
    } catch (error) {
 | 
					    } catch (error) {
 | 
				
			||||||
        if (error instanceof 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 {
 | 
					export interface IStatsView {
 | 
				
			||||||
    CiphersSolved?: number;
 | 
					    CiphersSolved?: number;
 | 
				
			||||||
    CiphersFailed?: number;
 | 
					    CiphersFailed?: number;
 | 
				
			||||||
@ -23,28 +25,32 @@ export interface IStatsView {
 | 
				
			|||||||
    ReviveCount?: number;
 | 
					    ReviveCount?: number;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export interface IStatsDatabase extends IStatsView {
 | 
				
			||||||
 | 
					    accountOwnerId: Types.ObjectId;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export interface IAbility {
 | 
					export interface IAbility {
 | 
				
			||||||
    used: number;
 | 
					 | 
				
			||||||
    type: string;
 | 
					    type: string;
 | 
				
			||||||
 | 
					    used: number;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export interface IEnemy {
 | 
					export interface IEnemy {
 | 
				
			||||||
 | 
					    type: string;
 | 
				
			||||||
    executions?: number;
 | 
					    executions?: number;
 | 
				
			||||||
    headshots?: number;
 | 
					    headshots?: number;
 | 
				
			||||||
    kills?: number;
 | 
					    kills?: number;
 | 
				
			||||||
    type: string;
 | 
					 | 
				
			||||||
    assists?: number;
 | 
					    assists?: number;
 | 
				
			||||||
    deaths?: number;
 | 
					    deaths?: number;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export interface IMission {
 | 
					export interface IMission {
 | 
				
			||||||
    highScore: number;
 | 
					 | 
				
			||||||
    type: string;
 | 
					    type: string;
 | 
				
			||||||
 | 
					    highScore: number;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export interface IScan {
 | 
					export interface IScan {
 | 
				
			||||||
    scans: number;
 | 
					 | 
				
			||||||
    type: string;
 | 
					    type: string;
 | 
				
			||||||
 | 
					    scans: number;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export interface ITutorial {
 | 
					export interface ITutorial {
 | 
				
			||||||
@ -52,12 +58,91 @@ export interface ITutorial {
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export interface IWeapon {
 | 
					export interface IWeapon {
 | 
				
			||||||
 | 
					    type: string;
 | 
				
			||||||
    equipTime?: number;
 | 
					    equipTime?: number;
 | 
				
			||||||
    hits?: number;
 | 
					    hits?: number;
 | 
				
			||||||
    kills?: number;
 | 
					    kills?: number;
 | 
				
			||||||
    xp?: number;
 | 
					    xp?: number;
 | 
				
			||||||
    assists?: number;
 | 
					    assists?: number;
 | 
				
			||||||
    type: string;
 | 
					 | 
				
			||||||
    headshots?: number;
 | 
					    headshots?: number;
 | 
				
			||||||
    fired?: 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