返回提交历史
Modified
src/controllers/api/inventoryController.ts
+8
-16
Modified
src/controllers/dynamic/getProfileViewingDataController.ts
+72
-28
XFEstudio/XFESpaceNinjaServer
chore: convert dates & oids in profile viewing data for pre-U19.5 (#3921)
Closes #3915 Reviewed-on: https://onlyg.it/OpenWF/SpaceNinjaServer/pulls/3921 Co-authored-by: Sainan <63328889+Sainan@users.noreply.github.com> Co-committed-by: Sainan <63328889+Sainan@users.noreply.github.com>
74e06da5
代码差异
2 个文件
+80
-44
@@ -889,25 +889,17 @@ export const getInventoryResponse = async (
889
889
if (category in inventoryResponse.LoadOutPresets) {
890
890
for (const item of inventoryResponse.LoadOutPresets[category]) {
891
891
toLegacyOid(item.ItemId);
892
if (item.s) {
893
if (item.s.ItemId) {
894
toLegacyOid(item.s.ItemId);
895
}
892
if (item.s?.ItemId) {
893
toLegacyOid(item.s.ItemId);
896
894
}
897
if (item.l) {
898
if (item.l.ItemId) {
899
toLegacyOid(item.l.ItemId);
900
}
895
if (item.l?.ItemId) {
896
toLegacyOid(item.l.ItemId);
901
897
}
902
if (item.p) {
903
if (item.p.ItemId) {
904
toLegacyOid(item.p.ItemId);
905
}
898
if (item.p?.ItemId) {
899
toLegacyOid(item.p.ItemId);
906
900
}
907
if (item.m) {
908
if (item.m.ItemId) {
909
toLegacyOid(item.m.ItemId);
910
}
901
if (item.m?.ItemId) {
902
toLegacyOid(item.m.ItemId);
911
903
}
912
904
}
913
905
}
@@ -1,4 +1,4 @@
1
import { fromOid, toMongoDate, toOid } from "../../helpers/inventoryHelpers.ts";
1
import { fromOid, toLegacyOid, toMongoDate2, toOid2, version_compare } from "../../helpers/inventoryHelpers.ts";
2
2
import type { TGuildDatabaseDocument } from "../../models/guildModel.ts";
3
3
import { Guild, GuildMember } from "../../models/guildModel.ts";
4
4
import type { TInventoryDatabaseDocument } from "../../models/inventoryModels/inventoryModel.ts";
@@ -7,7 +7,7 @@ import { Loadout } from "../../models/inventoryModels/loadoutModel.ts";
7
7
import { Account } from "../../models/loginModel.ts";
8
8
import { Stats } from "../../models/statsModel.ts";
9
9
import { allDailyAffiliationKeys } from "../../services/inventoryService.ts";
10
import type { IMongoDate, IOid } from "../../types/commonTypes.ts";
10
import type { IMongoDateWithLegacySupport, IOid, IOidWithLegacySupport } from "../../types/commonTypes.ts";
11
11
import type {
12
12
IAffiliation,
13
13
IAlignment,
@@ -28,6 +28,8 @@ import type { IEquipmentClient } from "../../types/equipmentTypes.ts";
28
28
import type { ILoadoutConfigClient } from "../../types/saveLoadoutTypes.ts";
29
29
import { skinLookupTable } from "../../helpers/skinLookupTable.ts";
30
30
import type { ITechProjectClient } from "../../types/guildTypes.ts";
31
import { getAccountForRequest } from "../../services/loginService.ts";
32
import gameToBuildVersion from "../../constants/gameToBuildVersion.ts";
31
33
32
34
export const getProfileViewingDataGetController: RequestHandler = async (req, res) => {
33
35
if (req.query.playerId) {
@@ -53,8 +55,17 @@ export const getProfileViewingDataGetController: RequestHandler = async (req, re
53
55
type IGetProfileViewingDataRequest = { AccountId: string } | { GuildId: string };
54
56
export const getProfileViewingDataPostController: RequestHandler = async (req, res) => {
55
57
const payload = getJSONfromString<IGetProfileViewingDataRequest>(String(req.body));
56
if ("GuildId" in payload) {
57
const data = await getProfileViewingDataByGuildId(payload.GuildId);
58
if ("AccountId" in payload) {
59
const account = await getAccountForRequest(req);
60
const data = await getProfileViewingDataByPlayerId(payload.AccountId, account.BuildLabel);
61
if (data) {
62
res.json(data);
63
} else {
64
res.status(409).send("Could not find requested account");
65
}
66
} else if ("GuildId" in payload) {
67
const account = await getAccountForRequest(req);
68
const data = await getProfileViewingDataByGuildId(payload.GuildId, account.BuildLabel);
58
69
if (data) {
59
70
res.json(data);
60
71
} else {
@@ -62,7 +73,7 @@ export const getProfileViewingDataPostController: RequestHandler = async (req, r
62
73
}
63
74
} else {
64
75
const playerId = req.query.playerId as string; // companion app sends a POST request should be handled like a GET request
65
const data = await getProfileViewingDataByPlayerId(playerId ? playerId : payload.AccountId);
76
const data = await getProfileViewingDataByPlayerId(playerId);
66
77
if (data) {
67
78
res.json(data);
68
79
} else {
@@ -71,7 +82,10 @@ export const getProfileViewingDataPostController: RequestHandler = async (req, r
71
82
}
72
83
};
73
84
74
export const getProfileViewingDataByPlayerId = async (playerId: string): Promise<IProfileViewingData | undefined> => {
85
const getProfileViewingDataByPlayerId = async (
86
playerId: string,
87
buildLabel?: string
88
): Promise<IProfileViewingData | undefined> => {
75
89
const account = await Account.findById(playerId, "DisplayName");
76
90
if (!account) {
77
91
return;
@@ -79,7 +93,7 @@ export const getProfileViewingDataByPlayerId = async (playerId: string): Promise
79
93
const inventory = (await Inventory.findOne({ accountOwnerId: account._id }))!;
80
94
81
95
const result: IPlayerProfileViewingDataResult = {
82
AccountId: toOid(account._id),
96
AccountId: toOid2(account._id, buildLabel),
83
97
DisplayName: account.DisplayName,
84
98
PlayerLevel: (inventory.spoofMasteryRank ?? -1) !== -1 ? inventory.spoofMasteryRank! : inventory.PlayerLevel,
85
99
LoadOutInventory: {
@@ -91,7 +105,7 @@ export const getProfileViewingDataByPlayerId = async (playerId: string): Promise
91
105
DeathMarks: inventory.DeathMarks,
92
106
Harvestable: inventory.Harvestable,
93
107
DeathSquadable: inventory.DeathSquadable,
94
Created: toMongoDate(inventory.Created),
108
Created: toMongoDate2(inventory.Created, buildLabel),
95
109
TitleType: inventory.TitleType,
96
110
MigratedToConsole: false,
97
111
Missions: inventory.Missions,
@@ -106,10 +120,10 @@ export const getProfileViewingDataByPlayerId = async (playerId: string): Promise
106
120
Partner: inventory.Partner,
107
121
Accolades: inventory.Accolades
108
122
};
109
await populateLoadout(inventory, result);
123
await populateLoadout(inventory, result, buildLabel);
110
124
if (inventory.GuildId) {
111
125
const guild = (await Guild.findById(inventory.GuildId, "Name Tier XP Class Emblem"))!;
112
populateGuild(guild, result);
126
populateGuild(guild, result, buildLabel);
113
127
}
114
128
for (const key of allDailyAffiliationKeys) {
115
129
result[key] = inventory[key];
@@ -124,7 +138,10 @@ export const getProfileViewingDataByPlayerId = async (playerId: string): Promise
124
138
};
125
139
};
126
140
127
export const getProfileViewingDataByGuildId = async (guildId: string): Promise<IProfileViewingData | undefined> => {
141
export const getProfileViewingDataByGuildId = async (
142
guildId: string,
143
buildLabel?: string
144
): Promise<IProfileViewingData | undefined> => {
128
145
const guild = await Guild.findById(guildId, "Name Tier XP Class Emblem TechProjects ClaimedXP");
129
146
if (!guild) {
130
147
return;
@@ -141,7 +158,7 @@ export const getProfileViewingDataByGuildId = async (guildId: string): Promise<I
141
158
)
142
159
]);
143
160
const result: IPlayerProfileViewingDataResult = {
144
AccountId: toOid(account!._id),
161
AccountId: toOid2(account!._id, buildLabel),
145
162
DisplayName: account!.DisplayName,
146
163
PlayerLevel:
147
164
(inventory!.spoofMasteryRank ?? -1) !== -1 ? inventory!.spoofMasteryRank! : inventory!.PlayerLevel,
@@ -150,10 +167,10 @@ export const getProfileViewingDataByGuildId = async (guildId: string): Promise<I
150
167
XPInfo: inventory!.XPInfo
151
168
}
152
169
};
153
await populateLoadout(inventory!, result);
170
await populateLoadout(inventory!, result, buildLabel);
154
171
results.push(result);
155
172
}
156
populateGuild(guild, results[0]);
173
populateGuild(guild, results[0], buildLabel);
157
174
158
175
const combinedStats: IStatsClient = {};
159
176
const statsArr = await Stats.find({ accountOwnerId: { $in: members.map(x => x.accountId) } }).lean(); // need this as POJO so Object.entries works as expected
@@ -212,7 +229,7 @@ export const getProfileViewingDataByGuildId = async (guildId: string): Promise<I
212
229
TechProjects:
213
230
guild.TechProjects?.map(x => ({
214
231
...x,
215
CompletionDate: x.CompletionDate ? toMongoDate(x.CompletionDate) : undefined
232
CompletionDate: x.CompletionDate ? toMongoDate2(x.CompletionDate, buildLabel) : undefined
216
233
})) ?? [],
217
234
XpComponents: xpComponents,
218
235
//XpCacheExpiryDate, some IMongoDate in the future, no clue what it's for
@@ -229,7 +246,7 @@ interface IProfileViewingData {
229
246
}
230
247
231
248
interface IPlayerProfileViewingDataResult extends Partial<IDailyAffiliations>, IInventoryAccolades {
232
AccountId: IOid;
249
AccountId: IOidWithLegacySupport;
233
250
DisplayName: string;
234
251
PlayerLevel: number;
235
252
LoadOutPreset?: Omit<ILoadoutConfigClient, "ItemId"> & { ItemId?: IOid };
@@ -241,7 +258,7 @@ interface IPlayerProfileViewingDataResult extends Partial<IDailyAffiliations>, I
241
258
Melee?: IEquipmentClient[];
242
259
XPInfo: ITypeXPItem[];
243
260
};
244
GuildId?: IOid;
261
GuildId?: IOidWithLegacySupport;
245
262
GuildName?: string;
246
263
GuildTier?: number;
247
264
GuildXp?: number;
@@ -252,7 +269,7 @@ interface IPlayerProfileViewingDataResult extends Partial<IDailyAffiliations>, I
252
269
DeathMarks?: string[];
253
270
Harvestable?: boolean;
254
271
DeathSquadable?: boolean;
255
Created?: IMongoDate;
272
Created?: IMongoDateWithLegacySupport;
256
273
TitleType?: string;
257
274
MigratedToConsole?: boolean;
258
275
Missions?: IMission[];
@@ -263,7 +280,7 @@ interface IPlayerProfileViewingDataResult extends Partial<IDailyAffiliations>, I
263
280
}
264
281
265
282
interface IXPComponentClient {
266
_id?: IOid;
283
_id?: IOidWithLegacySupport;
267
284
StoreTypeName: string;
268
285
TypeName?: string;
269
286
PurchaseQuantity?: number;
@@ -282,11 +299,13 @@ interface IXPComponentClient {
282
299
locTags?: Record<string, string>;
283
300
}
284
301
285
const resolveAndCollectSkins = (
302
const processLoadoutEquipment = (
286
303
inventory: TInventoryDatabaseDocument,
287
304
skins: Set<string>,
288
item: IEquipmentClient
305
item: IEquipmentClient,
306
buildLabel: string | undefined
289
307
): void => {
308
// Resolve and collect skins
290
309
for (const config of item.Configs) {
291
310
if (config.Skins) {
292
311
for (let i = 0; i != config.Skins.length; ++i) {
@@ -307,42 +326,63 @@ const resolveAndCollectSkins = (
307
326
}
308
327
}
309
328
}
329
330
if (buildLabel && version_compare(buildLabel, gameToBuildVersion["19.5.0"]) <= 0) {
331
toLegacyOid(item.ItemId);
332
}
310
333
};
311
334
312
335
const populateLoadout = async (
313
336
inventory: TInventoryDatabaseDocument,
314
result: IPlayerProfileViewingDataResult
337
result: IPlayerProfileViewingDataResult,
338
buildLabel: string | undefined
315
339
): Promise<void> => {
316
340
if (inventory.CurrentLoadOutIds.length) {
317
341
const loadout = (await Loadout.findById(inventory.LoadOutPresets, "NORMAL"))!;
342
318
343
result.LoadOutPreset = loadout.NORMAL.id(
319
344
inventory.CurrentLoadOutIds[LoadoutIndex.NORMAL]
320
345
)!.toJSON<ILoadoutConfigClient>();
321
346
result.LoadOutPreset.ItemId = undefined;
347
if (buildLabel && version_compare(buildLabel, gameToBuildVersion["19.5.0"]) <= 0) {
348
if (result.LoadOutPreset.s?.ItemId) {
349
toLegacyOid(result.LoadOutPreset.s.ItemId);
350
}
351
if (result.LoadOutPreset.l?.ItemId) {
352
toLegacyOid(result.LoadOutPreset.l.ItemId);
353
}
354
if (result.LoadOutPreset.p?.ItemId) {
355
toLegacyOid(result.LoadOutPreset.p.ItemId);
356
}
357
if (result.LoadOutPreset.m?.ItemId) {
358
toLegacyOid(result.LoadOutPreset.m.ItemId);
359
}
360
}
361
322
362
const skins = new Set<string>();
323
363
if (result.LoadOutPreset.s?.ItemId) {
324
364
result.LoadOutInventory.Suits = [
325
365
inventory.Suits.id(fromOid(result.LoadOutPreset.s.ItemId))!.toJSON<IEquipmentClient>()
326
366
];
327
resolveAndCollectSkins(inventory, skins, result.LoadOutInventory.Suits[0]);
367
processLoadoutEquipment(inventory, skins, result.LoadOutInventory.Suits[0], buildLabel);
328
368
}
329
369
if (result.LoadOutPreset.p?.ItemId) {
330
370
result.LoadOutInventory.Pistols = [
331
371
inventory.Pistols.id(fromOid(result.LoadOutPreset.p.ItemId))!.toJSON<IEquipmentClient>()
332
372
];
333
resolveAndCollectSkins(inventory, skins, result.LoadOutInventory.Pistols[0]);
373
processLoadoutEquipment(inventory, skins, result.LoadOutInventory.Pistols[0], buildLabel);
334
374
}
335
375
if (result.LoadOutPreset.l?.ItemId) {
336
376
result.LoadOutInventory.LongGuns = [
337
377
inventory.LongGuns.id(fromOid(result.LoadOutPreset.l.ItemId))!.toJSON<IEquipmentClient>()
338
378
];
339
resolveAndCollectSkins(inventory, skins, result.LoadOutInventory.LongGuns[0]);
379
processLoadoutEquipment(inventory, skins, result.LoadOutInventory.LongGuns[0], buildLabel);
340
380
}
341
381
if (result.LoadOutPreset.m?.ItemId) {
342
382
result.LoadOutInventory.Melee = [
343
383
inventory.Melee.id(fromOid(result.LoadOutPreset.m.ItemId))!.toJSON<IEquipmentClient>()
344
384
];
345
resolveAndCollectSkins(inventory, skins, result.LoadOutInventory.Melee[0]);
385
processLoadoutEquipment(inventory, skins, result.LoadOutInventory.Melee[0], buildLabel);
346
386
}
347
387
for (const skin of skins) {
348
388
result.LoadOutInventory.WeaponSkins.push({ ItemType: skin });
@@ -350,8 +390,12 @@ const populateLoadout = async (
350
390
}
351
391
};
352
392
353
const populateGuild = (guild: TGuildDatabaseDocument, result: IPlayerProfileViewingDataResult): void => {
354
result.GuildId = toOid(guild._id);
393
const populateGuild = (
394
guild: TGuildDatabaseDocument,
395
result: IPlayerProfileViewingDataResult,
396
buildLabel: string | undefined
397
): void => {
398
result.GuildId = toOid2(guild._id, buildLabel);
355
399
result.GuildName = guild.Name;
356
400
result.GuildTier = guild.Tier;
357
401
result.GuildXp = guild.XP;