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

Mission rewards save (#46)

db0aaffb
Nicat <52883148+holmityd@users.noreply.github.com>
提交于

代码差异

11 个文件 +292 -15
Modified .gitignore +1 -0
@@ -4,3 +4,4 @@
4 4 /.env
5 5 /static/data/*.bin
6 6 yarn.lock
7 /tmp
Modified src/controllers/api/missionInventoryUpdateController.ts +56 -1
@@ -1,6 +1,61 @@
1 1 import { RequestHandler } from "express";
2 import { missionInventoryUpdate } from "@/src/services/inventoryService";
3 import { MissionInventoryUpdate } from "@/src/types/missionInventoryUpdateType";
4 /*
5 - [ ] crossPlaySetting
6 - [ ] rewardsMultiplier
7 - [ ] ActiveBoosters
8 - [x] LongGuns
9 - [x] Pistols
10 - [x] Suits
11 - [x] Melee
12 - [x] RawUpgrades
13 - [x] MiscItems
14 - [x] RegularCredits
15 - [ ] RandomUpgradesIdentified
16 - [ ] MissionFailed
17 - [ ] MissionStatus
18 - [ ] CurrentLoadOutIds
19 - [ ] AliveTime
20 - [ ] MissionTime
21 - [ ] Missions
22 - [ ] CompletedAlerts
23 - [ ] LastRegionPlayed
24 - [ ] GameModeId
25 - [ ] hosts
26 - [x] ChallengeProgress
27 - [ ] SeasonChallengeHistory
28 - [ ] PS
29 - [ ] ActiveDojoColorResearch
30 - [ ] RewardInfo
31 - [ ] ReceivedCeremonyMsg
32 - [ ] LastCeremonyResetDate
33 - [ ] MissionPTS
34 - [ ] RepHash
35 - [ ] EndOfMatchUpload
36 - [ ] ObjectiveReached
37 - [ ] FpsAvg
38 - [ ] FpsMin
39 - [ ] FpsMax
40 - [ ] FpsSamples
41 */
2 42
3 const missionInventoryUpdateController: RequestHandler = (_req, res) => {
43 // eslint-disable-next-line @typescript-eslint/no-misused-promises
44 const missionInventoryUpdateController: RequestHandler = async (req, res) => {
45 const [data] = String(req.body).split("\n");
46 const id = req.query.accountId as string;
47
48 // TODO - salt check
49
50 try {
51 const parsedData = JSON.parse(data) as MissionInventoryUpdate;
52 if (typeof parsedData !== "object" || parsedData === null) throw new Error("Invalid data format");
53 await missionInventoryUpdate(parsedData, id);
54 } catch (err) {
55 console.error("Error parsing JSON data:", err);
56 }
57
58 // TODO - get original response
4 59 res.json({});
5 60 };
6 61
Added src/controllers/stats/viewController.ts +7 -0
@@ -0,0 +1,7 @@
1 import { RequestHandler } from "express";
2
3 const viewController: RequestHandler = (_req, res) => {
4 res.json({});
5 };
6
7 export { viewController };
Modified src/models/inventoryModel.ts +10 -2
@@ -1,5 +1,5 @@
1 1 import { Model, Schema, Types, model } from "mongoose";
2 import { FlavourItem, IInventoryDatabase } from "../types/inventoryTypes/inventoryTypes";
2 import { FlavourItem, RawUpgrade, MiscItem, IInventoryDatabase, Booster } from "../types/inventoryTypes/inventoryTypes";
3 3 import { Oid } from "../types/commonTypes";
4 4 import { ISuitDatabase, ISuitDocument } from "@/src/types/inventoryTypes/SuitTypes";
5 5 import { IWeaponDatabase } from "@/src/types/inventoryTypes/weaponTypes";
@@ -66,6 +66,11 @@ const WeaponSchema = new Schema({
66 66 UnlockLevel: Number
67 67 });
68 68
69 const BoosterSchema = new Schema({
70 ExpiryDate: Number,
71 ItemType: String
72 });
73
69 74 WeaponSchema.set("toJSON", {
70 75 transform(_document, returnedObject) {
71 76 // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment, @typescript-eslint/no-unsafe-call
@@ -213,7 +218,7 @@ const inventorySchema = new Schema<IInventoryDatabase, InventoryDocumentProps>({
213 218 LoreFragmentScans: [Schema.Types.Mixed],
214 219 EquippedEmotes: [String],
215 220 PendingTrades: [Schema.Types.Mixed],
216 Boosters: [Schema.Types.Mixed],
221 Boosters: [BoosterSchema],
217 222 ActiveDojoColorResearch: String,
218 223 SentientSpawnChanceBoosters: Schema.Types.Mixed,
219 224 Affiliations: [Schema.Types.Mixed],
@@ -334,6 +339,9 @@ type InventoryDocumentProps = {
334 339 Pistols: Types.DocumentArray<IWeaponDatabase>;
335 340 Melee: Types.DocumentArray<IWeaponDatabase>;
336 341 FlavourItems: Types.DocumentArray<FlavourItem>;
342 RawUpgrades: Types.DocumentArray<RawUpgrade>;
343 MiscItems: Types.DocumentArray<MiscItem>;
344 Boosters: Types.DocumentArray<Booster>;
337 345 };
338 346
339 347 type InventoryModelType = Model<IInventoryDatabase, {}, InventoryDocumentProps>;
Modified src/routes/stats.ts +4 -0
@@ -1,7 +1,11 @@
1 import { viewController } from "../controllers/api/viewController";
1 2 import { uploadController } from "@/src/controllers/stats/uploadController";
3
2 4 import express from "express";
3 5
4 6 const statsRouter = express.Router();
5 7
8 statsRouter.get("/view.php", viewController);
6 9 statsRouter.post("/upload.php", uploadController);
10
7 11 export { statsRouter };
Modified src/services/inventoryService.ts +100 -1
@@ -5,7 +5,13 @@ import { Types } from "mongoose";
5 5 import { ISuitResponse } from "@/src/types/inventoryTypes/SuitTypes";
6 6 import { SlotType } from "@/src/types/purchaseTypes";
7 7 import { IWeaponResponse } from "@/src/types/inventoryTypes/weaponTypes";
8 import { FlavourItem } from "@/src/types/inventoryTypes/inventoryTypes";
8 import { ChallengeProgress, FlavourItem, IInventoryDatabaseDocument } from "@/src/types/inventoryTypes/inventoryTypes";
9 import {
10 MissionInventoryUpdate,
11 MissionInventoryUpdateCard,
12 MissionInventoryUpdateGear,
13 MissionInventoryUpdateItem
14 } from "../types/missionInventoryUpdateType";
9 15
10 16 const createInventory = async (accountOwnerId: Types.ObjectId) => {
11 17 try {
@@ -106,4 +112,97 @@ export const addCustomization = async (customizatonName: string, accountId: stri
106 112 return changedInventory.FlavourItems[flavourItemIndex].toJSON(); //mongoose bug forces as FlavourItem
107 113 };
108 114
115 const addGearExpByCategory = (
116 inventory: IInventoryDatabaseDocument,
117 gearArray: MissionInventoryUpdateGear[] | undefined,
118 categoryName: "Pistols" | "LongGuns" | "Melee" | "Suits"
119 ) => {
120 const category = inventory[categoryName];
121
122 gearArray?.forEach(({ ItemId, XP }) => {
123 const itemIndex = category.findIndex(i => i._id?.equals(ItemId.$oid));
124 const item = category[itemIndex];
125
126 if (itemIndex !== -1 && item.XP != undefined) {
127 item.XP += XP;
128 inventory.markModified(`${categoryName}.${itemIndex}.XP`);
129 }
130 });
131 };
132
133 const addItemsByCategory = (
134 inventory: IInventoryDatabaseDocument,
135 itemsArray: (MissionInventoryUpdateItem | MissionInventoryUpdateCard)[] | undefined,
136 categoryName: "RawUpgrades" | "MiscItems"
137 ) => {
138 const category = inventory[categoryName];
139
140 itemsArray?.forEach(({ ItemCount, ItemType }) => {
141 const itemIndex = category.findIndex(i => i.ItemType === ItemType);
142
143 if (itemIndex !== -1) {
144 category[itemIndex].ItemCount += ItemCount;
145 inventory.markModified(`${categoryName}.${itemIndex}.ItemCount`);
146 } else {
147 category.push({ ItemCount, ItemType });
148 }
149 });
150 };
151
152 const addChallenges = (inventory: IInventoryDatabaseDocument, itemsArray: ChallengeProgress[] | undefined) => {
153 const category = inventory.ChallengeProgress;
154
155 itemsArray?.forEach(({ Name, Progress }) => {
156 const itemIndex = category.findIndex(i => i.Name === Name);
157
158 if (itemIndex !== -1) {
159 category[itemIndex].Progress += Progress;
160 inventory.markModified(`ChallengeProgress.${itemIndex}.ItemCount`);
161 } else {
162 category.push({ Name, Progress });
163 }
164 });
165 };
166
167 const gearKeys = ["Suits", "Pistols", "LongGuns", "Melee"] as const;
168 type GearKeysType = (typeof gearKeys)[number];
169
170 export const missionInventoryUpdate = async (data: MissionInventoryUpdate, accountId: string): Promise<void> => {
171 const { RawUpgrades, MiscItems, RegularCredits, ChallengeProgress } = data;
172 const inventory = await getInventory(accountId);
173
174 // TODO - multipliers logic
175 // credits
176 inventory.RegularCredits += RegularCredits || 0;
177
178 // gear exp
179 gearKeys.forEach((key: GearKeysType) => addGearExpByCategory(inventory, data[key], key));
180
181 // other
182 addItemsByCategory(inventory, RawUpgrades, "RawUpgrades"); // TODO - check mods fusion level
183 addItemsByCategory(inventory, MiscItems, "MiscItems");
184 addChallenges(inventory, ChallengeProgress);
185
186 await inventory.save();
187 };
188
189 export const addBooster = async (ItemType: string, time: number, accountId: string): Promise<void> => {
190 const currentTime = Math.floor(Date.now() / 1000) - 129600; // booster time getting more without 129600, probably defence logic, idk
191
192 const inventory = await getInventory(accountId);
193 const { Boosters } = inventory;
194
195 const itemIndex = Boosters.findIndex(i => i.ItemType === ItemType);
196
197 if (itemIndex !== -1) {
198 const existingBooster = Boosters[itemIndex];
199 existingBooster.ExpiryDate = Math.max(existingBooster.ExpiryDate, currentTime) + time;
200 inventory.markModified(`Boosters.${itemIndex}.ExpiryDate`);
201 } else {
202 Boosters.push({ ItemType, ExpiryDate: currentTime + time }) - 1;
203 }
204
205 await inventory.save();
206 };
207
109 208 export { createInventory, addPowerSuit };
Modified src/services/purchaseService.ts +31 -8
@@ -1,13 +1,7 @@
1 1 import { getWeaponType } from "@/src/helpers/purchaseHelpers";
2 2 import { getSubstringFromKeyword } from "@/src/helpers/stringHelpers";
3 import {
4 addCustomization,
5 addPowerSuit,
6 addWeapon,
7 updateCurrency,
8 updateSlots
9 } from "@/src/services/inventoryService";
10 import { IPurchaseRequest, IPurchaseResponse, SlotType } from "@/src/types/purchaseTypes";
3 import { addBooster, addCustomization, addPowerSuit, addWeapon, updateSlots } from "@/src/services/inventoryService";
4 import { IPurchaseRequest, SlotType } from "@/src/types/purchaseTypes";
11 5
12 6 export const getStoreItemCategory = (storeItem: string) => {
13 7 const storeItemString = getSubstringFromKeyword(storeItem, "StoreItems/");
@@ -41,6 +35,9 @@ export const handlePurchase = async (purchaseRequest: IPurchaseRequest, accountI
41 35 case "Types":
42 36 purchaseResponse = await handleTypesPurchase(internalName, accountId);
43 37 break;
38 case "Boosters":
39 purchaseResponse = await handleBoostersPurchase(internalName, accountId);
40 break;
44 41
45 42 default:
46 43 throw new Error(`unknown store category: ${storeCategory} not implemented or new`);
@@ -114,3 +111,29 @@ const handleSuitCustomizationsPurchase = async (customizationName: string, accou
114 111 }
115 112 };
116 113 };
114
115 const boosterCollection = [
116 "/Lotus/Types/Boosters/ResourceAmountBooster",
117 "/Lotus/Types/Boosters/AffinityBooster",
118 "/Lotus/Types/Boosters/ResourceDropChanceBooster",
119 "/Lotus/Types/Boosters/CreditBooster"
120 ];
121
122 const handleBoostersPurchase = async (boosterStoreName: string, accountId: string) => {
123 const match = boosterStoreName.match(/(\d+)Day/);
124 if (!match) return;
125
126 const extractedDigit = Number(match[1]);
127 const ItemType = boosterCollection.find(i =>
128 boosterStoreName.includes(i.split("/").pop()!.replace("Booster", ""))
129 )!;
130 const ExpiryDate = extractedDigit * 86400;
131
132 await addBooster(ItemType, ExpiryDate, accountId);
133
134 return {
135 InventoryChanges: {
136 Boosters: [{ ItemType, ExpiryDate }]
137 }
138 };
139 };
Modified src/types/inventoryTypes/SuitTypes.ts +6 -2
@@ -1,8 +1,11 @@
1 1 import { Oid } from "@/src/types/commonTypes";
2 2 import { AbilityOverride, Color, Polarity } from "@/src/types/inventoryTypes/commonInventoryTypes";
3 import { Document } from "mongoose";
3 import { Document, Types } from "mongoose";
4 4
5 export interface ISuitDocument extends ISuitResponse, Document {}
5 // export interface ISuitDocument extends ISuitResponse, Document {}
6 export interface ISuitDocument extends Document, ISuitResponse {
7 _id: Types.ObjectId;
8 }
6 9
7 10 export interface ISuitResponse extends ISuitDatabase {
8 11 ItemId: Oid;
@@ -20,6 +23,7 @@ export interface ISuitDatabase {
20 23 ModSlotPurchases?: number;
21 24 FocusLens?: string;
22 25 UnlockLevel?: number;
26 _id: Types.ObjectId;
23 27 }
24 28
25 29 export interface SuitConfig {
Modified src/types/inventoryTypes/inventoryTypes.ts +11 -1
@@ -377,6 +377,16 @@ export interface FlavourItem {
377 377 ItemType: string;
378 378 }
379 379
380 export interface RawUpgrade {
381 ItemCount: number;
382 ItemType: string;
383 }
384
385 export interface MiscItem {
386 ItemCount: number;
387 ItemType: string;
388 }
389
380 390 export interface CrewshipWeapon {
381 391 PILOT: Pilot;
382 392 PORT_GUNS: PortGuns;
@@ -919,7 +929,7 @@ export interface Progress {
919 929
920 930 export interface RawUpgrade {
921 931 ItemCount: number;
922 LastAdded: Oid;
932 LastAdded?: Oid;
923 933 ItemType: string;
924 934 }
925 935
Modified src/types/inventoryTypes/weaponTypes.ts +2 -0
@@ -1,5 +1,6 @@
1 1 import { Oid } from "@/src/types/commonTypes";
2 2 import { Color, Polarity } from "@/src/types/inventoryTypes/commonInventoryTypes";
3 import { Types } from "mongoose";
3 4
4 5 export interface IWeaponResponse extends IWeaponDatabase {
5 6 ItemId: Oid;
@@ -20,6 +21,7 @@ export interface IWeaponDatabase {
20 21 ItemName?: string;
21 22 ModularParts?: string[];
22 23 UnlockLevel?: number;
24 _id?: Types.ObjectId;
23 25 }
24 26
25 27 export interface WeaponConfig {
Added src/types/missionInventoryUpdateType.ts +64 -0
@@ -0,0 +1,64 @@
1 interface MongooseId{
2 $oid: string;
3 }
4
5 interface ExpireDate{
6 $date: {
7 $numberLong: string;
8 }
9 }
10
11 export interface MissionInventoryUpdateGear{
12 ItemType: string;
13 ItemName: string;
14 ItemId: MongooseId;
15 XP: number;
16 UpgradeVer: number;
17 Features: number;
18 Polarized: number;
19 CustomizationSlotPurchases: number;
20 ModSlotPurchases: number;
21 FocusLens: string;
22 Expiry: ExpireDate;
23 Polarity: any[];
24 Configs: any[];
25 ModularParts: any[];
26 SkillTree: string;
27 UpgradeType: string;
28 UpgradeFingerprint: string;
29 OffensiveUpgrade: string;
30 DefensiveUpgrade: string;
31 UpgradesExpiry: ExpireDate;
32 ArchonCrystalUpgrades: any[];
33 }
34
35 export interface MissionInventoryUpdateItem{
36 ItemCount: number;
37 ItemType: string;
38 }
39
40 export interface MissionInventoryUpdateCard extends MissionInventoryUpdateItem{
41 ItemId: MongooseId;
42 UpgradeFingerprint: string;
43 PendingRerollFingerprint: string;
44 LastAdded: MongooseId;
45 }
46
47 interface MissionInventoryUpdateChallange{
48 Name: string;
49 Progress: number;
50 Completed: any[];
51 }
52
53 export interface MissionInventoryUpdate{
54 rewardsMultiplier?: number;
55 ActiveBoosters?: any[];
56 LongGuns?: MissionInventoryUpdateGear[];
57 Pistols?: MissionInventoryUpdateGear[];
58 Suits?: MissionInventoryUpdateGear[];
59 Melee?: MissionInventoryUpdateGear[];
60 RawUpgrades?: MissionInventoryUpdateCard[];
61 MiscItems?: MissionInventoryUpdateItem[];
62 RegularCredits?: number;
63 ChallengeProgress?: MissionInventoryUpdateChallange[];
64 }