返回提交历史
Modified
src/services/serversideVendorsService.ts
+30
-38
Modified
src/types/vendorTypes.ts
+2
-18
Deleted
static/fixed_responses/getVendorInfo/GuildAdvertisementVendorManifest.json
+0
-101
XFEstudio/SpaceNinjaServer
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
代码差异
3 个文件
+32
-157
@@ -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,
@@ -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
}
@@ -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
}