feat: stats saving #490
@ -25,11 +25,11 @@ import { logger } from "@/src/utils/logger";
 | 
			
		||||
- [ ]  MissionTime
 | 
			
		||||
- [x]  Missions
 | 
			
		||||
- [ ]  CompletedAlerts
 | 
			
		||||
- [ ]  LastRegionPlayed
 | 
			
		||||
- [x]  LastRegionPlayed
 | 
			
		||||
- [ ]  GameModeId
 | 
			
		||||
- [ ]  hosts
 | 
			
		||||
- [x]  ChallengeProgress
 | 
			
		||||
- [ ]  SeasonChallengeHistory
 | 
			
		||||
- [x]  SeasonChallengeHistory
 | 
			
		||||
- [ ]  PS (Passive anti-cheat data which includes your username, module list, process list, and system name.)
 | 
			
		||||
- [ ]  ActiveDojoColorResearch
 | 
			
		||||
- [x]  RewardInfo
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										22
									
								
								src/controllers/api/updateNodeIntrosController.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										22
									
								
								src/controllers/api/updateNodeIntrosController.ts
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,22 @@
 | 
			
		||||
import { RequestHandler } from "express";
 | 
			
		||||
import { getAccountIdForRequest } from "@/src/services/loginService";
 | 
			
		||||
import { getJSONfromString } from "@/src/helpers/stringHelpers";
 | 
			
		||||
import { Inventory } from "@/src/models/inventoryModels/inventoryModel";
 | 
			
		||||
import { IUpdateNodeIntro } from "@/src/types/requestTypes";
 | 
			
		||||
 | 
			
		||||
// eslint-disable-next-line @typescript-eslint/no-misused-promises
 | 
			
		||||
