feat: getProfileViewingData for players
	
		
			
	
		
	
	
		
	
		
			All checks were successful
		
		
	
	
		
			
				
	
				Build / build (20) (pull_request) Successful in 1m5s
				
			
		
			
				
	
				Build / build (22) (pull_request) Successful in 38s
				
			
		
			
				
	
				Build / build (20) (push) Successful in 39s
				
			
		
			
				
	
				Build / build (22) (push) Successful in 1m5s
				
			
		
			
				
	
				Build / build (18) (pull_request) Successful in 40s
				
			
		
			
				
	
				Build / build (18) (push) Successful in 1m19s
				
			
		
		
	
	
				
					
				
			
		
			All checks were successful
		
		
	
	Build / build (20) (pull_request) Successful in 1m5s
				
			Build / build (22) (pull_request) Successful in 38s
				
			Build / build (20) (push) Successful in 39s
				
			Build / build (22) (push) Successful in 1m5s
				
			Build / build (18) (pull_request) Successful in 40s
				
			Build / build (18) (push) Successful in 1m19s
				
			This commit is contained in:
		
							parent
							
								
									b761ff1bff
								
							
						
					
					
						commit
						c88b92b89c
					
				@ -32,6 +32,7 @@ app.use(requestLogger);
 | 
				
			|||||||
app.use("/api", apiRouter);
 | 
					app.use("/api", apiRouter);
 | 
				
			||||||
app.use("/", cacheRouter);
 | 
					app.use("/", cacheRouter);
 | 
				
			||||||
app.use("/custom", customRouter);
 | 
					app.use("/custom", customRouter);
 | 
				
			||||||
 | 
					app.use("/dynamic", dynamicController);
 | 
				
			||||||
app.use("/:id/dynamic", dynamicController);
 | 
					app.use("/:id/dynamic", dynamicController);
 | 
				
			||||||
app.use("/pay", payRouter);
 | 
					app.use("/pay", payRouter);
 | 
				
			||||||
app.use("/stats", statsRouter);
 | 
					app.use("/stats", statsRouter);
 | 
				
			||||||
 | 
				
			|||||||
