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: asyncMissionAssist (#3814)

Closes #3188 Reviewed-on: https://onlyg.it/OpenWF/SpaceNinjaServer/pulls/3814 Reviewed-by: Sainan <63328889+sainan@users.noreply.github.com> Co-authored-by: AMelonInsideLemon <166175391+AMelonInsideLemon@users.noreply.github.com> Co-committed-by: AMelonInsideLemon <166175391+AMelonInsideLemon@users.noreply.github.com>

f0cc75aa
AMelonInsideLemon <166175391+AMelonInsideLemon@users.noreply.github.com>
提交于

代码差异

4 个文件 +149 -2
Added src/controllers/api/asyncMissionAssistController.ts +136 -0
@@ -0,0 +1,136 @@
1 import type { RequestHandler } from "express";
2 import { getAccountForRequest, getUnicodeName, stripUnicodeSuffix } from "../../services/loginService.ts";
3 import type { IOid } from "../../types/commonTypes.ts";
4 import { Account } from "../../models/loginModel.ts";
5 import { Inventory } from "../../models/inventoryModels/inventoryModel.ts";
6 import type { IEquipmentClient } from "../../types/equipmentTypes.ts";
7 import { Loadout } from "../../models/inventoryModels/loadoutModel.ts";
8 import { LoadoutIndex } from "../../types/inventoryTypes/inventoryTypes.ts";
9 import { toObjectId } from "../../helpers/inventoryHelpers.ts";
10
11 export const asyncMissionAssistController: RequestHandler = async (req, res) => {
12 const account = await getAccountForRequest(req);
13 const payload = JSON.parse(String(req.body)) as IAsyncMissionAssistRequest;
14 const excludedOwnerIds = [account._id];
15 const accounts = await Account.find(
16 { DisplayName: { $in: payload.ExcludedNames.map(stripUnicodeSuffix) } },
17 { _id: 1 }
18 );
19 for (const acc of accounts) {
20 excludedOwnerIds.push(acc._id);
21 }
22 const candidateIds = payload.Candidates.map(c => toObjectId(c.$oid));
23 let inventories = await Inventory.find(
24 {
25 accountOwnerId: { $in: candidateIds, $nin: excludedOwnerIds },
26 QuestKeys: {
27 $elemMatch: { ItemType: "/Lotus/Types/Keys/OrokinMoonQuest/OrokinMoonQuestKeyChain", Completed: true }
28 },
29 OperatorSuits: { $elemMatch: { ItemType: "/Lotus/Powersuits/Operator/ChildOperatorSuitRemaster" } }
30 },
31 { _id: 0, accountOwnerId: 1, FocusAbility: 1, OperatorSuits: 1, CurrentLoadOutIds: 1 }
32 ).limit(payload.Count);
33 if (inventories.length < payload.Count) {
34 const remaining = payload.Count - inventories.length;
35 const otherInventories = await Inventory.aggregate([
36 {
37 $match: {
38 accountOwnerId: { $nin: [...excludedOwnerIds, ...inventories.map(i => i.accountOwnerId)] },
39 QuestKeys: {
40 $elemMatch: {
41 ItemType: "/Lotus/Types/Keys/OrokinMoonQuest/OrokinMoonQuestKeyChain",
42 Completed: true
43 }
44 },
45 OperatorSuits: { $elemMatch: { ItemType: "/Lotus/Powersuits/Operator/ChildOperatorSuitRemaster" } }
46 }
47 },
48 { $sample: { size: remaining } },
49 { $project: { _id: 0, accountOwnerId: 1, FocusAbility: 1, OperatorSuits: 1, CurrentLoadOutIds: 1 } }
50 ]);
51 inventories = inventories.concat(otherInventories);
52 }
53 const ownerIds = inventories.map(i => i.accountOwnerId);
54
55 const accountsRaw = await Account.find({ _id: { $in: ownerIds } });
56 const accountsMap = new Map(accountsRaw.map(a => [a._id.toString(), a]));
57 const loadoutsRaw = await Loadout.find(
58 { loadoutOwnerId: { $in: ownerIds } },
59 { _id: 0, loadoutOwnerId: 1, OPERATOR: 1 }
60 );
61 const loadoutMap = new Map(loadoutsRaw.map(l => [l.loadoutOwnerId.toString(), l]));
62 const resp: IAsyncMissionAssistResponce = {
63 Results: []
64 };
65
66 for (const inventory of inventories) {
67 const operatorDb = inventory.OperatorSuits.find(
68 o => o.ItemType == "/Lotus/Powersuits/Operator/ChildOperatorSuitRemaster"
69 );
70 const loadout = loadoutMap.get(inventory.accountOwnerId.toString());
71 const configNum =
72 loadout?.OPERATOR.find(l => l._id == inventory.CurrentLoadOutIds[LoadoutIndex.OPERATOR])?.s?.cus || 0;
73 const operator: Omit<IEquipmentClient, "ItemId"> = {
74 ItemType: operatorDb?.ItemType || "/Lotus/Powersuits/Operator/ChildOperatorSuitRemaster",
75 Configs: operatorDb?.Configs.slice(configNum, configNum + 1) || [],
76 UpgradeVer: 101
77 };
78 const a = accountsMap.get(inventory.accountOwnerId.toString())!;
79 const focusAbility = inventory.FocusAbility || "/Lotus/Upgrades/Focus/Power/PowerFocusAbility";
80 const result: IAsyncMissionAssistResult = {
81 DeploymentType: focusAbilityToDeploymentType[focusAbility],
82 FocusAbility: focusAbility,
83 Location: payload.MissionTag, // not faithful, but game doesn't complain?
84 Name: getUnicodeName(a, undefined) || "cat\uE000",
85 Operator: operator
86 };
87 resp.Results.push(result);
88 }
89
90 while (resp.Results.length < payload.Count) {
91 const focusAbilities = Object.keys(focusAbilityToDeploymentType);
92 const focusAbility = focusAbilities[Math.floor(Math.random() * focusAbilities.length)];
93 resp.Results.push({
94 DeploymentType: focusAbilityToDeploymentType[focusAbility],
95 FocusAbility: focusAbility,
96 Location: payload.MissionTag,
97 Name: "cat" + resp.Results.length + "\uE000",
98 Operator: {
99 ItemType: "/Lotus/Powersuits/Operator/ChildOperatorSuitRemaster",
100 Configs: [],
101 UpgradeVer: 101
102 }
103 });
104 }
105
106 res.json(resp);
107 };
108
109 interface IAsyncMissionAssistRequest {
110 MissionTag: string;
111 Count: number;
112 Candidates: IOid[];
113 ExcludedNames: string[];
114 }
115
116 interface IAsyncMissionAssistResult {
117 DeploymentType: TDeploymentType;
118 FocusAbility: string;
119 Location: string;
120 Name: string;
121 Operator: Omit<IEquipmentClient, "ItemId">;
122 }
123
124 interface IAsyncMissionAssistResponce {
125 Results: IAsyncMissionAssistResult[];
126 }
127
128 type TDeploymentType = "MADURAI" | "VAZARIN" | "ZENURIK" | "NARAMON" | "UNAIRU";
129
130 const focusAbilityToDeploymentType: Record<string, TDeploymentType> = {
131 "/Lotus/Upgrades/Focus/Attack/AttackFocusAbility": "MADURAI",
132 "/Lotus/Upgrades/Focus/Defense/DefenseFocusAbility": "VAZARIN",
133 "/Lotus/Upgrades/Focus/Power/PowerFocusAbility": "ZENURIK",
134 "/Lotus/Upgrades/Focus/Tactic/TacticFocusAbility": "NARAMON",
135 "/Lotus/Upgrades/Focus/Ward/WardFocusAbility": "UNAIRU"
136 };
Modified src/routes/api.ts +2 -0
@@ -17,6 +17,7 @@ import { arcaneCommonController } from "../controllers/api/arcaneCommonControlle
17 17 import { archonFusionController } from "../controllers/api/archonFusionController.ts";
18 18 import { artifactsController } from "../controllers/api/artifactsController.ts";
19 19 import { artifactTransmutationController } from "../controllers/api/artifactTransmutationController.ts";
20 import { asyncMissionAssistController } from "../controllers/api/asyncMissionAssistController.ts";
20 21 import { cancelGuildAdvertisementController } from "../controllers/api/cancelGuildAdvertisementController.ts";
21 22 import { changeDojoRootController } from "../controllers/api/changeDojoRootController.ts";
22 23 import { changeDojoSpawnRoomController } from "../controllers/api/changeDojoSpawnRoomController.ts";
@@ -283,6 +284,7 @@ apiRouter.post("/arcaneCommon.php", arcaneCommonController);
283 284 apiRouter.post("/archonFusion.php", archonFusionController);
284 285 apiRouter.post("/artifacts.php", artifactsController);
285 286 apiRouter.post("/artifactTransmutation.php", artifactTransmutationController);
287 apiRouter.post("/asyncMissionAssist.php", asyncMissionAssistController)
286 288 apiRouter.post("/changeDojoRoot.php", changeDojoRootController);
287 289 apiRouter.post("/changeDojoSpawnRoom.php", changeDojoSpawnRoomController);
288 290 apiRouter.post("/claimCompletedRecipe.php", claimCompletedRecipeController);
Modified src/services/loginService.ts +1 -1
@@ -164,7 +164,7 @@ export const getUnicodeName = (account: TAccountDocument, buildLabel: string | u
164 164 };
165 165
166 166 export const stripUnicodeSuffix = (name: string): string => {
167 if (name.charCodeAt(name.length) >= 0xe000) {
167 if (name.charCodeAt(name.length - 1) >= 0xe000) {
168 168 name = name.substring(0, name.length - 1);
169 169 }
170 170 return name;
Modified src/types/inventoryTypes/inventoryTypes.ts +10 -1
@@ -297,7 +297,16 @@ export type TSolarMapRegion =
297 297
298 298 export enum LoadoutIndex {
299 299 NORMAL = 0,
300 DATAKNIFE = 7
300 SENTINEL = 1,
301 ARCHWING = 2,
302 NORMAL_PVP = 3,
303 LUNARO = 4,
304 OPERATOR = 5,
305 KDRIVE = 6,
306 DATAKNIFE = 7,
307 MECH = 8,
308 OPERATOR_ADULT = 9,
309 DRIFTER = 10
301 310 }
302 311
303 312 export interface IDailyAffiliations {