返回提交历史
Modified
src/controllers/api/giveKeyChainTriggeredItemsController.ts
+5
-20
Modified
src/controllers/api/giveKeyChainTriggeredMessageController.ts
+2
-21
Modified
src/controllers/custom/manageQuestsController.ts
+49
-17
Modified
src/models/inventoryModels/inventoryModel.ts
+1
-0
Modified
src/services/inventoryService.ts
+2
-6
Modified
src/services/questService.ts
+101
-0
XFEstudio/SpaceNinjaServer
fix(webui): quest cheats (#965)
Completing Quests via the webui will now also award the quest's items and mails. Also fixes doubly adding key chain items. A few items will not be added, as it is currently impossible to determine the item category by path for these items. This will be fixed soon. Reviewed-on: https://onlyg.it/OpenWF/SpaceNinjaServer/pulls/965 Co-authored-by: Ordis <134585663+OrdisPrime@users.noreply.github.com> Co-committed-by: Ordis <134585663+OrdisPrime@users.noreply.github.com>
fb8d176f
代码差异
6 个文件
+160
-64
@@ -1,34 +1,19 @@
1
1
import { RequestHandler } from "express";
2
import { isEmptyObject, parseString } from "@/src/helpers/general";
2
import { parseString } from "@/src/helpers/general";
3
3
import { getJSONfromString } from "@/src/helpers/stringHelpers";
4
import { addKeyChainItems, getInventory } from "@/src/services/inventoryService";
4
import { getInventory } from "@/src/services/inventoryService";
5
5
import { IGroup } from "@/src/types/loginTypes";
6
import { updateQuestStage } from "@/src/services/questService";
6
import { giveKeyChainItem } from "@/src/services/questService";
7
7
8
8
export const giveKeyChainTriggeredItemsController: RequestHandler = async (req, res) => {
9
9
const accountId = parseString(req.query.accountId);
10
10
const keyChainInfo = getJSONfromString<IKeyChainRequest>((req.body as string).toString());
11
11
12
12
const inventory = await getInventory(accountId);
13
const inventoryChanges = await addKeyChainItems(inventory, keyChainInfo);
14
15
if (isEmptyObject(inventoryChanges)) {
16
throw new Error("inventory changes was empty after getting keychain items: should not happen");
17
}
18
// items were added: update quest stage's i (item was given)
19
updateQuestStage(inventory, keyChainInfo, { i: true });
20
13
const inventoryChanges = giveKeyChainItem(inventory, keyChainInfo);
21
14
await inventory.save();
22
res.send(inventoryChanges);
23
15
24
//TODO: Check whether Wishlist is used to track items which should exist uniquely in the inventory
25
/*
26
some items are added or removed (not sure) to the wishlist, in that case a
27
WishlistChanges: ["/Lotus/Types/Items/ShipFeatureItems/ArsenalFeatureItem"],
28
is added to the response, need to determine for which items this is the case and what purpose this has.
29
*/
30
//{"KeyChain":"/Lotus/Types/Keys/VorsPrize/VorsPrizeQuestKeyChain","ChainStage":0}
31
//{"WishlistChanges":["/Lotus/Types/Items/ShipFeatureItems/ArsenalFeatureItem"],"MiscItems":[{"ItemType":"/Lotus/Types/Items/ShipFeatureItems/ArsenalFeatureItem","ItemCount":1}]}
16
res.send(inventoryChanges);
32
17
};
33
18
34
19
export interface IKeyChainRequest {
@@ -1,34 +1,15 @@
1
1
import { IKeyChainRequest } from "@/src/controllers/api/giveKeyChainTriggeredItemsController";
2
import { IMessage } from "@/src/models/inboxModel";
3
import { createMessage } from "@/src/services/inboxService";
4
2
import { getInventory } from "@/src/services/inventoryService";
5
import { getKeyChainMessage } from "@/src/services/itemDataService";
6
3
import { getAccountIdForRequest } from "@/src/services/loginService";
7
import { updateQuestStage } from "@/src/services/questService";
4
import { giveKeyChainMessage } from "@/src/services/questService";
8
5
import { RequestHandler } from "express";
9
6
10
7
export const giveKeyChainTriggeredMessageController: RequestHandler = async (req, res) => {
11
8
const accountId = await getAccountIdForRequest(req);
12
9
const keyChainInfo = JSON.parse((req.body as Buffer).toString()) as IKeyChainRequest;
13
10
14
const keyChainMessage = getKeyChainMessage(keyChainInfo);
15
16
const message = {
17
sndr: keyChainMessage.sender,
18
msg: keyChainMessage.body,
19
sub: keyChainMessage.title,
20
att: keyChainMessage.attachments.length > 0 ? keyChainMessage.attachments : undefined,
21
countedAtt: keyChainMessage.countedAttachments.length > 0 ? keyChainMessage.countedAttachments : undefined,
22
icon: keyChainMessage.icon ?? "",
23
transmission: keyChainMessage.transmission ?? "",
24
highPriority: keyChainMessage.highPriority ?? false,
25
r: false
26
} satisfies IMessage;
27
28
await createMessage(accountId, [message]);
29
30
11
const inventory = await getInventory(accountId, "QuestKeys");
31
updateQuestStage(inventory, keyChainInfo, { m: true });
12
await giveKeyChainMessage(inventory, accountId, keyChainInfo);
32
13
await inventory.save();
33
14
34
15
res.send(1);
@@ -1,8 +1,7 @@
1
1
import { addString } from "@/src/controllers/api/inventoryController";
2
2
import { getInventory } from "@/src/services/inventoryService";
3
3
import { getAccountIdForRequest } from "@/src/services/loginService";
4
import { addQuestKey, IUpdateQuestRequest, updateQuestKey } from "@/src/services/questService";
5
import { IQuestStage } from "@/src/types/inventoryTypes/inventoryTypes";
4
import { addQuestKey, completeQuest, IUpdateQuestRequest, updateQuestKey } from "@/src/services/questService";
6
5
import { logger } from "@/src/utils/logger";
7
6
import { RequestHandler } from "express";
8
7
import { ExportKeys } from "warframe-public-export-plus";
@@ -23,7 +22,7 @@ export const manageQuestsController: RequestHandler = async (req, res) => {
23
22
allQuestKeys.push(k);
24
23
}
25
24
}
26
const inventory = await getInventory(accountId, "QuestKeys NodeIntrosCompleted");
25
const inventory = await getInventory(accountId);
27
26
28
27
switch (operation) {
29
28
case "updateKey": {
@@ -40,21 +39,31 @@ export const manageQuestsController: RequestHandler = async (req, res) => {
40
39
case "completeAll": {
41
40
logger.info("completing all quests..");
42
41
for (const questKey of allQuestKeys) {
43
const chainStageTotal = ExportKeys[questKey].chainStages?.length ?? 0;
44
const Progress = Array(chainStageTotal).fill({ c: 0, i: true, m: true, b: [] } satisfies IQuestStage);
45
const inventoryQuestKey = inventory.QuestKeys.find(qk => qk.ItemType === questKey);
46
if (inventoryQuestKey) {
47
inventoryQuestKey.Completed = true;
48
inventoryQuestKey.Progress = Progress;
49
continue;
42
try {
43
await completeQuest(inventory, questKey);
44
} catch (error) {
45
if (error instanceof Error) {
46
logger.error(
47
`Something went wrong completing quest ${questKey}, probably could not add some item`
48
);
49
logger.error(error.message);
50
}
51
}
52
53
//Skip "Watch The Maker"
54
if (questKey === "/Lotus/Types/Keys/NewWarIntroQuest/NewWarIntroKeyChain") {
55
addString(
56
inventory.NodeIntrosCompleted,
57
"/Lotus/Levels/Cinematics/NewWarIntro/NewWarStageTwo.level"
58
);
59
}
60
61
if (questKey === "/Lotus/Types/Keys/ArchwingQuest/ArchwingQuestKeyChain") {
62
inventory.ArchwingEnabled = true;
50
63
}
51
addQuestKey(inventory, { ItemType: questKey, Completed: true, unlock: true, Progress: Progress });
52
64
}
53
inventory.ArchwingEnabled = true;
54
inventory.ActiveQuest = "";
55
65
56
// Skip "Watch The Maker"
57
addString(inventory.NodeIntrosCompleted, "/Lotus/Levels/Cinematics/NewWarIntro/NewWarStageTwo.level");
66
inventory.ActiveQuest = "";
58
67
break;
59
68
}
60
69
case "ResetAll": {
@@ -63,14 +72,37 @@ export const manageQuestsController: RequestHandler = async (req, res) => {
63
72
questKey.Completed = false;
64
73
questKey.Progress = [];
65
74
}
75
inventory.ActiveQuest = "";
66
76
break;
67
77
}
68
78
case "completeAllUnlocked": {
69
79
logger.info("completing all unlocked quests..");
70
80
for (const questKey of inventory.QuestKeys) {
71
//if (!questKey.unlock) { continue; }
72
questKey.Completed = true;
81
console.log("size of questkeys", inventory.QuestKeys.length);
82
try {
83
await completeQuest(inventory, questKey.ItemType);
84
} catch (error) {
85
if (error instanceof Error) {
86
logger.error(
87
`Something went wrong completing quest ${questKey.ItemType}, probably could not add some item`
88
);
89
logger.error(error.message);
90
}
91
}
92
93
//Skip "Watch The Maker"
94
if (questKey.ItemType === "/Lotus/Types/Keys/NewWarIntroQuest/NewWarIntroKeyChain") {
95
addString(
96
inventory.NodeIntrosCompleted,
97
"/Lotus/Levels/Cinematics/NewWarIntro/NewWarStageTwo.level"
98
);
99
}
100
101
if (questKey.ItemType === "/Lotus/Types/Keys/ArchwingQuest/ArchwingQuestKeyChain") {
102
inventory.ArchwingEnabled = true;
103
}
73
104
}
105
inventory.ActiveQuest = "";
74
106
break;
75
107
}
76
108
}
@@ -1249,6 +1249,7 @@ export type InventoryDocumentProps = {
1249
1249
KahlLoadOuts: Types.DocumentArray<IOperatorConfigDatabase>;
1250
1250
PendingRecipes: Types.DocumentArray<IPendingRecipeDatabase>;
1251
1251
WeaponSkins: Types.DocumentArray<IWeaponSkinDatabase>;
1252
QuestKeys: Types.DocumentArray<IQuestKeyDatabase>;
1252
1253
} & { [K in TEquipmentKey]: Types.DocumentArray<IEquipmentDatabase> };
1253
1254
1254
1255
// eslint-disable-next-line @typescript-eslint/ban-types
@@ -367,7 +367,7 @@ export const addItem = async (
367
367
};
368
368
}
369
369
if (typeName in ExportKeys) {
370
// Note: "/Lotus/Types/Keys/" contains some EmailItems and ShipFeatureItems
370
// Note: "/Lotus/Types/Keys/" contains some EmailItems
371
371
inventory.QuestKeys.push({ ItemType: typeName });
372
372
return {
373
373
InventoryChanges: {
@@ -524,9 +524,7 @@ export const addItem = async (
524
524
}
525
525
break;
526
526
}
527
const errorMessage = `unable to add item: ${typeName}`;
528
logger.error(errorMessage);
529
throw new Error(errorMessage);
527
throw new Error(`unable to add item: ${typeName}`);
530
528
};
531
529
532
530
export const addItems = async (
@@ -1183,7 +1181,5 @@ export const addKeyChainItems = async (
1183
1181
combineInventoryChanges(inventoryChanges, inventoryChangesDelta.InventoryChanges);
1184
1182
}
1185
1183
1186
await addItems(inventory, nonStoreItems);
1187
1188
1184
return inventoryChanges;
1189
1185
};
@@ -1,8 +1,14 @@
1
1
import { IKeyChainRequest } from "@/src/controllers/api/giveKeyChainTriggeredItemsController";
2
import { isEmptyObject } from "@/src/helpers/general";
3
import { IMessage } from "@/src/models/inboxModel";
2
4
import { TInventoryDatabaseDocument } from "@/src/models/inventoryModels/inventoryModel";
5
import { createMessage } from "@/src/services/inboxService";
6
import { addKeyChainItems } from "@/src/services/inventoryService";
7
import { getKeyChainMessage } from "@/src/services/itemDataService";
3
8
import { IInventoryDatabase, IQuestKeyDatabase, IQuestStage } from "@/src/types/inventoryTypes/inventoryTypes";
4
9
import { logger } from "@/src/utils/logger";
5
10
import { HydratedDocument } from "mongoose";
11
import { ExportKeys } from "warframe-public-export-plus";
6
12
7
13
export interface IUpdateQuestRequest {
8
14
QuestKeys: Omit<IQuestKeyDatabase, "CompletionDate">[];
@@ -70,3 +76,98 @@ export const addQuestKey = (inventory: TInventoryDatabaseDocument, questKey: IQu
70
76
}
71
77
inventory.QuestKeys.push(questKey);
72
78
};
79
80
export const completeQuest = async (inventory: TInventoryDatabaseDocument, questKey: string) => {
81
const chainStages = ExportKeys[questKey]?.chainStages;
82
83
if (!chainStages) {
84
throw new Error(`Quest ${questKey} does not contain chain stages`);
85
}
86
87
const chainStageTotal = ExportKeys[questKey].chainStages?.length ?? 0;
88
89
const existingQuestKey = inventory.QuestKeys.find(qk => qk.ItemType === questKey);
90
91
if (existingQuestKey?.Completed) {
92
return;
93
}
94
const Progress = Array(chainStageTotal).fill({
95
c: 0,
96
i: false,
97
m: false,
98
b: []
99
} satisfies IQuestStage);
100
101
const completedQuestKey: IQuestKeyDatabase = {
102
ItemType: questKey,
103
Completed: true,
104
unlock: true,
105
Progress: Progress,
106
CompletionDate: new Date()
107
};
108
109
//overwrite current quest progress, might lead to multiple quest item rewards
110
if (existingQuestKey) {
111
existingQuestKey.overwrite(completedQuestKey);
112
//Object.assign(existingQuestKey, completedQuestKey);
113
} else {
114
addQuestKey(inventory, completedQuestKey);
115
}
116
117
for (let i = 0; i < chainStageTotal; i++) {
118
if (chainStages[i].itemsToGiveWhenTriggered.length > 0) {
119
await giveKeyChainItem(inventory, { KeyChain: questKey, ChainStage: i });
120
}
121
122
if (chainStages[i].messageToSendWhenTriggered) {
123
await giveKeyChainMessage(inventory, inventory.accountOwnerId.toString(), {
124
KeyChain: questKey,
125
ChainStage: i
126
});
127
}
128
}
129
//TODO: handle quest completions
130
};
131
132
export const giveKeyChainItem = async (inventory: TInventoryDatabaseDocument, keyChainInfo: IKeyChainRequest) => {
133
const inventoryChanges = await addKeyChainItems(inventory, keyChainInfo);
134
135
if (isEmptyObject(inventoryChanges)) {
136
throw new Error("inventory changes was empty after getting keychain items: should not happen");
137
}
138
// items were added: update quest stage's i (item was given)
139
updateQuestStage(inventory, keyChainInfo, { i: true });
140
141
//TODO: Check whether Wishlist is used to track items which should exist uniquely in the inventory
142
/*
143
some items are added or removed (not sure) to the wishlist, in that case a
144
WishlistChanges: ["/Lotus/Types/Items/ShipFeatureItems/ArsenalFeatureItem"],
145
is added to the response, need to determine for which items this is the case and what purpose this has.
146
*/
147
//{"KeyChain":"/Lotus/Types/Keys/VorsPrize/VorsPrizeQuestKeyChain","ChainStage":0}
148
//{"WishlistChanges":["/Lotus/Types/Items/ShipFeatureItems/ArsenalFeatureItem"],"MiscItems":[{"ItemType":"/Lotus/Types/Items/ShipFeatureItems/ArsenalFeatureItem","ItemCount":1}]}
149
};
150
151
export const giveKeyChainMessage = async (
152
inventory: TInventoryDatabaseDocument,
153
accountId: string,
154
keyChainInfo: IKeyChainRequest
155
) => {
156
const keyChainMessage = getKeyChainMessage(keyChainInfo);
157
158
const message = {
159
sndr: keyChainMessage.sender,
160
msg: keyChainMessage.body,
161
sub: keyChainMessage.title,
162
att: keyChainMessage.attachments.length > 0 ? keyChainMessage.attachments : undefined,
163
countedAtt: keyChainMessage.countedAttachments.length > 0 ? keyChainMessage.countedAttachments : undefined,
164
icon: keyChainMessage.icon ?? "",
165
transmission: keyChainMessage.transmission ?? "",
166
highPriority: keyChainMessage.highPriority ?? false,
167
r: false
168
} satisfies IMessage;
169
170
await createMessage(accountId, [message]);
171
172
updateQuestStage(inventory, keyChainInfo, { m: true });
173
};