const updateNodeIntrosController: RequestHandler = async (req, res) => {
 | 
			
		||||
    const accountId = await getAccountIdForRequest(req);
 | 
			
		||||
    const payload = getJSONfromString(String(req.body)) as IUpdateNodeIntro;
 | 
			
		||||
 | 
			
		||||
    // Update inventory
 | 
			
		||||
    const inventory = await Inventory.findOne({ accountOwnerId: accountId });
 | 
			
		||||
    if (inventory) {
 | 
			
		||||
        inventory.NodeIntrosCompleted.push(payload.NodeIntrosCompleted);
 | 
			
		||||
        await inventory.save();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    res.status(200).end();
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
export { updateNodeIntrosController };
 | 
			
		||||
@ -1,6 +1,14 @@
 | 
			
		||||
import { getJSONfromString } from "@/src/helpers/stringHelpers";
 | 
			
		||||
import { getAccountIdForRequest } from "@/src/services/loginService";
 | 
			
		||||
import { uploadStats } from "@/src/services/statsService";
 | 
			
		||||
import { IStatsUpload } from "@/src/types/statTypes";
 | 
			
		||||
import { RequestHandler } from "express";
 | 
			
		||||
 | 
			
		||||
const uploadController: RequestHandler = (_req, res) => {
 | 
			
		||||
// eslint-disable-next-line @typescript-eslint/no-misused-promises
 | 
			
		||||
const uploadController: RequestHandler = async (req, res) => {
 | 
			
		||||
    const payload = getJSONfromString(String(req.body)) as IStatsUpload;
 | 
			
		||||
    const accountId = await getAccountIdForRequest(req);
 | 
			
		||||
    await uploadStats(accountId, payload);
 | 
			
		||||
    res.status(200).end();
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -4,24 +4,37 @@ import { Inventory } from "@/src/models/inventoryModels/inventoryModel";
 | 
			
		||||
import { IStatsView } from "@/src/types/statTypes";
 | 
			
		||||
import { config } from "@/src/services/configService";
 | 
			
		||||
import allScans from "@/static/fixed_responses/allScans.json";
 | 
			
		||||
import Stat from "@/src/models/statsModel";
 | 
			
		||||
 | 
			
		||||
// eslint-disable-next-line @typescript-eslint/no-misused-promises
 | 
			
		||||
const viewController: RequestHandler = async (req, res) => {
 | 
			
		||||
    const accountId = await getAccountIdForRequest(req);
 | 
			
		||||
    const inventory = await Inventory.findOne({ accountOwnerId: accountId });
 | 
			
		||||
    let playerStats = await Stat.findOne({ accountOwnerId: accountId });
 | 
			
		||||
 | 
			
		||||
    if (!inventory) {
 | 
			
		||||
        res.status(400).json({ error: "inventory was undefined" });
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    const responseJson: IStatsView = {};
 | 
			
		||||
    responseJson.Weapons = [];
 | 
			
		||||
    if (!playerStats) {
 | 
			
		||||
        playerStats = new Stat({ accountOwnerId: accountId });
 | 
			
		||||
        await playerStats.save();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    const responseJson: IStatsView = playerStats;
 | 
			
		||||
    responseJson.Weapons ??= [];
 | 
			
		||||
    for (const item of inventory.XPInfo) {
 | 
			
		||||
        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) {
 | 
			
		||||
        responseJson.Scans = allScans;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										89
									
								
								src/models/statsModel.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										89
									
								
								src/models/statsModel.ts
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,89 @@
 | 
			
		||||
import { Schema, model } from "mongoose";
 | 
			
		||||
import { IEnemy, IMission, IScan, ITutorial, IAbility, IWeapon, IStatsDatabase } from "@/src/types/statTypes";
 | 
			
		||||
 | 
			
		||||
const abilitySchema = new Schema<IAbility>(
 | 
			
		||||
    {
 | 
			
		||||
        used: Number,
 | 
			
		||||
        type: { type: String, required: true }
 | 
			
		||||
    },
 | 
			
		||||
    { _id: false }
 | 
			
		||||
);
 | 
			
		||||
 | 
			
		||||
const enemySchema = new Schema<IEnemy>(
 | 
			
		||||
    {
 | 
			
		||||
        executions: Number,
 | 
			
		||||
        headshots: Number,
 | 
			
		||||
        kills: Number,
 | 
			
		||||
        type: { type: String, required: true },
 | 
			
		||||
        assists: Number,
 | 
			
		||||
        deaths: Number
 | 
			
		||||
    },
 | 
			
		||||
    { _id: false }
 | 
			
		||||
);
 | 
			
		||||
 | 
			
		||||
const missionSchema = new Schema<IMission>(
 | 
			
		||||
    {
 | 
			
		||||
        highScore: Number,
 | 
			
		||||
        type: { type: String, required: true }
 | 
			
		||||
    },
 | 
			
		||||
    { _id: false }
 | 
			
		||||
);
 | 
			
		||||
 | 
			
		||||
const scanSchema = new Schema<IScan>(
 | 
			
		||||
    {
 | 
			
		||||
        scans: Number,
 | 
			
		||||
        type: { type: String, required: true }
 | 
			
		||||
    },
 | 
			
		||||
    { _id: false }
 | 
			
		||||
);
 | 
			
		||||
 | 
			
		||||
const tutorialSchema = new Schema<ITutorial>(
 | 
			
		||||
    {
 | 
			
		||||
        stage: Number
 | 
			
		||||
    },
 | 
			
		||||
    { _id: false }
 | 
			
		||||
);
 | 
			
		||||
 | 
			
		||||
const weaponSchema = new Schema<IWeapon>(
 | 
			
		||||
    {
 | 
			
		||||
        equipTime: Number,
 | 
			
		||||
        hits: Number,
 | 
			
		||||
        kills: Number,
 | 
			
		||||
        xp: Number,
 | 
			
		||||
        assists: Number,
 | 
			
		||||
        type: { type: String, required: true },
 | 
			
		||||
        headshots: Number,
 | 
			
		||||
        fired: Number
 | 
			
		||||
    },
 | 
			
		||||
    { _id: false }
 | 
			
		||||
);
 | 
			
		||||
 | 
			
		||||
const statsSchema = new Schema<IStatsDatabase>({
 | 
			
		||||
    accountOwnerId: Schema.Types.ObjectId,
 | 
			
		||||
    CiphersSolved: Number,
 | 
			
		||||
    CiphersFailed: Number,
 | 
			
		||||
    CipherTime: Number,
 | 
			
		||||
    Weapons: [weaponSchema],
 | 
			
		||||
    Enemies: [enemySchema],
 | 
			
		||||
    MeleeKills: Number,
 | 
			
		||||
    MissionsCompleted: Number,
 | 
			
		||||
    MissionsQuit: Number,
 | 
			
		||||
    MissionsFailed: Number,
 | 
			
		||||
    TimePlayedSec: Number,
 | 
			
		||||
    PickupCount: Number,
 | 
			
		||||
    Tutorial: { type: Map, of: tutorialSchema },
 | 
			
		||||
    Abilities: [abilitySchema],
 | 
			
		||||
    Rating: Number,
 | 
			
		||||
    Income: Number,
 | 
			
		||||
    Rank: Number,
 | 
			
		||||
    PlayerLevel: Number,
 | 
			
		||||
    Scans: [scanSchema],
 | 
			
		||||
    Missions: [missionSchema],
 | 
			
		||||
    Deaths: Number,
 | 
			
		||||
    HealCount: Number,
 | 
			
		||||
    ReviveCount: Number
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
const Stats = model<IStatsDatabase>("Stats", statsSchema);
 | 
			
		||||
 | 
			
		||||
export default Stats;
 | 
			
		||||
@ -1,6 +1,6 @@
 | 
			
		||||
import express from "express";
 | 
			
		||||
import { addFriendImageController } from "@/src/controllers/api/addFriendImageController";
 | 
			
		||||
import { artifactsController } from "../controllers/api/artifactsController";
 | 
			
		||||
import { artifactsController } from "@/src/controllers/api/artifactsController";
 | 
			
		||||
import { checkDailyMissionBonusController } from "@/src/controllers/api/checkDailyMissionBonusController";
 | 
			
		||||
import { claimCompletedRecipeController } from "@/src/controllers/api/claimCompletedRecipeController";
 | 
			
		||||
import { createGuildController } from "@/src/controllers/api/createGuildController";
 | 
			
		||||
@ -17,13 +17,13 @@ import { getDailyDealStockLevelsController } from "@/src/controllers/api/getDail
 | 
			
		||||
import { getFriendsController } from "@/src/controllers/api/getFriendsController";
 | 
			
		||||
import { getGuildController } from "@/src/controllers/api/getGuildController";
 | 
			
		||||
import { getGuildDojoController } from "@/src/controllers/api/getGuildDojoController";
 | 
			
		||||
import { getGuildLogController } from "../controllers/api/getGuildLogController";
 | 
			
		||||
import { getGuildLogController } from "@/src/controllers/api/getGuildLogController";
 | 
			
		||||
import { getIgnoredUsersController } from "@/src/controllers/api/getIgnoredUsersController";
 | 
			
		||||
import { getNewRewardSeedController } from "@/src/controllers/api/getNewRewardSeedController";
 | 
			
		||||
import { getShipController } from "@/src/controllers/api/getShipController";
 | 
			
		||||
import { getVendorInfoController } from "@/src/controllers/api/getVendorInfoController";
 | 
			
		||||
import { gildWeaponController } from "@/src/controllers/api/gildWeaponController";
 | 
			
		||||
import { guildTechController } from "../controllers/api/guildTechController";
 | 
			
		||||
import { guildTechController } from "@/src/controllers/api/guildTechController";
 | 
			
		||||
import { hostSessionController } from "@/src/controllers/api/hostSessionController";
 | 
			
		||||
import { hubController } from "@/src/controllers/api/hubController";
 | 
			
		||||
import { hubInstancesController } from "@/src/controllers/api/hubInstancesController";
 | 
			
		||||
@ -40,7 +40,7 @@ import { missionInventoryUpdateController } from "@/src/controllers/api/missionI
 | 
			
		||||
import { modularWeaponCraftingController } from "@/src/controllers/api/modularWeaponCraftingController";
 | 
			
		||||
import { modularWeaponSaleController } from "@/src/controllers/api/modularWeaponSaleController";
 | 
			
		||||
import { nameWeaponController } from "@/src/controllers/api/nameWeaponController";
 | 
			
		||||
import { projectionManagerController } from "../controllers/api/projectionManagerController";
 | 
			
		||||
import { projectionManagerController } from "@/src/controllers/api/projectionManagerController";
 | 
			
		||||
import { purchaseController } from "@/src/controllers/api/purchaseController";
 | 
			
		||||
import { queueDojoComponentDestructionController } from "@/src/controllers/api/queueDojoComponentDestructionController";
 | 
			
		||||
import { rerollRandomModController } from "@/src/controllers/api/rerollRandomModController";
 | 
			
		||||
@ -51,19 +51,20 @@ import { setActiveShipController } from "@/src/controllers/api/setActiveShipCont
 | 
			
		||||
import { setBootLocationController } from "@/src/controllers/api/setBootLocationController";
 | 
			
		||||
import { setShipCustomizationsController } from "@/src/controllers/api/setShipCustomizationsController";
 | 
			
		||||
import { setSupportedSyndicateController } from "@/src/controllers/api/setSupportedSyndicateController";
 | 
			
		||||
import { setWeaponSkillTreeController } from "../controllers/api/setWeaponSkillTreeController";
 | 
			
		||||
import { setWeaponSkillTreeController } from "@/src/controllers/api/setWeaponSkillTreeController";
 | 
			
		||||
import { shipDecorationsController } from "@/src/controllers/api/shipDecorationsController";
 | 
			
		||||
import { startDojoRecipeController } from "@/src/controllers/api/startDojoRecipeController";
 | 
			
		||||
import { startRecipeController } from "@/src/controllers/api/startRecipeController";
 | 
			
		||||
import { stepSequencersController } from "@/src/controllers/api/stepSequencersController";
 | 
			
		||||
import { surveysController } from "@/src/controllers/api/surveysController";
 | 
			
		||||
import { syndicateSacrificeController } from "../controllers/api/syndicateSacrificeController";
 | 
			
		||||
import { syndicateSacrificeController } from "@/src/controllers/api/syndicateSacrificeController";
 | 
			
		||||
import { tauntHistoryController } from "@/src/controllers/api/tauntHistoryController";
 | 
			
		||||
import { trainingResultController } from "@/src/controllers/api/trainingResultController";
 | 
			
		||||
import { updateChallengeProgressController } from "@/src/controllers/api/updateChallengeProgressController";
 | 
			
		||||
import { updateSessionGetController, updateSessionPostController } from "@/src/controllers/api/updateSessionController";
 | 
			
		||||
import { updateThemeController } from "../controllers/api/updateThemeController";
 | 
			
		||||
import { updateThemeController } from "@/src/controllers/api/updateThemeController";
 | 
			
		||||
import { upgradesController } from "@/src/controllers/api/upgradesController";
 | 
			
		||||
import { updateNodeIntrosController } from "@/src/controllers/api/updateNodeIntrosController";
 | 
			
		||||
 | 
			
		||||
const apiRouter = express.Router();
 | 
			
		||||
 | 
			
		||||
@ -137,5 +138,6 @@ apiRouter.post("/updateChallengeProgress.php", updateChallengeProgressController
 | 
			
		||||
apiRouter.post("/updateSession.php", updateSessionPostController);
 | 
			
		||||
apiRouter.post("/updateTheme.php", updateThemeController);
 | 
			
		||||
apiRouter.post("/upgrades.php", upgradesController);
 | 
			
		||||
apiRouter.post("/updateNodeIntros.php", updateNodeIntrosController);
 | 
			
		||||
 | 
			
		||||
export { apiRouter };
 | 
			
		||||
 | 
			
		||||
@ -675,7 +675,7 @@ export const updateChallengeProgress = async (challenges: IUpdateChallengeProgre
 | 
			
		||||
    const inventory = await getInventory(accountId);
 | 
			
		||||
 | 
			
		||||
    addChallenges(inventory, challenges.ChallengeProgress);
 | 
			
		||||
    addSeasonalChallengeHistory(inventory, challenges.SeasonChallengeHistory);
 | 
			
		||||
    addSeasonalChallengeHistory(inventory, challenges.SeasonChallengeCompletions);
 | 
			
		||||
 | 
			
		||||
    await inventory.save();
 | 
			
		||||
};
 | 
			
		||||
@ -700,14 +700,15 @@ export const addSeasonalChallengeHistory = (
 | 
			
		||||
export const addChallenges = (inventory: IInventoryDatabaseDocument, itemsArray: IChallengeProgress[] | undefined) => {
 | 
			
		||||
    const category = inventory.ChallengeProgress;
 | 
			
		||||
 | 
			
		||||
    itemsArray?.forEach(({ Name, Progress }) => {
 | 
			
		||||
    itemsArray?.forEach(({ Name, Progress, Completed }) => {
 | 
			
		||||
        const itemIndex = category.findIndex(i => i.Name === Name);
 | 
			
		||||
 | 
			
		||||
        if (itemIndex !== -1) {
 | 
			
		||||
            category[itemIndex].Progress += Progress;
 | 
			
		||||
            inventory.markModified(`ChallengeProgress.${itemIndex}.ItemCount`);
 | 
			
		||||
            category[itemIndex].Progress = Progress;
 | 
			
		||||
            category[itemIndex].Completed = Completed;
 | 
			
		||||
            inventory.markModified(`ChallengeProgress.${itemIndex}.Progress`);
 | 
			
		||||
        } else {
 | 
			
		||||
            category.push({ Name, Progress });
 | 
			
		||||
            category.push({ Name, Progress, Completed });
 | 
			
		||||
        }
 | 
			
		||||
    });
 | 
			
		||||
};
 | 
			
		||||
@ -724,6 +725,13 @@ const addMissionComplete = (inventory: IInventoryDatabaseDocument, { Tag, Comple
 | 
			
		||||
    }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
const addDeathMarks = (inventory: IInventoryDatabaseDocument, marks: string[] | undefined) => {
 | 
			
		||||
    const { DeathMarks } = inventory;
 | 
			
		||||
    marks?.forEach(mark => {
 | 
			
		||||
        if (!DeathMarks.find(element => element === mark)) DeathMarks.push(mark);
 | 
			
		||||
    });
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
export const missionInventoryUpdate = async (data: IMissionInventoryUpdateRequest, accountId: string) => {
 | 
			
		||||
    const {
 | 
			
		||||
        RawUpgrades,
 | 
			
		||||
@ -734,7 +742,8 @@ export const missionInventoryUpdate = async (data: IMissionInventoryUpdateReques
 | 
			
		||||
        Consumables,
 | 
			
		||||
        Recipes,
 | 
			
		||||
        Missions,
 | 
			
		||||
        FusionTreasures
 | 
			
		||||
        FusionTreasures,
 | 
			
		||||
        DeathMarks
 | 
			
		||||
    } = data;
 | 
			
		||||
    const inventory = await getInventory(accountId);
 | 
			
		||||
 | 
			
		||||
@ -796,6 +805,7 @@ export const missionInventoryUpdate = async (data: IMissionInventoryUpdateReques
 | 
			
		||||
    if (Missions) {
 | 
			
		||||
        addMissionComplete(inventory, Missions);
 | 
			
		||||
    }
 | 
			
		||||
    addDeathMarks(inventory, DeathMarks);
 | 
			
		||||
 | 
			
		||||
    const changedInventory = await inventory.save();
 | 
			
		||||
    return changedInventory.toJSON();
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										273
									
								
								src/services/statsService.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										273
									
								
								src/services/statsService.ts
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,273 @@
 | 
			
		||||
import Stats from "@/src/models/statsModel";
 | 
			
		||||
import { IStatsUpload } from "@/src/types/statTypes";
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
    Missing in stats update
 | 
			
		||||
 | 
			
		||||
    Weapons?: IWeapon[]; // assists
 | 
			
		||||
    Enemies?: IEnemy[]; // assists
 | 
			
		||||
    Tutorial?: { [key: string]: ITutorial };
 | 
			
		||||
    Missions?: IMission[];
 | 
			
		||||
    HealCount?: number;
 | 
			
		||||
    ReviveCount?: number;
 | 
			
		||||
*/
 | 
			
		||||
 | 
			
		||||
export const uploadStats = async (accountId: string, payload: IStatsUpload): Promise<void> => {
 | 
			
		||||
    let playerStats = await Stats.findOne({ accountOwnerId: accountId });
 | 
			
		||||
    if (!playerStats) {
 | 
			
		||||
        playerStats = new Stats({ accountOwnerId: accountId });
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    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) {
 | 
			
		||||
            for (const [key, scans] of Object.entries(SCAN)) {
 | 
			
		||||
                playerStats.Scans ??= [];
 | 
			
		||||
                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) {
 | 
			
		||||
            for (const [key, used] of Object.entries(USE_ABILITY)) {
 | 
			
		||||
                playerStats.Abilities ??= [];
 | 
			
		||||
                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) {
 | 
			
		||||
            for (const [key, fired] of Object.entries(FIRE_WEAPON)) {
 | 
			
		||||
                playerStats.Weapons ??= [];
 | 
			
		||||
                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) {
 | 
			
		||||
            for (const [key, hits] of Object.entries(HIT_ENTITY_ITEM)) {
 | 
			
		||||
                playerStats.Weapons ??= [];
 | 
			
		||||
                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) {
 | 
			
		||||
            for (const [key, headshots] of Object.entries(HEADSHOT_ITEM)) {
 | 
			
		||||
                playerStats.Weapons ??= [];
 | 
			
		||||
                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) {
 | 
			
		||||
            for (const [key, kills] of Object.entries(KILL_ENEMY_ITEM)) {
 | 
			
		||||
                playerStats.Weapons ??= [];
 | 
			
		||||
                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) {
 | 
			
		||||
            for (const [key, kills] of Object.entries(KILL_ENEMY)) {
 | 
			
		||||
                playerStats.Enemies ??= [];
 | 
			
		||||
                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) {
 | 
			
		||||
            for (const [key, executions] of Object.entries(EXECUTE_ENEMY)) {
 | 
			
		||||
                playerStats.Enemies ??= [];
 | 
			
		||||
                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) {
 | 
			
		||||
            for (const [key, headshots] of Object.entries(HEADSHOT)) {
 | 
			
		||||
                playerStats.Enemies ??= [];
 | 
			
		||||
                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) {
 | 
			
		||||
            for (const [key, deaths] of Object.entries(DIE)) {
 | 
			
		||||
                playerStats.Deaths ??= 0;
 | 
			
		||||
                playerStats.Deaths += deaths;
 | 
			
		||||
                playerStats.Enemies ??= [];
 | 
			
		||||
                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) {
 | 
			
		||||
            for (const [key, equipTime] of Object.entries(EQUIP_WEAPON)) {
 | 
			
		||||
                playerStats.Weapons ??= [];
 | 
			
		||||
                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 } = payload.max;
 | 
			
		||||
        if (WEAPON_XP) {
 | 
			
		||||
            for (const [key, xp] of Object.entries(WEAPON_XP)) {
 | 
			
		||||
                playerStats.Weapons ??= [];
 | 
			
		||||
                const weapon = playerStats.Weapons.find(element => element.type === key);
 | 
			
		||||
                if (weapon) {
 | 
			
		||||
                    weapon.xp ??= 0;
 | 
			
		||||
                    weapon.xp += xp;
 | 
			
		||||
                } else {
 | 
			
		||||
                    playerStats.Weapons.push({ type: key, xp });
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    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();
 | 
			
		||||
};
 | 
			
		||||
@ -284,7 +284,7 @@ export interface IInventoryResponse {
 | 
			
		||||
    Robotics: any[];
 | 
			
		||||
    UsedDailyDeals: any[];
 | 
			
		||||
    LibraryPersonalProgress: ILibraryPersonalProgress[];
 | 
			
		||||
    CollectibleSeries: ICollectibleSery[];
 | 
			
		||||
    CollectibleSeries: ICollectibleSeries[];
 | 
			
		||||
    LibraryAvailableDailyTaskInfo: ILibraryAvailableDailyTaskInfo;
 | 
			
		||||
    HasResetAccount: boolean;
 | 
			
		||||
    PendingCoupon: IPendingCoupon;
 | 
			
		||||
@ -326,10 +326,10 @@ export interface IParam {
 | 
			
		||||
export interface IChallengeProgress {
 | 
			
		||||
    Progress: number;
 | 
			
		||||
    Name: string;
 | 
			
		||||
    Completed?: string[];
 | 
			
		||||
    Completed: string[];
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export interface ICollectibleSery {
 | 
			
		||||
export interface ICollectibleSeries {
 | 
			
		||||
    CollectibleType: string;
 | 
			
		||||
    Count: number;
 | 
			
		||||
    Tracking: string;
 | 
			
		||||
 | 
			
		||||
@ -70,6 +70,7 @@ export interface IMissionInventoryUpdateRequest {
 | 
			
		||||
    Missions?: IMission;
 | 
			
		||||
    EvolutionProgress?: IEvolutionProgress[];
 | 
			
		||||
    LastRegionPlayed?: TSolarMapRegion;
 | 
			
		||||
    DeathMarks?: string[];
 | 
			
		||||
 | 
			
		||||
    FusionPoints?: number; // Not a part of the request, but we put it in this struct as an intermediate storage.
 | 
			
		||||
}
 | 
			
		||||
@ -114,3 +115,8 @@ export interface IUpgradeOperation {
 | 
			
		||||
    PolarizeValue: ArtifactPolarity;
 | 
			
		||||
    PolarityRemap: IPolarity[];
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export interface IUpdateNodeIntro {
 | 
			
		||||
    NodeIntrosCompleted: string;
 | 
			
		||||
    crossPlaySetting?: string;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -1,3 +1,5 @@
 | 
			
		||||
import { Types } from "mongoose";
 | 
			
		||||
 | 
			
		||||
export interface IStatsView {
 | 
			
		||||
    CiphersSolved?: number;
 | 
			
		||||
    CiphersFailed?: number;
 | 
			
		||||
@ -23,6 +25,10 @@ export interface IStatsView {
 | 
			
		||||
    ReviveCount?: number;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export interface IStatsDatabase extends IStatsView {
 | 
			
		||||
    accountOwnerId: Types.ObjectId;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export interface IAbility {
 | 
			
		||||
    used: number;
 | 
			
		||||
    type: string;
 | 
			
		||||
@ -61,3 +67,81 @@ export interface IWeapon {
 | 
			
		||||
    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;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
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