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

XFESpaceNinjaServer

A simple server for a small space ninja game

公开
关注 0 Fork 0 Star 0
返回提交历史

XFEstudio/XFESpaceNinjaServer

feat: Quests 2 (#878)

edc3171e
Ordis <134585663+OrdisPrime@users.noreply.github.com>
提交于

代码差异

15 个文件 +210 -72
Modified package-lock.json +4 -4
@@ -12,7 +12,7 @@
12 12 "copyfiles": "^2.4.1",
13 13 "express": "^5",
14 14 "mongoose": "^8.9.4",
15 "warframe-public-export-plus": "^0.5.24",
15 "warframe-public-export-plus": "^0.5.26",
16 16 "warframe-riven-info": "^0.1.2",
17 17 "winston": "^3.17.0",
18 18 "winston-daily-rotate-file": "^5.0.0"
@@ -4092,9 +4092,9 @@
4092 4092 }
4093 4093 },
4094 4094 "node_modules/warframe-public-export-plus": {
4095 "version": "0.5.25",
4096 "resolved": "https://registry.npmjs.org/warframe-public-export-plus/-/warframe-public-export-plus-0.5.25.tgz",
4097 "integrity": "sha512-vYEm6q7zECk+2xi9pb6KEksoNNEqSqcrbEtwuEDCySAzZJwbE8iErFtTA2ZAK81LOG42i8yDMevvldyke/Sr3A=="
4095 "version": "0.5.26",
4096 "resolved": "https://registry.npmjs.org/warframe-public-export-plus/-/warframe-public-export-plus-0.5.26.tgz",
4097 "integrity": "sha512-oRcz14nBHcwx5CgCbxw5bhesiJtVlspuHyqzjOwRu63c2Vs+1TgfqbVGdqc9sm3AZL9eJhtay+khc1h7r3BM/A=="
4098 4098 },
4099 4099 "node_modules/warframe-riven-info": {
4100 4100 "version": "0.1.2",
Modified package.json +1 -1
@@ -16,7 +16,7 @@
16 16 "copyfiles": "^2.4.1",
17 17 "express": "^5",
18 18 "mongoose": "^8.9.4",
19 "warframe-public-export-plus": "^0.5.24",
19 "warframe-public-export-plus": "^0.5.26",
20 20 "warframe-riven-info": "^0.1.2",
21 21 "winston": "^3.17.0",
22 22 "winston-daily-rotate-file": "^5.0.0"
Modified src/controllers/api/giveKeyChainTriggeredItemsController.ts +7 -25
@@ -2,40 +2,21 @@ import { RequestHandler } from "express";
2 2 import { isEmptyObject, parseString } from "@/src/helpers/general";
3 3 import { getJSONfromString } from "@/src/helpers/stringHelpers";
4 4 import { addKeyChainItems, getInventory } from "@/src/services/inventoryService";
5 import { IGroup } from "@/src/types/loginTypes";
6 import { updateQuestStage } from "@/src/services/questService";
5 7
6 8 export const giveKeyChainTriggeredItemsController: RequestHandler = async (req, res) => {
7 9 const accountId = parseString(req.query.accountId);
8 const keyChainTriggeredItemsRequest = getJSONfromString<IGiveKeyChainTriggeredItemsRequest>(
9 (req.body as string).toString()
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, keyChainTriggeredItemsRequest);
13 const inventoryChanges = await addKeyChainItems(inventory, keyChainInfo);
14 14
15 15 if (isEmptyObject(inventoryChanges)) {
16 16 throw new Error("inventory changes was empty after getting keychain items: should not happen");
17 17 }
18 18 // items were added: update quest stage's i (item was given)
19 const quest = inventory.QuestKeys.find(quest => quest.ItemType === keyChainTriggeredItemsRequest.KeyChain);
20
21 if (!quest) {
22 throw new Error(`Quest ${keyChainTriggeredItemsRequest.KeyChain} not found in QuestKeys`);
23 }
24 if (!quest.Progress) {
25 throw new Error(`Progress should always exist when giving keychain triggered items`);
26 }
27
28 const questStage = quest.Progress[keyChainTriggeredItemsRequest.ChainStage];
29 if (questStage) {
30 questStage.i = true;
31 } else {
32 const questStageIndex = quest.Progress.push({ i: true }) - 1;
33 if (questStageIndex !== keyChainTriggeredItemsRequest.ChainStage) {
34 throw new Error(
35 `Quest stage index mismatch: ${questStageIndex} !== ${keyChainTriggeredItemsRequest.ChainStage}`
36 );
37 }
38 }
19 updateQuestStage(inventory, keyChainInfo, { i: true });
39 20
40 21 await inventory.save();
41 22 res.send(inventoryChanges);
@@ -50,7 +31,8 @@ export const giveKeyChainTriggeredItemsController: RequestHandler = async (req,
50 31 //{"WishlistChanges":["/Lotus/Types/Items/ShipFeatureItems/ArsenalFeatureItem"],"MiscItems":[{"ItemType":"/Lotus/Types/Items/ShipFeatureItems/ArsenalFeatureItem","ItemCount":1}]}
51 32 };
52 33
53 export interface IGiveKeyChainTriggeredItemsRequest {
34 export interface IKeyChainRequest {
54 35 KeyChain: string;
55 36 ChainStage: number;
37 Groups?: IGroup[];
56 38 }
Added src/controllers/api/giveKeyChainTriggeredMessageController.ts +36 -0
@@ -0,0 +1,36 @@
1 import { IKeyChainRequest } from "@/src/controllers/api/giveKeyChainTriggeredItemsController";
2 import { IMessage } from "@/src/models/inboxModel";
3 import { createMessage } from "@/src/services/inboxService";
4 import { getInventory } from "@/src/services/inventoryService";
5 import { getKeyChainMessage } from "@/src/services/itemDataService";
6 import { getAccountIdForRequest } from "@/src/services/loginService";
7 import { updateQuestStage } from "@/src/services/questService";
8 import { RequestHandler } from "express";
9
10 export const giveKeyChainTriggeredMessageController: RequestHandler = async (req, res) => {
11 const accountId = await getAccountIdForRequest(req);
12 const keyChainInfo = JSON.parse((req.body as Buffer).toString()) as IKeyChainRequest;
13 console.log(keyChainInfo);
14
15 const keyChainMessage = getKeyChainMessage(keyChainInfo);
16
17 const message = {
18 sndr: keyChainMessage.sender,
19 msg: keyChainMessage.body,
20 sub: keyChainMessage.title,
21 att: keyChainMessage.attachments.length > 0 ? keyChainMessage.attachments : undefined,
22 icon: keyChainMessage.icon ?? "",
23 transmission: keyChainMessage.transmission ?? "",
24 highPriority: keyChainMessage.highPriority ?? false,
25 r: false
26 } satisfies IMessage;
27
28 const savedMessages = await createMessage(accountId, [message]);
29 console.log("savedMessages", savedMessages);
30
31 const inventory = await getInventory(accountId, "QuestKeys");
32 updateQuestStage(inventory, keyChainInfo, { m: true });
33 await inventory.save();
34
35 res.send(1);
36 };
Added src/controllers/api/giveQuestKey.ts +45 -0
@@ -0,0 +1,45 @@
1 import { getJSONfromString } from "@/src/helpers/stringHelpers";
2 import { addItem, getInventory } from "@/src/services/inventoryService";
3 import { getAccountIdForRequest } from "@/src/services/loginService";
4 import { IOid } from "@/src/types/commonTypes";
5 import { RequestHandler } from "express";
6
7 export const giveQuestKeyRewardController: RequestHandler = async (req, res) => {
8 const accountId = await getAccountIdForRequest(req);
9 const rewardRequest = getJSONfromString<IQuestKeyRewardRequest>((req.body as Buffer).toString());
10
11 if (Array.isArray(rewardRequest.reward)) {
12 throw new Error("Multiple rewards not expected");
13 }
14
15 const reward = rewardRequest.reward;
16 const inventory = await getInventory(accountId);
17 const inventoryChanges = await addItem(inventory, reward.ItemType, reward.Amount);
18 await inventory.save();
19 res.json(inventoryChanges.InventoryChanges);
20 //TODO: consider whishlist changes
21 };
22
23 export interface IQuestKeyRewardRequest {
24 reward: IQuestKeyReward;
25 }
26
27 export interface IQuestKeyReward {
28 RewardType: string;
29 CouponType: string;
30 Icon: string;
31 ItemType: string;
32 StoreItemType: string;
33 ProductCategory: string;
34 Amount: number;
35 ScalingMultiplier: number;
36 Durability: string;
37 DisplayName: string;
38 Duration: number;
39 CouponSku: number;
40 Syndicate: string;
41 Milestones: any[];
42 ChooseSetIndex: number;
43 NewSystemReward: boolean;
44 _id: IOid;
45 }
Modified src/controllers/api/updateQuestController.ts +15 -10
@@ -4,7 +4,8 @@ import { logger } from "@/src/utils/logger";
4 4 import { getJSONfromString } from "@/src/helpers/stringHelpers";
5 5 import { updateQuestKey, IUpdateQuestRequest } from "@/src/services/questService";
6 6 import { getQuestCompletionItems } from "@/src/services/itemDataService";
7 import { addItem, combineInventoryChanges, getInventory } from "@/src/services/inventoryService";
7 import { addItems, getInventory } from "@/src/services/inventoryService";
8 import { IInventoryChanges } from "@/src/types/purchaseTypes";
8 9
9 10 // eslint-disable-next-line @typescript-eslint/no-misused-promises
10 11 export const updateQuestController: RequestHandler = async (req, res) => {
@@ -18,24 +19,28 @@ export const updateQuestController: RequestHandler = async (req, res) => {
18 19
19 20 const inventory = await getInventory(accountId);
20 21
22 const updateQuestResponse: { CustomData?: string; InventoryChanges?: IInventoryChanges; MissionRewards: [] } = {
23 MissionRewards: []
24 };
21 25 updateQuestKey(inventory, updateQuestRequest.QuestKeys);
22 26
23 27 if (updateQuestRequest.QuestKeys[0].Completed) {
24 28 logger.debug(`completed quest ${updateQuestRequest.QuestKeys[0].ItemType} `);
25 29 const questKeyName = updateQuestRequest.QuestKeys[0].ItemType;
26 30 const questCompletionItems = getQuestCompletionItems(questKeyName);
31 logger.debug(`quest completion items`, questCompletionItems);
27 32
28 logger.debug(`quest completion items { ${questCompletionItems.map(item => item.ItemType).join(", ")} }`);
33 const inventoryChanges = await addItems(inventory, questCompletionItems);
34 inventory.ActiveQuest = "";
29 35
30 const inventoryChanges = {};
31 for (const item of questCompletionItems) {
32 const inventoryDelta = await addItem(inventory, item.ItemType, item.ItemCount);
33 combineInventoryChanges(inventoryChanges, inventoryDelta.InventoryChanges);
34 }
35 res.json({ MissionRewards: [], inventoryChanges });
36 return;
36 updateQuestResponse.InventoryChanges = inventoryChanges;
37 }
38
39 //TODO: might need to parse the custom data and add the associated items to inventory
40 if (updateQuestRequest.QuestKeys[0].CustomData) {
41 updateQuestResponse.CustomData = updateQuestRequest.QuestKeys[0].CustomData;
37 42 }
38 43
39 44 await inventory.save();
40 res.send({ MissionRewards: [] });
45 res.send(updateQuestResponse);
41 46 };
Modified src/models/inboxModel.ts +7 -4
@@ -12,23 +12,26 @@ export interface IMessageClient extends Omit<IMessageDatabase, "_id" | "date" |
12 12 messageId: IOid;
13 13 }
14 14
15 export interface IMessageDatabase {
15 export interface IMessageDatabase extends IMessage {
16 16 ownerId: Types.ObjectId;
17 date: Date;
17 date: Date; //created at
18 18 _id: Types.ObjectId;
19 }
20
21 export interface IMessage {
19 22 sndr: string;
20 23 msg: string;
21 24 sub: string;
22 icon: string;
25 icon?: string;
23 26 highPriority?: boolean;
24 27 lowPrioNewPlayers?: boolean;
25 28 startDate?: Date;
26 29 endDate?: Date;
27 r?: boolean;
28 30 att?: string[];
29 31 countedAtt?: ITypeCount[];
30 32 transmission?: string;
31 33 arg?: Arg[];
34 r?: boolean;
32 35 }
33 36
34 37 export interface Arg {
Modified src/models/inventoryModels/inventoryModel.ts +10 -7
@@ -479,19 +479,22 @@ const helminthResourceSchema = new Schema<IHelminthResource>(
479 479 { _id: false }
480 480 );
481 481
482 const questProgressSchema = new Schema<IQuestStage>({
483 c: Number,
484 i: Boolean,
485 m: Boolean,
486 b: []
487 });
482 const questProgressSchema = new Schema<IQuestStage>(
483 {
484 c: Number,
485 i: Boolean,
486 m: Boolean,
487 b: []
488 },
489 { _id: false }
490 );
488 491
489 492 const questKeysSchema = new Schema<IQuestKeyDatabase>(
490 493 {
491 494 Progress: { type: [questProgressSchema], default: undefined },
492 495 unlock: Boolean,
493 496 Completed: Boolean,
494 //CustomData: Schema.Types.Mixed,
497 CustomData: String,
495 498 CompletionDate: Date,
496 499 ItemType: String
497 500 },
Modified src/routes/api.ts +4 -0
@@ -82,6 +82,8 @@ import { updateQuestController } from "@/src/controllers/api/updateQuestControll
82 82 import { updateSessionGetController, updateSessionPostController } from "@/src/controllers/api/updateSessionController";
83 83 import { updateThemeController } from "../controllers/api/updateThemeController";
84 84 import { upgradesController } from "@/src/controllers/api/upgradesController";
85 import { giveKeyChainTriggeredMessageController } from "@/src/controllers/api/giveKeyChainTriggeredMessageController";
86 import { giveQuestKeyRewardController } from "@/src/controllers/api/giveQuestKey";
85 87
86 88 const apiRouter = express.Router();
87 89
@@ -138,6 +140,8 @@ apiRouter.post("/getAlliance.php", getAllianceController);
138 140 apiRouter.post("/getVoidProjectionRewards.php", getVoidProjectionRewardsController);
139 141 apiRouter.post("/gildWeapon.php", gildWeaponController);
140 142 apiRouter.post("/giveKeyChainTriggeredItems.php", giveKeyChainTriggeredItemsController);
143 apiRouter.post("/giveKeyChainTriggeredMessage.php", giveKeyChainTriggeredMessageController);
144 apiRouter.post("/giveQuestKeyReward.php", giveQuestKeyRewardController);
141 145 apiRouter.post("/guildTech.php", guildTechController);
142 146 apiRouter.post("/hostSession.php", hostSessionController);
143 147 apiRouter.post("/infestedFoundry.php", infestedFoundryController);
Modified src/services/inboxService.ts +0 -1
@@ -46,7 +46,6 @@ export const createNewEventMessages = async (req: Request) => {
46 46 prev.eventMessageDate > current.eventMessageDate ? prev : current
47 47 );
48 48
49 console.log("latestEventMessage", latestEventMessage);
50 49 account.LatestEventMessageDate = new Date(latestEventMessage.eventMessageDate);
51 50 await account.save();
52 51 };
Modified src/services/inventoryService.ts +11 -4
@@ -47,7 +47,7 @@ import {
47 47 } from "warframe-public-export-plus";
48 48 import { createShip } from "./shipService";
49 49 import { creditBundles, fusionBundles } from "@/src/services/missionInventoryUpdateService";
50 import { IGiveKeyChainTriggeredItemsRequest } from "@/src/controllers/api/giveKeyChainTriggeredItemsController";
50 import { IKeyChainRequest } from "@/src/controllers/api/giveKeyChainTriggeredItemsController";
51 51 import { toOid } from "../helpers/inventoryHelpers";
52 52
53 53 export const createInventory = async (
@@ -499,11 +499,16 @@ export const addItem = async (
499 499
500 500 export const addItems = async (
501 501 inventory: TInventoryDatabaseDocument,
502 items: ITypeCount[],
502 items: ITypeCount[] | string[],
503 503 inventoryChanges: IInventoryChanges = {}
504 504 ): Promise<IInventoryChanges> => {
505 let inventoryDelta;
505 506 for (const item of items) {
506 const inventoryDelta = await addItem(inventory, item.ItemType, item.ItemCount);
507 if (typeof item === "string") {
508 inventoryDelta = await addItem(inventory, item);
509 } else {
510 inventoryDelta = await addItem(inventory, item.ItemType, item.ItemCount);
511 }
507 512 combineInventoryChanges(inventoryChanges, inventoryDelta.InventoryChanges);
508 513 }
509 514 return inventoryChanges;
@@ -1074,7 +1079,7 @@ export const updateSyndicate = (
1074 1079 */
1075 1080 export const addKeyChainItems = async (
1076 1081 inventory: TInventoryDatabaseDocument,
1077 keyChainData: IGiveKeyChainTriggeredItemsRequest
1082 keyChainData: IKeyChainRequest
1078 1083 ): Promise<IInventoryChanges> => {
1079 1084 const keyChainItems = getKeyChainItems(keyChainData);
1080 1085
@@ -1092,5 +1097,7 @@ export const addKeyChainItems = async (
1092 1097 combineInventoryChanges(inventoryChanges, inventoryChangesDelta.InventoryChanges);
1093 1098 }
1094 1099
1100 await addItems(inventory, nonStoreItems);
1101
1095 1102 return inventoryChanges;
1096 1103 };
Modified src/services/itemDataService.ts +26 -3
@@ -1,4 +1,4 @@
1 import { IGiveKeyChainTriggeredItemsRequest } from "@/src/controllers/api/giveKeyChainTriggeredItemsController";
1 import { IKeyChainRequest } from "@/src/controllers/api/giveKeyChainTriggeredItemsController";
2 2 import { getIndexAfter } from "@/src/helpers/stringHelpers";
3 3 import { ITypeCount } from "@/src/types/inventoryTypes/inventoryTypes";
4 4 import { logger } from "@/src/utils/logger";
@@ -142,7 +142,7 @@ export const getString = (key: string, dict: Record<string, string>): string =>
142 142 return dict[key] ?? key;
143 143 };
144 144
145 export const getKeyChainItems = ({ KeyChain, ChainStage }: IGiveKeyChainTriggeredItemsRequest): string[] => {
145 export const getKeyChainItems = ({ KeyChain, ChainStage }: IKeyChainRequest): string[] => {
146 146 const chainStages = ExportKeys[KeyChain].chainStages;
147 147 if (!chainStages) {
148 148 throw new Error(`KeyChain ${KeyChain} does not contain chain stages`);
@@ -154,7 +154,9 @@ export const getKeyChainItems = ({ KeyChain, ChainStage }: IGiveKeyChainTriggere
154 154 }
155 155
156 156 if (keyChainStage.itemsToGiveWhenTriggered.length === 0) {
157 throw new Error(`No items to give for KeyChain ${KeyChain} at stage ${ChainStage}`);
157 throw new Error(
158 `client requested key chain items in KeyChain ${KeyChain} at stage ${ChainStage}, but they did not exist`
159 );
158 160 }
159 161
160 162 return keyChainStage.itemsToGiveWhenTriggered;
@@ -194,3 +196,24 @@ export const getQuestCompletionItems = (questKey: string) => {
194 196
195 197 return items;
196 198 };
199
200 export const getKeyChainMessage = ({ KeyChain, ChainStage }: IKeyChainRequest) => {
201 const chainStages = ExportKeys[KeyChain]?.chainStages;
202 if (!chainStages) {
203 throw new Error(`KeyChain ${KeyChain} does not contain chain stages`);
204 }
205
206 const keyChainStage = chainStages[ChainStage];
207 if (!keyChainStage) {
208 throw new Error(`KeyChainStage ${ChainStage} not found`);
209 }
210
211 const chainStageMessage = keyChainStage.messageToSendWhenTriggered;
212
213 if (!chainStageMessage) {
214 throw new Error(
215 `client requested key chain message in keychain ${KeyChain} at stage ${ChainStage} but they did not exist`
216 );
217 }
218 return chainStageMessage;
219 };
Modified src/services/questService.ts +40 -9
Modified src/types/inventoryTypes/inventoryTypes.ts +1 -1
Modified src/types/requestTypes.ts +3 -3