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

chore: auto-generate guild advertisment vendor (#1845)

With this, preprocessing is simplified to just refreshing expiry dates. No real change to auto-generation logic. Reviewed-on: https://onlyg.it/OpenWF/SpaceNinjaServer/pulls/1845 Co-authored-by: Sainan <63328889+Sainan@users.noreply.github.com> Co-committed-by: Sainan <63328889+Sainan@users.noreply.github.com>

90e97d78
Sainan <63328889+Sainan@users.noreply.github.com>
提交于

代码差异

3 个文件 +32 -157
Modified src/services/serversideVendorsService.ts +30 -38
@@ -1,13 +1,7 @@
1 1 import { unixTimesInMs } from "@/src/constants/timeConstants";
2 2 import { CRng, mixSeeds } from "@/src/services/rngService";
3 3 import { IMongoDate } from "@/src/types/commonTypes";
4 import {
5 IItemManifestPreprocessed,
6 IRawVendorManifest,
7 IVendorInfo,
8 IVendorInfoPreprocessed,
9 IVendorManifestPreprocessed
10 } from "@/src/types/vendorTypes";
4 import { IItemManifest, IVendorInfo, IVendorManifest } from "@/src/types/vendorTypes";
11 5 import { ExportVendors, IRange } from "warframe-public-export-plus";
12 6
13 7 import ArchimedeanVendorManifest from "@/static/fixed_responses/getVendorInfo/ArchimedeanVendorManifest.json";
@@ -24,7 +18,6 @@ import DeimosProspectorVendorManifest from "@/static/fixed_responses/getVendorIn
24 18 import DuviriAcrithisVendorManifest from "@/static/fixed_responses/getVendorInfo/DuviriAcrithisVendorManifest.json";
25 19 import EntratiLabsEntratiLabsCommisionsManifest from "@/static/fixed_responses/getVendorInfo/EntratiLabsEntratiLabsCommisionsManifest.json";
26 20 import EntratiLabsEntratiLabVendorManifest from "@/static/fixed_responses/getVendorInfo/EntratiLabsEntratiLabVendorManifest.json";
27 import GuildAdvertisementVendorManifest from "@/static/fixed_responses/getVendorInfo/GuildAdvertisementVendorManifest.json";
28 21 import HubsIronwakeDondaVendorManifest from "@/static/fixed_responses/getVendorInfo/HubsIronwakeDondaVendorManifest.json";
29 22 import HubsRailjackCrewMemberVendorManifest from "@/static/fixed_responses/getVendorInfo/HubsRailjackCrewMemberVendorManifest.json";
30 23 import MaskSalesmanManifest from "@/static/fixed_responses/getVendorInfo/MaskSalesmanManifest.json";
@@ -39,7 +32,7 @@ import SolarisProspectorVendorManifest from "@/static/fixed_responses/getVendorI
39 32 import TeshinHardModeVendorManifest from "@/static/fixed_responses/getVendorInfo/TeshinHardModeVendorManifest.json";
40 33 import ZarimanCommisionsManifestArchimedean from "@/static/fixed_responses/getVendorInfo/ZarimanCommisionsManifestArchimedean.json";
41 34
42 const rawVendorManifests: IRawVendorManifest[] = [
35 const rawVendorManifests: IVendorManifest[] = [
43 36 ArchimedeanVendorManifest,
44 37 DeimosEntratiFragmentVendorProductsManifest,
45 38 DeimosFishmongerVendorManifest,
@@ -54,7 +47,6 @@ const rawVendorManifests: IRawVendorManifest[] = [
54 47 DuviriAcrithisVendorManifest,
55 48 EntratiLabsEntratiLabsCommisionsManifest,
56 49 EntratiLabsEntratiLabVendorManifest,
57 GuildAdvertisementVendorManifest, // uses preprocessing
58 50 HubsIronwakeDondaVendorManifest, // uses preprocessing
59 51 HubsRailjackCrewMemberVendorManifest,
60 52 MaskSalesmanManifest,
@@ -71,8 +63,8 @@ const rawVendorManifests: IRawVendorManifest[] = [
71 63 ];
72 64
73 65 interface IGeneratableVendorInfo extends Omit<IVendorInfo, "ItemManifest" | "Expiry"> {
74 cycleOffset: number;
75 cycleDuration: number;
66 cycleOffset?: number;
67 cycleDuration?: number;
76 68 }
77 69
78 70 const generatableVendors: IGeneratableVendorInfo[] = [
@@ -99,9 +91,13 @@ const generatableVendors: IGeneratableVendorInfo[] = [
99 91 _id: { $oid: "5be4a159b144f3cdf1c22efa" },
100 92 TypeName: "/Lotus/Types/Game/VendorManifests/Solaris/DebtTokenVendorManifest",
101 93 PropertyTextHash: "A39621049CA3CA13761028CD21C239EF",
102 RandomSeedType: "VRST_FLAVOUR_TEXT",
103 cycleOffset: 1734307200_000,
104 cycleDuration: unixTimesInMs.hour
94 RandomSeedType: "VRST_FLAVOUR_TEXT"
95 },
96 {
97 _id: { $oid: "61ba123467e5d37975aeeb03" },
98 TypeName: "/Lotus/Types/Game/VendorManifests/Hubs/GuildAdvertisementVendorManifest",
99 PropertyTextHash: "255AFE2169BAE4130B4B20D7C55D14FA",
100 RandomSeedType: "VRST_FLAVOUR_TEXT"
105 101 }
106 102 // {
107 103 // _id: { $oid: "5dbb4c41e966f7886c3ce939" },
@@ -110,7 +106,7 @@ const generatableVendors: IGeneratableVendorInfo[] = [
110 106 // }
111 107 ];
112 108
113 export const getVendorManifestByTypeName = (typeName: string): IVendorManifestPreprocessed | undefined => {
109 export const getVendorManifestByTypeName = (typeName: string): IVendorManifest | undefined => {
114 110 for (const vendorManifest of rawVendorManifests) {
115 111 if (vendorManifest.VendorInfo.TypeName == typeName) {
116 112 return preprocessVendorManifest(vendorManifest);
@@ -124,7 +120,7 @@ export const getVendorManifestByTypeName = (typeName: string): IVendorManifestPr
124 120 return undefined;
125 121 };
126 122
127 export const getVendorManifestByOid = (oid: string): IVendorManifestPreprocessed | undefined => {
123 export const getVendorManifestByOid = (oid: string): IVendorManifest | undefined => {
128 124 for (const vendorManifest of rawVendorManifests) {
129 125 if (vendorManifest.VendorInfo._id.$oid == oid) {
130 126 return preprocessVendorManifest(vendorManifest);
@@ -138,29 +134,20 @@ export const getVendorManifestByOid = (oid: string): IVendorManifestPreprocessed
138 134 return undefined;
139 135 };
140 136
141 const preprocessVendorManifest = (originalManifest: IRawVendorManifest): IVendorManifestPreprocessed => {
137 const preprocessVendorManifest = (originalManifest: IVendorManifest): IVendorManifest => {
142 138 if (Date.now() >= parseInt(originalManifest.VendorInfo.Expiry.$date.$numberLong)) {
143 139 const manifest = structuredClone(originalManifest);
144 140 const info = manifest.VendorInfo;
145 141 refreshExpiry(info.Expiry);
146 142 for (const offer of info.ItemManifest) {
147 const iteration = refreshExpiry(offer.Expiry);
148 if (offer.ItemPrices) {
149 for (const price of offer.ItemPrices) {
150 if (typeof price.ItemType != "string") {
151 const itemSeed = parseInt(offer.Id.$oid.substring(16), 16);
152 const rng = new CRng(mixSeeds(itemSeed, iteration));
153 price.ItemType = rng.randomElement(price.ItemType);
154 }
155 }
156 }
143 refreshExpiry(offer.Expiry);
157 144 }
158 return manifest as IVendorManifestPreprocessed;
145 return manifest;
159 146 }
160 return originalManifest as IVendorManifestPreprocessed;
147 return originalManifest;
161 148 };
162 149
163 const refreshExpiry = (expiry: IMongoDate): number => {
150 const refreshExpiry = (expiry: IMongoDate): void => {
164 151 const period = parseInt(expiry.$date.$numberLong);
165 152 if (Date.now() >= period) {
166 153 const epoch = 1734307200_000; // Monday (for weekly schedules)
@@ -168,9 +155,7 @@ const refreshExpiry = (expiry: IMongoDate): number => {
168 155 const start = epoch + iteration * period;
169 156 const end = start + period;
170 157 expiry.$date.$numberLong = end.toString();
171 return iteration;
172 158 }
173 return 0;
174 159 };
175 160
176 161 const toRange = (value: IRange | number): IRange => {
@@ -180,9 +165,9 @@ const toRange = (value: IRange | number): IRange => {
180 165 return value;
181 166 };
182 167
183 const vendorInfoCache: Record<string, IVendorInfoPreprocessed> = {};
168 const vendorInfoCache: Record<string, IVendorInfo> = {};
184 169
185 const generateVendorManifest = (vendorInfo: IGeneratableVendorInfo): IVendorManifestPreprocessed => {
170 const generateVendorManifest = (vendorInfo: IGeneratableVendorInfo): IVendorManifest => {
186 171 if (!(vendorInfo.TypeName in vendorInfoCache)) {
187 172 // eslint-disable-next-line @typescript-eslint/no-unused-vars
188 173 const { cycleOffset, cycleDuration, ...clientVendorInfo } = vendorInfo;
@@ -205,7 +190,9 @@ const generateVendorManifest = (vendorInfo: IGeneratableVendorInfo): IVendorMani
205 190
206 191 // Add new offers
207 192 const vendorSeed = parseInt(vendorInfo._id.$oid.substring(16), 16);
208 const cycleIndex = Math.trunc((Date.now() - vendorInfo.cycleOffset) / vendorInfo.cycleDuration);
193 const cycleOffset = vendorInfo.cycleOffset ?? 1734307200_000;
194 const cycleDuration = vendorInfo.cycleDuration ?? unixTimesInMs.hour;
195 const cycleIndex = Math.trunc((Date.now() - cycleOffset) / cycleDuration);
209 196 const rng = new CRng(mixSeeds(vendorSeed, cycleIndex));
210 197 const manifest = ExportVendors[vendorInfo.TypeName];
211 198 const offersToAdd = [];
@@ -226,14 +213,19 @@ const generateVendorManifest = (vendorInfo: IGeneratableVendorInfo): IVendorMani
226 213 offersToAdd.push(rawItem);
227 214 }
228 215 }
216
217 // For most vendors, the offers seem to roughly be in reverse order from the manifest. Coda weapons are an odd exception.
218 if (!manifest.isOneBinPerCycle) {
219 offersToAdd.reverse();
220 }
229 221 }
230 const cycleStart = vendorInfo.cycleOffset + cycleIndex * vendorInfo.cycleDuration;
222 const cycleStart = cycleOffset + cycleIndex * cycleDuration;
231 223 for (const rawItem of offersToAdd) {
232 224 const durationHoursRange = toRange(rawItem.durationHours);
233 225 const expiry =
234 226 cycleStart +
235 227 rng.randomInt(durationHoursRange.minValue, durationHoursRange.maxValue) * unixTimesInMs.hour;
236 const item: IItemManifestPreprocessed = {
228 const item: IItemManifest = {
237 229 StoreItem: rawItem.storeItem,
238 230 ItemPrices: rawItem.itemPrices?.map(itemPrice => ({ ...itemPrice, ProductCategory: "MiscItems" })),
239 231 Bin: "BIN_" + rawItem.bin,
Modified src/types/vendorTypes.ts +2 -18
@@ -1,15 +1,11 @@
1 1 import { IMongoDate, IOid } from "./commonTypes";
2 2
3 3 export interface IItemPrice {
4 ItemType: string | string[]; // If string[], preprocessing will use RNG to pick one for the current period.
4 ItemType: string;
5 5 ItemCount: number;
6 6 ProductCategory: string;
7 7 }
8 8
9 export interface IItemPricePreprocessed extends Omit<IItemPrice, "ItemType"> {
10 ItemType: string;
11 }
12
13 9 export interface IItemManifest {
14 10 StoreItem: string;
15 11 ItemPrices?: IItemPrice[];
@@ -24,10 +20,6 @@ export interface IItemManifest {
24 20 Id: IOid;
25 21 }
26 22
27 export interface IItemManifestPreprocessed extends Omit<IItemManifest, "ItemPrices"> {
28 ItemPrices?: IItemPricePreprocessed[];
29 }
30
31 23 export interface IVendorInfo {
32 24 _id: IOid;
33 25 TypeName: string;
@@ -39,14 +31,6 @@ export interface IVendorInfo {
39 31 Expiry: IMongoDate; // Either a date in the distant future or a period in milliseconds for preprocessing.
40 32 }
41 33
42 export interface IVendorInfoPreprocessed extends Omit<IVendorInfo, "ItemManifest"> {
43 ItemManifest: IItemManifestPreprocessed[];
44 }
45
46 export interface IRawVendorManifest {
34 export interface IVendorManifest {
47 35 VendorInfo: IVendorInfo;
48 36 }
49
50 export interface IVendorManifestPreprocessed {
51 VendorInfo: IVendorInfoPreprocessed;
52 }
Deleted static/fixed_responses/getVendorInfo/GuildAdvertisementVendorManifest.json +0 -101
@@ -1,101 +0,0 @@
1 {
2 "VendorInfo": {
3 "_id": { "$oid": "61ba123467e5d37975aeeb03" },
4 "TypeName": "/Lotus/Types/Game/VendorManifests/Hubs/GuildAdvertisementVendorManifest",
5 "ItemManifest": [
6 {
7 "StoreItem": "/Lotus/StoreItems/Types/Items/Guild/GuildAdvertisementMoon",
8 "ItemPrices": [
9 {
10 "ItemType": ["/Lotus/Types/Items/Research/BioFragment", "/Lotus/Types/Items/Research/ChemComponent", "/Lotus/Types/Items/Research/EnergyFragment"],
11 "ItemCount": 12,
12 "ProductCategory": "MiscItems"
13 }
14 ],
15 "RegularPrice": [1, 1],
16 "Bin": "BIN_4",
17 "QuantityMultiplier": 1,
18 "Expiry": { "$date": { "$numberLong": "604800000" } },
19 "PurchaseQuantityLimit": 1,
20 "AllowMultipurchase": false,
21 "LocTagRandSeed": 79554843,
22 "Id": { "$oid": "67bbb592e1534511d6c1c1e2" }
23 },
24 {
25 "StoreItem": "/Lotus/StoreItems/Types/Items/Guild/GuildAdvertisementMountain",
26 "ItemPrices": [
27 {
28 "ItemType": ["/Lotus/Types/Items/Research/BioFragment", "/Lotus/Types/Items/Research/ChemComponent", "/Lotus/Types/Items/Research/EnergyFragment"],
29 "ItemCount": 7,
30 "ProductCategory": "MiscItems"
31 }
32 ],
33 "RegularPrice": [1, 1],
34 "Bin": "BIN_3",
35 "QuantityMultiplier": 1,
36 "Expiry": { "$date": { "$numberLong": "604800000" } },
37 "PurchaseQuantityLimit": 1,
38 "AllowMultipurchase": false,
39 "LocTagRandSeed": 2413820225,
40 "Id": { "$oid": "67bbb592e1534511d6c1c1e3" }
41 },
42 {
43 "StoreItem": "/Lotus/StoreItems/Types/Items/Guild/GuildAdvertisementStorm",
44 "ItemPrices": [
45 {
46 "ItemType": ["/Lotus/Types/Items/Research/BioFragment", "/Lotus/Types/Items/Research/ChemComponent", "/Lotus/Types/Items/Research/EnergyFragment"],
47 "ItemCount": 3,
48 "ProductCategory": "MiscItems"
49 }
50 ],
51 "RegularPrice": [1, 1],
52 "Bin": "BIN_2",
53 "QuantityMultiplier": 1,
54 "Expiry": { "$date": { "$numberLong": "604800000" } },
55 "PurchaseQuantityLimit": 1,
56 "AllowMultipurchase": false,
57 "LocTagRandSeed": 3262300883,
58 "Id": { "$oid": "67bbb592e1534511d6c1c1e4" }
59 },
60 {
61 "StoreItem": "/Lotus/StoreItems/Types/Items/Guild/GuildAdvertisementShadow",
62 "ItemPrices": [
63 {
64 "ItemType": ["/Lotus/Types/Items/Research/BioFragment", "/Lotus/Types/Items/Research/ChemComponent", "/Lotus/Types/Items/Research/EnergyFragment"],
65 "ItemCount": 20,
66 "ProductCategory": "MiscItems"
67 }
68 ],
69 "RegularPrice": [1, 1],
70 "Bin": "BIN_1",
71 "QuantityMultiplier": 1,
72 "Expiry": { "$date": { "$numberLong": "604800000" } },
73 "PurchaseQuantityLimit": 1,
74 "AllowMultipurchase": false,
75 "LocTagRandSeed": 2797325750,
76 "Id": { "$oid": "67bbb592e1534511d6c1c1e5" }
77 },
78 {
79 "StoreItem": "/Lotus/StoreItems/Types/Items/Guild/GuildAdvertisementGhost",
80 "ItemPrices": [
81 {
82 "ItemType": ["/Lotus/Types/Items/Research/BioFragment", "/Lotus/Types/Items/Research/ChemComponent", "/Lotus/Types/Items/Research/EnergyFragment"],
83 "ItemCount": 10,
84 "ProductCategory": "MiscItems"
85 }
86 ],
87 "RegularPrice": [1, 1],
88 "Bin": "BIN_0",
89 "QuantityMultiplier": 1,
90 "Expiry": { "$date": { "$numberLong": "604800000" } },
91 "PurchaseQuantityLimit": 1,
92 "AllowMultipurchase": false,
93 "LocTagRandSeed": 554932310,
94 "Id": { "$oid": "67bbb592e1534511d6c1c1e6" }
95 }
96 ],
97 "PropertyTextHash": "255AFE2169BAE4130B4B20D7C55D14FA",
98 "RandomSeedType": "VRST_FLAVOUR_TEXT",
99 "Expiry": { "$date": { "$numberLong": "604800000" } }
100 }
101 }