XFE Git
XFE Studio Git
Git 首页 全局搜索
XFE 主站 文档 NuGet

SpaceNinjaServer

A simple server for a small space ninja game

公开
关注 0 Fork 1 Star 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
Ordis <134585663+OrdisPrime@users.noreply.github.com>
提交于

代码差异

6 个文件 +160 -64
Modified src/controllers/api/giveKeyChainTriggeredItemsController.ts +5 -20
@@ -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 {
Modified src/controllers/api/giveKeyChainTriggeredMessageController.ts +2 -21
@@ -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);
Modified src/controllers/custom/manageQuestsController.ts +49 -17
@@ -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 }
Modified src/models/inventoryModels/inventoryModel.ts +1 -0
@@ -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
Modified src/services/inventoryService.ts +2 -6
@@ -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 };
Modified src/services/questService.ts +101 -0
@@ -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 };