返回提交历史
Modified
src/controllers/api/artifactsController.ts
+7
-8
Modified
src/controllers/api/missionInventoryUpdateController.ts
+10
-11
Modified
src/helpers/stringHelpers.ts
+1
-1
Modified
src/services/inventoryService.ts
+25
-18
Modified
src/services/missionInventoryUpdateService .ts
+20
-24
Modified
src/types/inventoryTypes/SuitTypes.ts
+1
-0
Modified
src/types/inventoryTypes/weaponTypes.ts
+1
-0
Deleted
src/types/missionInventoryUpdateType.ts
+0
-94
Added
src/types/missionTypes.ts
+17
-0
Added
src/types/requestTypes.ts
+53
-0
XFEstudio/SpaceNinjaServer
Mission rewards fix (#54)
67b73383
代码差异
10 个文件
+135
-156
@@ -1,19 +1,18 @@
1
import { parseString } from "@/src/helpers/general";
2
import { getJSONfromString } from "@/src/helpers/stringHelpers";
1
3
import { upgradeMod } from "@/src/services/inventoryService";
4
import { IArtifactsRequest } from "@/src/types/requestTypes";
2
5
import { RequestHandler } from "express";
3
6
4
7
// eslint-disable-next-line @typescript-eslint/no-misused-promises
5
8
const artifactsController: RequestHandler = async (req, res) => {
6
const [data] = String(req.body).split("\n");
7
const id = req.query.accountId as string;
8
9
// TODO - salt check
9
const accountId = parseString(req.query.accountId);
10
10
11
11
try {
12
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
13
const parsedData = JSON.parse(data);
14
12
// eslint-disable-next-line @typescript-eslint/no-unsafe-argument, @typescript-eslint/no-unsafe-call
13
const artifactsData = getJSONfromString(req.body.toString()) as IArtifactsRequest;
15
14
// eslint-disable-next-line @typescript-eslint/no-unsafe-argument
16
const upgradeModId = await upgradeMod(parsedData, id);
15
const upgradeModId = await upgradeMod(artifactsData, accountId);
17
16
res.send(upgradeModId);
18
17
} catch (err) {
19
18
console.error("Error parsing JSON data:", err);
@@ -1,7 +1,9 @@
1
1
import { RequestHandler } from "express";
2
2
import { missionInventoryUpdate } from "@/src/services/inventoryService";
3
3
import { combineRewardAndLootInventory, getRewards } from "@/src/services/missionInventoryUpdateService ";
4
import { IMissionInventoryUpdate } from "@/src/types/missionInventoryUpdateType";
4
import { getJSONfromString } from "@/src/helpers/stringHelpers";
5
import { parseString } from "@/src/helpers/general";
6
import { IMissionInventoryUpdateRequest } from "@/src/types/requestTypes";
5
7
/*
6
8
**** INPUT ****
7
9
- [ ] crossPlaySetting
@@ -20,7 +22,7 @@ import { IMissionInventoryUpdate } from "@/src/types/missionInventoryUpdateType"
20
22
- [ ] CurrentLoadOutIds
21
23
- [ ] AliveTime
22
24
- [ ] MissionTime
23
- [ ] Missions
25
- [x] Missions
24
26
- [ ] CompletedAlerts
25
27
- [ ] LastRegionPlayed
26
28
- [ ] GameModeId
@@ -43,23 +45,20 @@ import { IMissionInventoryUpdate } from "@/src/types/missionInventoryUpdateType"
43
45
*/
44
46
45
47
// eslint-disable-next-line @typescript-eslint/no-misused-promises
46
const missionInventoryUpdateController: RequestHandler = async (req, res) => {
47
const [data] = String(req.body).split("\n");
48
const id = req.query.accountId as string;
48
const missionInventoryUpdateController: RequestHandler = async (req, res): Promise<void> => {
49
const accountId = parseString(req.query.accountId);
49
50
50
51
try {
51
const lootInventory = JSON.parse(data) as IMissionInventoryUpdate;
52
if (typeof lootInventory !== "object" || lootInventory === null) {
53
throw new Error("Invalid data format");
54
}
52
// eslint-disable-next-line @typescript-eslint/no-unsafe-argument, @typescript-eslint/no-unsafe-call
53
const lootInventory = getJSONfromString(req.body.toString()) as IMissionInventoryUpdateRequest;
55
54
56
const { InventoryChanges, MissionRewards } = getRewards(lootInventory.RewardInfo);
55
const { InventoryChanges, MissionRewards } = getRewards(lootInventory);
57
56
58
57
const { combinedInventoryChanges, TotalCredits, CreditsBonus, MissionCredits, FusionPoints } =
59
58
combineRewardAndLootInventory(InventoryChanges, lootInventory);
60
59
61
60
// eslint-disable-next-line @typescript-eslint/no-unused-vars
62
const InventoryJson = JSON.stringify(await missionInventoryUpdate(combinedInventoryChanges, id));
61
const InventoryJson = JSON.stringify(await missionInventoryUpdate(combinedInventoryChanges, accountId));
63
62
res.json({
64
63
// InventoryJson, // this part will reset game data and missions will be locked
65
64
MissionRewards,
@@ -1,4 +1,4 @@
1
const getJSONfromString = (str: string): any => {
1
export const getJSONfromString = (str: string): any => {
2
2
const jsonSubstring = str.substring(0, str.lastIndexOf("}") + 1);
3
3
return JSON.parse(jsonSubstring);
4
4
};
@@ -2,20 +2,20 @@ import { Inventory } from "@/src/models/inventoryModel";
2
2
import new_inventory from "@/static/fixed_responses/postTutorialInventory.json";
3
3
import config from "@/config.json";
4
4
import { Types } from "mongoose";
5
import { ISuitResponse } from "@/src/types/inventoryTypes/SuitTypes";
5
import { ISuitDatabase, ISuitResponse } from "@/src/types/inventoryTypes/SuitTypes";
6
6
import { SlotType } from "@/src/types/purchaseTypes";
7
import { IWeaponResponse } from "@/src/types/inventoryTypes/weaponTypes";
7
import { IWeaponDatabase, IWeaponResponse } from "@/src/types/inventoryTypes/weaponTypes";
8
8
import {
9
9
IChallengeProgress,
10
10
IConsumable,
11
ICrewShipSalvagedWeaponSkin,
12
11
IFlavourItem,
13
12
IInventoryDatabaseDocument,
14
13
IMiscItem,
14
IMission,
15
15
IRawUpgrade
16
16
} from "@/src/types/inventoryTypes/inventoryTypes";
17
import { IMissionInventoryUpdate, IMissionInventoryUpdateGear } from "../types/missionInventoryUpdateType";
18
17
import { IGenericUpdate } from "../types/genericUpdate";
18
import { IArtifactsRequest, IMissionInventoryUpdateRequest } from "../types/requestTypes";
19
19
20
20
const createInventory = async (accountOwnerId: Types.ObjectId) => {
21
21
try {
@@ -139,17 +139,17 @@ export const addCustomization = async (customizatonName: string, accountId: stri
139
139
140
140
const addGearExpByCategory = (
141
141
inventory: IInventoryDatabaseDocument,
142
gearArray: IMissionInventoryUpdateGear[] | undefined,
142
gearArray: ISuitDatabase[] | IWeaponDatabase[] | undefined,
143
143
categoryName: "Pistols" | "LongGuns" | "Melee" | "Suits"
144
144
) => {
145
145
const category = inventory[categoryName];
146
146
147
147
gearArray?.forEach(({ ItemId, XP }) => {
148
const itemIndex = category.findIndex(item => item._id?.equals(ItemId.$oid));
148
const itemIndex = ItemId ? category.findIndex(item => item._id?.equals(ItemId.$oid)) : -1;
149
149
const item = category[itemIndex];
150
150
151
151
if (itemIndex !== -1 && item.XP != undefined) {
152
item.XP += XP;
152
item.XP += XP || 0;
153
153
inventory.markModified(`${categoryName}.${itemIndex}.XP`);
154
154
}
155
155
});
@@ -229,11 +229,24 @@ const addChallenges = (inventory: IInventoryDatabaseDocument, itemsArray: IChall
229
229
});
230
230
};
231
231
232
const addMissionComplete = (inventory: IInventoryDatabaseDocument, { Tag, Completes }: IMission) => {
233
const { Missions } = inventory;
234
const itemIndex = Missions.findIndex(item => item.Tag === Tag);
235
236
if (itemIndex !== -1) {
237
Missions[itemIndex].Completes += Completes;
238
inventory.markModified(`Missions.${itemIndex}.Completes`);
239
} else {
240
Missions.push({ Tag, Completes });
241
}
242
};
243
232
244
const gearKeys = ["Suits", "Pistols", "LongGuns", "Melee"] as const;
233
245
type GearKeysType = (typeof gearKeys)[number];
234
246
235
export const missionInventoryUpdate = async (data: IMissionInventoryUpdate, accountId: string) => {
236
const { RawUpgrades, MiscItems, RegularCredits, ChallengeProgress, FusionPoints, Consumables, Recipes } = data;
247
export const missionInventoryUpdate = async (data: IMissionInventoryUpdateRequest, accountId: string) => {
248
const { RawUpgrades, MiscItems, RegularCredits, ChallengeProgress, FusionPoints, Consumables, Recipes, Missions } =
249
data;
237
250
const inventory = await getInventory(accountId);
238
251
239
252
// credits
@@ -251,6 +264,7 @@ export const missionInventoryUpdate = async (data: IMissionInventoryUpdate, acco
251
264
addConsumables(inventory, Consumables);
252
265
addRecipes(inventory, Recipes);
253
266
addChallenges(inventory, ChallengeProgress);
267
addMissionComplete(inventory, Missions!);
254
268
255
269
const changedInventory = await inventory.save();
256
270
return changedInventory.toJSON();
@@ -275,15 +289,8 @@ export const addBooster = async (ItemType: string, time: number, accountId: stri
275
289
await inventory.save();
276
290
};
277
291
278
export const upgradeMod = async (
279
{
280
Upgrade,
281
LevelDiff,
282
Cost,
283
FusionPointCost
284
}: { Upgrade: ICrewShipSalvagedWeaponSkin; LevelDiff: number; Cost: number; FusionPointCost: number },
285
accountId: string
286
): Promise<string | undefined> => {
292
export const upgradeMod = async (artifactsData: IArtifactsRequest, accountId: string): Promise<string | undefined> => {
293
const { Upgrade, LevelDiff, Cost, FusionPointCost } = artifactsData;
287
294
try {
288
295
const inventory = await getInventory(accountId);
289
296
const { Upgrades, RawUpgrades } = inventory;
@@ -1,29 +1,26 @@
1
import {
2
IMissionInventoryUpdate,
3
IMissionInventoryUpdateRewardInfo,
4
IMissionRewardResponse,
5
IReward,
6
IInventoryFieldType,
7
inventoryFields
8
} from "@/src/types/missionInventoryUpdateType";
1
import { IMissionRewardResponse, IReward, IInventoryFieldType, inventoryFields } from "@/src/types/missionTypes";
9
2
10
3
import missionsDropTable from "@/static/json/missions-drop-table.json";
11
4
import { modNames, relicNames, miscNames, resourceNames, gearNames, blueprintNames } from "@/static/data/items";
5
import { IMissionInventoryUpdateRequest } from "../types/requestTypes";
12
6
13
7
// need reverse engineer rewardSeed, otherwise ingame displayed rotation reward will be different than added to db or displayed on mission end
14
const getRewards = (
15
rewardInfo: IMissionInventoryUpdateRewardInfo | undefined
16
): { InventoryChanges: IMissionInventoryUpdate; MissionRewards: IMissionRewardResponse[] } => {
17
if (!rewardInfo) {
8
const getRewards = ({
9
RewardInfo
10
}: IMissionInventoryUpdateRequest): {
11
InventoryChanges: IMissionInventoryUpdateRequest;
12
MissionRewards: IMissionRewardResponse[];
13
} => {
14
if (!RewardInfo) {
18
15
return { InventoryChanges: {}, MissionRewards: [] };
19
16
}
20
17
21
const rewards = (missionsDropTable as { [key: string]: IReward[] })[rewardInfo.node];
18
const rewards = (missionsDropTable as { [key: string]: IReward[] })[RewardInfo.node];
22
19
if (!rewards) {
23
20
return { InventoryChanges: {}, MissionRewards: [] };
24
21
}
25
22
26
const rotationCount = rewardInfo.rewardQualifications?.length || 0;
23
const rotationCount = RewardInfo.rewardQualifications?.length || 0;
27
24
const rotations = getRotations(rotationCount);
28
25
const drops: IReward[] = [];
29
26
for (const rotation of rotations) {
@@ -54,6 +51,7 @@ const getRewards = (
54
51
// { chance: 10.82, name: "2X Orokin Cell", rotation: "C" },
55
52
// { chance: 10.82, name: "Arrow Mutation", rotation: "C" },
56
53
// { chance: 10.82, name: "200 Endo", rotation: "C" },
54
// { chance: 10.82, name: "200 Endo", rotation: "C" },
57
55
// { chance: 10.82, name: "2,000,000 Credits Cache", rotation: "C" },
58
56
// { chance: 7.69, name: "Health Restore (Large)", rotation: "C" },
59
57
// { chance: 7.69, name: "Vapor Specter Blueprint", rotation: "C" }
@@ -66,8 +64,8 @@ const getRewards = (
66
64
};
67
65
68
66
const combineRewardAndLootInventory = (
69
rewardInventory: IMissionInventoryUpdate,
70
lootInventory: IMissionInventoryUpdate
67
rewardInventory: IMissionInventoryUpdateRequest,
68
lootInventory: IMissionInventoryUpdateRequest
71
69
) => {
72
70
const missionCredits = lootInventory.RegularCredits || 0;
73
71
const creditsBonus = rewardInventory.RegularCredits || 0;
@@ -98,12 +96,10 @@ const getRotations = (rotationCount: number): (string | undefined)[] => {
98
96
if (rotationCount === 0) return [undefined];
99
97
100
98
const rotationPattern = ["A", "A", "B", "C"];
101
let rotationIndex = 0;
102
99
const rotatedValues = [];
103
100
104
for (let i = 1; i <= rotationCount; i++) {
105
rotatedValues.push(rotationPattern[rotationIndex]);
106
rotationIndex = (rotationIndex + 1) % 3;
101
for (let i = 0; i < rotationCount; i++) {
102
rotatedValues.push(rotationPattern[i % rotationPattern.length]);
107
103
}
108
104
109
105
return rotatedValues;
@@ -128,8 +124,8 @@ const getRandomRewardByChance = (data: IReward[] | undefined): IReward | undefin
128
124
129
125
const formatRewardsToInventoryType = (
130
126
rewards: IReward[]
131
): { InventoryChanges: IMissionInventoryUpdate; MissionRewards: IMissionRewardResponse[] } => {
132
const InventoryChanges: IMissionInventoryUpdate = {};
127
): { InventoryChanges: IMissionInventoryUpdateRequest; MissionRewards: IMissionRewardResponse[] } => {
128
const InventoryChanges: IMissionInventoryUpdateRequest = {};
133
129
const MissionRewards: IMissionRewardResponse[] = [];
134
130
for (const reward of rewards) {
135
131
if (itemCheck(InventoryChanges, MissionRewards, reward.name)) {
@@ -152,7 +148,7 @@ const formatRewardsToInventoryType = (
152
148
};
153
149
154
150
const itemCheck = (
155
InventoryChanges: IMissionInventoryUpdate,
151
InventoryChanges: IMissionInventoryUpdateRequest,
156
152
MissionRewards: IMissionRewardResponse[],
157
153
name: string
158
154
) => {
@@ -184,7 +180,7 @@ const getCountFromName = (name: string) => {
184
180
};
185
181
186
182
const addRewardResponse = (
187
InventoryChanges: IMissionInventoryUpdate,
183
InventoryChanges: IMissionInventoryUpdateRequest,
188
184
MissionRewards: IMissionRewardResponse[],
189
185
ItemName: string,
190
186
ItemType: string,
@@ -24,6 +24,7 @@ export interface ISuitDatabase {
24
24
FocusLens?: string;
25
25
UnlockLevel?: number;
26
26
_id: Types.ObjectId;
27
ItemId?: IOid;
27
28
}
28
29
29
30
export interface SuitConfig {
@@ -22,6 +22,7 @@ export interface IWeaponDatabase {
22
22
ModularParts?: string[];
23
23
UnlockLevel?: number;
24
24
_id?: Types.ObjectId;
25
ItemId?: IOid;
25
26
}
26
27
27
28
export interface WeaponConfig {
@@ -1,94 +0,0 @@
1
/* eslint-disable @typescript-eslint/no-explicit-any */
2
import { IOid } from "./commonTypes";
3
import { IDate } from "./inventoryTypes/inventoryTypes";
4
5
export const inventoryFields = ["RawUpgrades", "MiscItems", "Consumables", "Recipes"] as const;
6
export type IInventoryFieldType = (typeof inventoryFields)[number];
7
export interface IMissionInventoryUpdateGear {
8
ItemType: string;
9
ItemName: string;
10
ItemId: IOid;
11
XP: number;
12
UpgradeVer: number;
13
Features: number;
14
Polarized: number;
15
CustomizationSlotPurchases: number;
16
ModSlotPurchases: number;
17
FocusLens: string;
18
Expiry: IDate;
19
Polarity: any[];
20
Configs: any[];
21
ModularParts: any[];
22
SkillTree: string;
23
UpgradeType: string;
24
UpgradeFingerprint: string;
25
OffensiveUpgrade: string;
26
DefensiveUpgrade: string;
27
UpgradesExpiry: IDate;
28
ArchonCrystalUpgrades: any[];
29
}
30
31
export interface IMissionInventoryUpdateItem {
32
ItemCount: number;
33
ItemType: string;
34
}
35
36
export interface IMissionInventoryUpdateCard extends IMissionInventoryUpdateItem {
37
ItemId: IOid;
38
UpgradeFingerprint: string;
39
PendingRerollFingerprint: string;
40
LastAdded: IOid;
41
}
42
43
export interface IMissionInventoryUpdateChallange {
44
Name: string;
45
Progress: number;
46
Completed: any[];
47
}
48
49
export interface IMissionInventoryUpdateRewardInfo {
50
node: string;
51
rewardTier?: number;
52
nightmareMode?: boolean;
53
useVaultManifest?: boolean;
54
EnemyCachesFound?: number;
55
toxinOk?: boolean;
56
lostTargetWave?: number;
57
defenseTargetCount?: number;
58
EOM_AFK?: number;
59
rewardQualifications?: string;
60
PurgatoryRewardQualifications?: string;
61
rewardSeed?: number;
62
}
63
64
export interface IMissionInventoryUpdate {
65
rewardsMultiplier?: number;
66
ActiveBoosters?: any[];
67
LongGuns?: IMissionInventoryUpdateGear[];
68
Pistols?: IMissionInventoryUpdateGear[];
69
Suits?: IMissionInventoryUpdateGear[];
70
Melee?: IMissionInventoryUpdateGear[];
71
RawUpgrades?: IMissionInventoryUpdateItem[];
72
MiscItems?: IMissionInventoryUpdateItem[];
73
Consumables?: IMissionInventoryUpdateItem[];
74
Recipes?: IMissionInventoryUpdateItem[];
75
RegularCredits?: number;
76
ChallengeProgress?: IMissionInventoryUpdateChallange[];
77
RewardInfo?: IMissionInventoryUpdateRewardInfo;
78
FusionPoints?: number;
79
}
80
81
export interface IMissionRewardResponse {
82
StoreItem?: string;
83
TypeName: string;
84
UpgradeLevel?: number;
85
ItemCount: number;
86
TweetText: string;
87
ProductCategory: string;
88
}
89
90
export interface IReward {
91
name: string;
92
chance: number;
93
rotation?: string;
94
}
@@ -0,0 +1,17 @@
1
export const inventoryFields = ["RawUpgrades", "MiscItems", "Consumables", "Recipes"] as const;
2
export type IInventoryFieldType = (typeof inventoryFields)[number];
3
4
export interface IMissionRewardResponse {
5
StoreItem?: string;
6
TypeName: string;
7
UpgradeLevel?: number;
8
ItemCount: number;
9
TweetText: string;
10
ProductCategory: string;
11
}
12
13
export interface IReward {
14
name: string;
15
chance: number;
16
rotation?: string;
17
}
@@ -0,0 +1,53 @@
1
import {
2
IBooster,
3
IChallengeProgress,
4
IConsumable,
5
ICrewShipSalvagedWeaponSkin,
6
IMiscItem,
7
IMission,
8
IRawUpgrade
9
} from "./inventoryTypes/inventoryTypes";
10
import { IWeaponDatabase } from "./inventoryTypes/weaponTypes";
11
import { ISuitDatabase } from "./inventoryTypes/SuitTypes";
12
13
interface IArtifactsRequest {
14
Upgrade: ICrewShipSalvagedWeaponSkin;
15
LevelDiff: number;
16
Cost: number;
17
FusionPointCost: number;
18
}
19
20
interface IMissionInventoryUpdateRequest {
21
rewardsMultiplier?: number;
22
ActiveBoosters?: IBooster[];
23
LongGuns?: IWeaponDatabase[];
24
Pistols?: IWeaponDatabase[];
25
Suits?: ISuitDatabase[];
26
Melee?: IWeaponDatabase[];
27
RawUpgrades?: IRawUpgrade[];
28
MiscItems?: IMiscItem[];
29
Consumables?: IConsumable[];
30
Recipes?: IConsumable[];
31
RegularCredits?: number;
32
ChallengeProgress?: IChallengeProgress[];
33
RewardInfo?: IMissionInventoryUpdateRequestRewardInfo;
34
FusionPoints?: number;
35
Missions?: IMission;
36
}
37
38
interface IMissionInventoryUpdateRequestRewardInfo {
39
node: string;
40
rewardTier?: number;
41
nightmareMode?: boolean;
42
useVaultManifest?: boolean;
43
EnemyCachesFound?: number;
44
toxinOk?: boolean;
45
lostTargetWave?: number;
46
defenseTargetCount?: number;
47
EOM_AFK?: number;
48
rewardQualifications?: string;
49
PurgatoryRewardQualifications?: string;
50
rewardSeed?: number;
51
}
52
53
export { IArtifactsRequest, IMissionInventoryUpdateRequest };