@ -297,7 +297,7 @@ const resourceGetParent = (resourceName: string): string | undefined => {
 | 
				
			|||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// This is FNV1a-32 except operating under modulus 2^31 because JavaScript is stinky and likes producing negative integers out of nowhere.
 | 
					// This is FNV1a-32 except operating under modulus 2^31 because JavaScript is stinky and likes producing negative integers out of nowhere.
 | 
				
			||||||
const catBreadHash = (name: string): number => {
 | 
					export const catBreadHash = (name: string): number => {
 | 
				
			||||||
    let hash = 2166136261;
 | 
					    let hash = 2166136261;
 | 
				
			||||||
    for (let i = 0; i != name.length; ++i) {
 | 
					    for (let i = 0; i != name.length; ++i) {
 | 
				
			||||||
        hash = (hash ^ name.charCodeAt(i)) & 0x7fffffff;
 | 
					        hash = (hash ^ name.charCodeAt(i)) & 0x7fffffff;
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										183
									
								
								src/controllers/dynamic/getProfileViewingDataController.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										183
									
								
								src/controllers/dynamic/getProfileViewingDataController.ts
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,183 @@
 | 
				
			|||||||
 | 
					import { toMongoDate, toOid } from "@/src/helpers/inventoryHelpers";
 | 
				
			||||||
 | 
					import { Guild } from "@/src/models/guildModel";
 | 
				
			||||||
 | 
					import { Inventory, TInventoryDatabaseDocument } from "@/src/models/inventoryModels/inventoryModel";
 | 
				
			||||||
 | 
					import { Loadout } from "@/src/models/inventoryModels/loadoutModel";
 | 
				
			||||||
 | 
					import { Account } from "@/src/models/loginModel";
 | 
				
			||||||
 | 
					import { Stats, TStatsDatabaseDocument } from "@/src/models/statsModel";
 | 
				
			||||||
 | 
					import { allDailyAffiliationKeys } from "@/src/services/inventoryService";
 | 
				
			||||||
 | 
					import { IMongoDate, IOid } from "@/src/types/commonTypes";
 | 
				
			||||||
 | 
					import { IEquipmentClient } from "@/src/types/inventoryTypes/commonInventoryTypes";
 | 
				
			||||||
 | 
					import {
 | 
				
			||||||
 | 
					    IAffiliation,
 | 
				
			||||||
 | 
					    IAlignment,
 | 
				
			||||||
 | 
					    IChallengeProgress,
 | 
				
			||||||
 | 
					    IDailyAffiliations,
 | 
				
			||||||
 | 
					    ILoadoutConfigClient,
 | 
				
			||||||
 | 
					    IMission,
 | 
				
			||||||
 | 
					    IPlayerSkills,
 | 
				
			||||||
 | 
					    ITypeXPItem
 | 
				
			||||||
 | 
					} from "@/src/types/inventoryTypes/inventoryTypes";
 | 
				
			||||||
 | 
					import { RequestHandler } from "express";
 | 
				
			||||||
 | 
					import { catBreadHash } from "../api/inventoryController";
 | 
				
			||||||
 | 
					import { ExportCustoms } from "warframe-public-export-plus";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export const getProfileViewingDataController: RequestHandler = async (req, res) => {
 | 
				
			||||||
 | 
					    if (!req.query.playerId) {
 | 
				
			||||||
 | 
					        res.status(400).end();
 | 
				
			||||||
 | 
					        return;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    const account = await Account.findOne({ _id: req.query.playerId as string }, "DisplayName");
 | 
				
			||||||
 | 
					    if (!account) {
 | 
				
			||||||
 | 
					        res.status(400).send("No account or guild ID specified");
 | 
				
			||||||
 | 
					        return;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    const inventory = (await Inventory.findOne({ accountOwnerId: account._id }))!;
 | 
				
			||||||
 | 
					    const loadout = (await Loadout.findOne({ _id: inventory.LoadOutPresets }, "NORMAL"))!;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    const result: IPlayerProfileViewingDataResult = {
 | 
				
			||||||
 | 
					        AccountId: toOid(account._id),
 | 
				
			||||||
 | 
					        DisplayName: account.DisplayName,
 | 
				
			||||||
 | 
					        PlayerLevel: inventory.PlayerLevel,
 | 
				
			||||||
 | 
					        LoadOutInventory: {
 | 
				
			||||||
 | 
					            WeaponSkins: [],
 | 
				
			||||||
 | 
					            XPInfo: inventory.XPInfo
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					        PlayerSkills: inventory.PlayerSkills,
 | 
				
			||||||
 | 
					        ChallengeProgress: inventory.ChallengeProgress,
 | 
				
			||||||
 | 
					        DeathMarks: inventory.DeathMarks,
 | 
				
			||||||
 | 
					        Harvestable: inventory.Harvestable,
 | 
				
			||||||
 | 
					        DeathSquadable: inventory.DeathSquadable,
 | 
				
			||||||
 | 
					        Created: toMongoDate(inventory.Created),
 | 
				
			||||||
 | 
					        MigratedToConsole: false,
 | 
				
			||||||
 | 
					        Missions: inventory.Missions,
 | 
				
			||||||
 | 
					        Affiliations: inventory.Affiliations,
 | 
				
			||||||
 | 
					        DailyFocus: inventory.DailyFocus,
 | 
				
			||||||
 | 
					        Wishlist: inventory.Wishlist,
 | 
				
			||||||
 | 
					        Alignment: inventory.Alignment
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					    if (inventory.CurrentLoadOutIds.length) {
 | 
				
			||||||
 | 
					        result.LoadOutPreset = loadout.NORMAL.id(inventory.CurrentLoadOutIds[0].$oid)!.toJSON<ILoadoutConfigClient>();
 | 
				
			||||||
 | 
					        result.LoadOutPreset.ItemId = undefined;
 | 
				
			||||||
 | 
					        const skins = new Set<string>();
 | 
				
			||||||
 | 
					        if (result.LoadOutPreset.s) {
 | 
				
			||||||
 | 
					            result.LoadOutInventory.Suits = [
 | 
				
			||||||
 | 
					                inventory.Suits.id(result.LoadOutPreset.s.ItemId.$oid)!.toJSON<IEquipmentClient>()
 | 
				
			||||||
 | 
					            ];
 | 
				
			||||||
 | 
					            resolveAndCollectSkins(inventory, skins, result.LoadOutInventory.Suits[0]);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        if (result.LoadOutPreset.p) {
 | 
				
			||||||
 | 
					            result.LoadOutInventory.Pistols = [
 | 
				
			||||||
 | 
					                inventory.Pistols.id(result.LoadOutPreset.p.ItemId.$oid)!.toJSON<IEquipmentClient>()
 | 
				
			||||||
 | 
					            ];
 | 
				
			||||||
 | 
					            resolveAndCollectSkins(inventory, skins, result.LoadOutInventory.Pistols[0]);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        if (result.LoadOutPreset.l) {
 | 
				
			||||||
 | 
					            result.LoadOutInventory.LongGuns = [
 | 
				
			||||||
 | 
					                inventory.LongGuns.id(result.LoadOutPreset.l.ItemId.$oid)!.toJSON<IEquipmentClient>()
 | 
				
			||||||
 | 
					            ];
 | 
				
			||||||
 | 
					            resolveAndCollectSkins(inventory, skins, result.LoadOutInventory.LongGuns[0]);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        if (result.LoadOutPreset.m) {
 | 
				
			||||||
 | 
					            result.LoadOutInventory.Melee = [
 | 
				
			||||||
 | 
					                inventory.Melee.id(result.LoadOutPreset.m.ItemId.$oid)!.toJSON<IEquipmentClient>()
 | 
				
			||||||
 | 
					            ];
 | 
				
			||||||
 | 
					            resolveAndCollectSkins(inventory, skins, result.LoadOutInventory.Melee[0]);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        for (const skin of skins) {
 | 
				
			||||||
 | 
					            result.LoadOutInventory.WeaponSkins.push({ ItemType: skin });
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    if (inventory.GuildId) {
 | 
				
			||||||
 | 
					        const guild = (await Guild.findOne({ _id: inventory.GuildId }, "Name Tier XP Class"))!;
 | 
				
			||||||
 | 
					        result.GuildId = toOid(inventory.GuildId);
 | 
				
			||||||
 | 
					        result.GuildName = guild.Name;
 | 
				
			||||||
 | 
					        result.GuildTier = guild.Tier;
 | 
				
			||||||
 | 
					        result.GuildXp = guild.XP;
 | 
				
			||||||
 | 
					        result.GuildClass = guild.Class;
 | 
				
			||||||
 | 
					        result.GuildEmblem = false;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    for (const key of allDailyAffiliationKeys) {
 | 
				
			||||||
 | 
					        result[key] = inventory[key];
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    const stats = (await Stats.findOne({ accountOwnerId: account._id }))!.toJSON<Partial<TStatsDatabaseDocument>>();
 | 
				
			||||||
 | 
					    delete stats._id;
 | 
				
			||||||
 | 
					    delete stats.__v;
 | 
				
			||||||
 | 
					    delete stats.accountOwnerId;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    res.json({
 | 
				
			||||||
 | 
					        Results: [result],
 | 
				
			||||||
 | 
					        TechProjects: [],
 | 
				
			||||||
 | 
					        XpComponents: [],
 | 
				
			||||||
 | 
					        //XpCacheExpiryDate, some IMongoDate in the future, no clue what it's for
 | 
				
			||||||
 | 
					        Stats: stats
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					interface IPlayerProfileViewingDataResult extends Partial<IDailyAffiliations> {
 | 
				
			||||||
 | 
					    AccountId: IOid;
 | 
				
			||||||
 | 
					    DisplayName: string;
 | 
				
			||||||
 | 
					    PlayerLevel: number;
 | 
				
			||||||
 | 
					    LoadOutPreset?: Omit<ILoadoutConfigClient, "ItemId"> & { ItemId?: IOid };
 | 
				
			||||||
 | 
					    LoadOutInventory: {
 | 
				
			||||||
 | 
					        WeaponSkins: { ItemType: string }[];
 | 
				
			||||||
 | 
					        Suits?: IEquipmentClient[];
 | 
				
			||||||
 | 
					        Pistols?: IEquipmentClient[];
 | 
				
			||||||
 | 
					        LongGuns?: IEquipmentClient[];
 | 
				
			||||||
 | 
					        Melee?: IEquipmentClient[];
 | 
				
			||||||
 | 
					        XPInfo: ITypeXPItem[];
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					    GuildId?: IOid;
 | 
				
			||||||
 | 
					    GuildName?: string;
 | 
				
			||||||
 | 
					    GuildTier?: number;
 | 
				
			||||||
 | 
					    GuildXp?: number;
 | 
				
			||||||
 | 
					    GuildClass?: number;
 | 
				
			||||||
 | 
					    GuildEmblem?: boolean;
 | 
				
			||||||
 | 
					    PlayerSkills: IPlayerSkills;
 | 
				
			||||||
 | 
					    ChallengeProgress: IChallengeProgress[];
 | 
				
			||||||
 | 
					    DeathMarks: string[];
 | 
				
			||||||
 | 
					    Harvestable: boolean;
 | 
				
			||||||
 | 
					    DeathSquadable: boolean;
 | 
				
			||||||
 | 
					    Created: IMongoDate;
 | 
				
			||||||
 | 
					    MigratedToConsole: boolean;
 | 
				
			||||||
 | 
					    Missions: IMission[];
 | 
				
			||||||
 | 
					    Affiliations: IAffiliation[];
 | 
				
			||||||
 | 
					    DailyFocus: number;
 | 
				
			||||||
 | 
					    Wishlist: string[];
 | 
				
			||||||
 | 
					    Alignment?: IAlignment;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					let skinLookupTable: Record<number, string> | undefined;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const resolveAndCollectSkins = (
 | 
				
			||||||
 | 
					    inventory: TInventoryDatabaseDocument,
 | 
				
			||||||
 | 
					    skins: Set<string>,
 | 
				
			||||||
 | 
					    item: IEquipmentClient
 | 
				
			||||||
 | 
					): void => {
 | 
				
			||||||
 | 
					    for (const config of item.Configs) {
 | 
				
			||||||
 | 
					        if (config.Skins) {
 | 
				
			||||||
 | 
					            for (let i = 0; i != config.Skins.length; ++i) {
 | 
				
			||||||
 | 
					                // Resolve oids to type names
 | 
				
			||||||
 | 
					                if (config.Skins[i].length == 24) {
 | 
				
			||||||
 | 
					                    if (config.Skins[i].substring(0, 16) == "ca70ca70ca70ca70") {
 | 
				
			||||||
 | 
					                        if (!skinLookupTable) {
 | 
				
			||||||
 | 
					                            skinLookupTable = {};
 | 
				
			||||||
 | 
					                            for (const key of Object.keys(ExportCustoms)) {
 | 
				
			||||||
 | 
					                                skinLookupTable[catBreadHash(key)] = key;
 | 
				
			||||||
 | 
					                            }
 | 
				
			||||||
 | 
					                        }
 | 
				
			||||||
 | 
					                        config.Skins[i] = skinLookupTable[parseInt(config.Skins[i].substring(16), 16)];
 | 
				
			||||||
 | 
					                    } else {
 | 
				
			||||||
 | 
					                        const skinItem = inventory.WeaponSkins.id(config.Skins[i]);
 | 
				
			||||||
 | 
					                        config.Skins[i] = skinItem ? skinItem.ItemType : "";
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                // Collect type names
 | 
				
			||||||
 | 
					                if (config.Skins[i]) {
 | 
				
			||||||
 | 
					                    skins.add(config.Skins[i]);
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
@ -153,6 +153,7 @@ const guildSchema = new Schema<IGuildDatabase>(
 | 
				
			|||||||
        LongMOTD: { type: longMOTDSchema, default: undefined },
 | 
					        LongMOTD: { type: longMOTDSchema, default: undefined },
 | 
				
			||||||
        Ranks: { type: [guildRankSchema], default: defaultRanks },
 | 
					        Ranks: { type: [guildRankSchema], default: defaultRanks },
 | 
				
			||||||
        TradeTax: { type: Number, default: 0 },
 | 
					        TradeTax: { type: Number, default: 0 },
 | 
				
			||||||
 | 
					        Tier: { type: Number, default: 1 },
 | 
				
			||||||
        DojoComponents: { type: [dojoComponentSchema], default: [] },
 | 
					        DojoComponents: { type: [dojoComponentSchema], default: [] },
 | 
				
			||||||
        DojoCapacity: { type: Number, default: 100 },
 | 
					        DojoCapacity: { type: Number, default: 100 },
 | 
				
			||||||
        DojoEnergy: { type: Number, default: 5 },
 | 
					        DojoEnergy: { type: Number, default: 5 },
 | 
				
			||||||
 | 
				
			|||||||
@ -468,7 +468,6 @@ const seasonChallengeHistorySchema = new Schema<ISeasonChallenge>(
 | 
				
			|||||||
//TODO: check whether this is complete
 | 
					//TODO: check whether this is complete
 | 
				
			||||||
const playerSkillsSchema = new Schema<IPlayerSkills>(
 | 
					const playerSkillsSchema = new Schema<IPlayerSkills>(
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        LPP_NONE: { type: Number, default: 0 },
 | 
					 | 
				
			||||||
        LPP_SPACE: { type: Number, default: 0 },
 | 
					        LPP_SPACE: { type: Number, default: 0 },
 | 
				
			||||||
        LPS_PILOTING: { type: Number, default: 0 },
 | 
					        LPS_PILOTING: { type: Number, default: 0 },
 | 
				
			||||||
        LPS_GUNNERY: { type: Number, default: 0 },
 | 
					        LPS_GUNNERY: { type: Number, default: 0 },
 | 
				
			||||||
 | 
				
			|||||||
@ -1,10 +1,12 @@
 | 
				
			|||||||
import { aggregateSessionsController } from "@/src/controllers/dynamic/aggregateSessionsController";
 | 
					 | 
				
			||||||
import { worldStateController } from "@/src/controllers/dynamic/worldStateController";
 | 
					 | 
				
			||||||
import express from "express";
 | 
					import express from "express";
 | 
				
			||||||
 | 
					import { aggregateSessionsController } from "@/src/controllers/dynamic/aggregateSessionsController";
 | 
				
			||||||
 | 
					import { getProfileViewingDataController } from "@/src/controllers/dynamic/getProfileViewingDataController";
 | 
				
			||||||
 | 
					import { worldStateController } from "@/src/controllers/dynamic/worldStateController";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const dynamicController = express.Router();
 | 
					const dynamicController = express.Router();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
dynamicController.get("/worldState.php", worldStateController);
 | 
					 | 
				
			||||||
dynamicController.get("/aggregateSessions.php", aggregateSessionsController);
 | 
					dynamicController.get("/aggregateSessions.php", aggregateSessionsController);
 | 
				
			||||||
 | 
					dynamicController.get("/getProfileViewingData.php", getProfileViewingDataController);
 | 
				
			||||||
 | 
					dynamicController.get("/worldState.php", worldStateController);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export { dynamicController };
 | 
					export { dynamicController };
 | 
				
			||||||
 | 
				
			|||||||
@ -91,7 +91,7 @@ export const getGuildClient = async (guild: TGuildDatabaseDocument, accountId: s
 | 
				
			|||||||
        Members: members,
 | 
					        Members: members,
 | 
				
			||||||
        Ranks: guild.Ranks,
 | 
					        Ranks: guild.Ranks,
 | 
				
			||||||
        TradeTax: guild.TradeTax,
 | 
					        TradeTax: guild.TradeTax,
 | 
				
			||||||
        Tier: 1,
 | 
					        Tier: guild.Tier,
 | 
				
			||||||
        Vault: getGuildVault(guild),
 | 
					        Vault: getGuildVault(guild),
 | 
				
			||||||
        ActiveDojoColorResearch: guild.ActiveDojoColorResearch,
 | 
					        ActiveDojoColorResearch: guild.ActiveDojoColorResearch,
 | 
				
			||||||
        Class: guild.Class,
 | 
					        Class: guild.Class,
 | 
				
			||||||
 | 
				
			|||||||
@ -27,6 +27,7 @@ export interface IGuildDatabase {
 | 
				
			|||||||
    LongMOTD?: ILongMOTD;
 | 
					    LongMOTD?: ILongMOTD;
 | 
				
			||||||
    Ranks: IGuildRank[];
 | 
					    Ranks: IGuildRank[];
 | 
				
			||||||
    TradeTax: number;
 | 
					    TradeTax: number;
 | 
				
			||||||
 | 
					    Tier: number;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    DojoComponents: IDojoComponentDatabase[];
 | 
					    DojoComponents: IDojoComponentDatabase[];
 | 
				
			||||||
    DojoCapacity: number;
 | 
					    DojoCapacity: number;
 | 
				
			||||||
 | 
				
			|||||||
@ -937,7 +937,6 @@ export interface IPersonalTechProject {
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export interface IPlayerSkills {
 | 
					export interface IPlayerSkills {
 | 
				
			||||||
    LPP_NONE: number;
 | 
					 | 
				
			||||||
    LPP_SPACE: number;
 | 
					    LPP_SPACE: number;
 | 
				
			||||||
    LPS_PILOTING: number;
 | 
					    LPS_PILOTING: number;
 | 
				
			||||||
    LPS_GUNNERY: number;
 | 
					    LPS_GUNNERY: number;
 | 
				
			||||||
 | 
				
			|||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user