返回提交历史
Added
src/helpers/pathHelper.ts
+4
-0
Modified
src/routes/webui.ts
+1
-2
Modified
src/services/buildConfigService.ts
+1
-2
Modified
src/services/configService.ts
+2
-3
Modified
src/services/inventoryService.ts
+23
-12
Modified
src/services/purchaseService.ts
+11
-3
Modified
src/services/rngService.ts
+9
-0
Modified
src/services/serversideVendorsService.ts
+38
-60
Modified
src/types/vendorTypes.ts
+1
-0
Added
static/fixed_responses/getVendorInfo/InfestedLichWeaponVendorManifest.json
+157
-0
XFEstudio/XFESpaceNinjaServer
feat: eleanor weapon offerings (#1419)
Need to do rotating offers for her some other time Reviewed-on: https://onlyg.it/OpenWF/SpaceNinjaServer/pulls/1419 Co-authored-by: Sainan <63328889+Sainan@users.noreply.github.com> Co-committed-by: Sainan <63328889+Sainan@users.noreply.github.com>
bf67a439
代码差异
10 个文件
+247
-82
@@ -0,0 +1,4 @@
1
import path from "path";
2
3
export const rootDir = path.join(__dirname, "../..");
4
export const repoDir = path.basename(rootDir) == "build" ? path.join(rootDir, "..") : rootDir;
@@ -1,9 +1,8 @@
1
1
import express from "express";
2
2
import path from "path";
3
import { repoDir, rootDir } from "@/src/helpers/pathHelper";
3
4
4
5
const webuiRouter = express.Router();
5
const rootDir = path.join(__dirname, "../..");
6
const repoDir = path.basename(rootDir) == "build" ? path.join(rootDir, "..") : rootDir;
7
6
8
7
// Redirect / to /webui/
9
8
webuiRouter.get("/", (_req, res) => {
@@ -1,5 +1,6 @@
1
1
import path from "path";
2
2
import fs from "fs";
3
import { repoDir } from "@/src/helpers/pathHelper";
3
4
4
5
interface IBuildConfig {
5
6
version: string;
@@ -13,8 +14,6 @@ export const buildConfig: IBuildConfig = {
13
14
matchmakingBuildId: ""
14
15
};
15
16
16
const rootDir = path.join(__dirname, "../..");
17
const repoDir = path.basename(rootDir) == "build" ? path.join(rootDir, "..") : rootDir;
18
17
const buildConfigPath = path.join(repoDir, "static/data/buildConfig.json");
19
18
if (fs.existsSync(buildConfigPath)) {
20
19
Object.assign(buildConfig, JSON.parse(fs.readFileSync(buildConfigPath, "utf-8")) as IBuildConfig);
@@ -1,10 +1,9 @@
1
import path from "path";
2
1
import fs from "fs";
3
2
import fsPromises from "fs/promises";
3
import path from "path";
4
import { repoDir } from "@/src/helpers/pathHelper";
4
5
import { logger } from "@/src/utils/logger";
5
6
6
const rootDir = path.join(__dirname, "../..");
7
const repoDir = path.basename(rootDir) == "build" ? path.join(rootDir, "..") : rootDir;
8
7
const configPath = path.join(repoDir, "config.json");
9
8
export const config = JSON.parse(fs.readFileSync(configPath, "utf-8")) as IConfig;
10
9
@@ -69,7 +69,7 @@ import { addStartingGear } from "@/src/controllers/api/giveStartingGearControlle
69
69
import { addQuestKey, completeQuest } from "@/src/services/questService";
70
70
import { handleBundleAcqusition } from "./purchaseService";
71
71
import libraryDailyTasks from "@/static/fixed_responses/libraryDailyTasks.json";
72
import { getRandomElement, getRandomInt } from "./rngService";
72
import { getRandomElement, getRandomInt, SRng } from "./rngService";
73
73
import { createMessage } from "./inboxService";
74
74
75
75
export const createInventory = async (
@@ -230,7 +230,8 @@ export const addItem = async (
230
230
inventory: TInventoryDatabaseDocument,
231
231
typeName: string,
232
232
quantity: number = 1,
233
premiumPurchase: boolean = false
233
premiumPurchase: boolean = false,
234
seed?: bigint
234
235
): Promise<IInventoryChanges> => {
235
236
// Bundles are technically StoreItems but a) they don't have a normal counterpart, and b) they are used in non-StoreItem contexts, e.g. email attachments.
236
237
if (typeName in ExportBundles) {
@@ -380,21 +381,31 @@ export const addItem = async (
380
381
defaultOverwrites.Features = EquipmentFeatures.DOUBLE_CAPACITY;
381
382
}
382
383
if (weapon.maxLevelCap == 40 && typeName.indexOf("BallasSword") == -1) {
384
if (!seed) {
385
seed = BigInt(Math.round(Math.random() * Number.MAX_SAFE_INTEGER));
386
}
387
const rng = new SRng(seed);
388
const tag = rng.randomElement([
389
"InnateElectricityDamage",
390
"InnateFreezeDamage",
391
"InnateHeatDamage",
392
"InnateImpactDamage",
393
"InnateMagDamage",
394
"InnateRadDamage",
395
"InnateToxinDamage"
396
]);
397
const WeaponUpgradeValueAttenuationExponent = 2.25;
398
let value = Math.pow(rng.randomFloat(), WeaponUpgradeValueAttenuationExponent);
399
if (value >= 0.941428) {
400
value = 1;
401
}
383
402
defaultOverwrites.UpgradeType = "/Lotus/Weapons/Grineer/KuvaLich/Upgrades/InnateDamageRandomMod";
384
403
defaultOverwrites.UpgradeFingerprint = JSON.stringify({
385
404
compat: typeName,
386
405
buffs: [
387
406
{
388
Tag: getRandomElement([
389
"InnateElectricityDamage",
390
"InnateFreezeDamage",
391
"InnateHeatDamage",
392
"InnateImpactDamage",
393
"InnateMagDamage",
394
"InnateRadDamage",
395
"InnateToxinDamage"
396
]),
397
Value: Math.trunc(Math.random() * 0x40000000)
407
Tag: tag,
408
Value: Math.trunc(value * 0x40000000)
398
409
}
399
410
]
400
411
});
@@ -51,6 +51,7 @@ export const handlePurchase = async (
51
51
logger.debug("purchase request", purchaseRequest);
52
52
53
53
const prePurchaseInventoryChanges: IInventoryChanges = {};
54
let seed: bigint | undefined;
54
55
if (purchaseRequest.PurchaseParams.Source == 7) {
55
56
const rawManifest = getVendorManifestByOid(purchaseRequest.PurchaseParams.SourceId!);
56
57
if (rawManifest) {
@@ -74,6 +75,9 @@ export const handlePurchase = async (
74
75
prePurchaseInventoryChanges
75
76
);
76
77
}
78
if (offer.LocTagRandSeed !== undefined) {
79
seed = BigInt(offer.LocTagRandSeed);
80
}
77
81
if (!config.noVendorPurchaseLimits && ItemId) {
78
82
inventory.RecentVendorPurchases ??= [];
79
83
let vendorPurchases = inventory.RecentVendorPurchases.find(
@@ -136,7 +140,10 @@ export const handlePurchase = async (
136
140
const purchaseResponse = await handleStoreItemAcquisition(
137
141
purchaseRequest.PurchaseParams.StoreItem,
138
142
inventory,
139
purchaseRequest.PurchaseParams.Quantity
143
purchaseRequest.PurchaseParams.Quantity,
144
undefined,
145
undefined,
146
seed
140
147
);
141
148
combineInventoryChanges(purchaseResponse.InventoryChanges, prePurchaseInventoryChanges);
142
149
@@ -324,7 +331,8 @@ export const handleStoreItemAcquisition = async (
324
331
inventory: TInventoryDatabaseDocument,
325
332
quantity: number = 1,
326
333
durability: TRarity = "COMMON",
327
ignorePurchaseQuantity: boolean = false
334
ignorePurchaseQuantity: boolean = false,
335
seed?: bigint
328
336
): Promise<IPurchaseResponse> => {
329
337
let purchaseResponse = {
330
338
InventoryChanges: {}
@@ -345,7 +353,7 @@ export const handleStoreItemAcquisition = async (
345
353
}
346
354
switch (storeCategory) {
347
355
default: {
348
purchaseResponse = { InventoryChanges: await addItem(inventory, internalName, quantity, true) };
356
purchaseResponse = { InventoryChanges: await addItem(inventory, internalName, quantity, true, seed) };
349
357
break;
350
358
}
351
359
case "Types":
@@ -127,4 +127,13 @@ export class SRng {
127
127
}
128
128
return min;
129
129
}
130
131
randomElement<T>(arr: T[]): T {
132
return arr[this.randomInt(0, arr.length - 1)];
133
}
134
135
randomFloat(): number {
136
this.state = (0x5851f42d4c957f2dn * this.state + 0x14057b7ef767814fn) & 0xffffffffffffffffn;
137
return (Number(this.state >> 38n) & 0xffffff) * 0.000000059604645;
138
}
130
139
}
@@ -1,69 +1,47 @@
1
import fs from "fs";
2
import path from "path";
3
import { repoDir } from "@/src/helpers/pathHelper";
1
4
import { CRng, mixSeeds } from "@/src/services/rngService";
2
5
import { IMongoDate } from "@/src/types/commonTypes";
3
6
import { IVendorManifest, IVendorManifestPreprocessed } from "@/src/types/vendorTypes";
7
import { JSONParse } from "json-with-bigint";
4
8
5
import ArchimedeanVendorManifest from "@/static/fixed_responses/getVendorInfo/ArchimedeanVendorManifest.json";
6
import DeimosEntratiFragmentVendorProductsManifest from "@/static/fixed_responses/getVendorInfo/DeimosEntratiFragmentVendorProductsManifest.json";
7
import DeimosFishmongerVendorManifest from "@/static/fixed_responses/getVendorInfo/DeimosFishmongerVendorManifest.json";
8
import DeimosHivemindCommisionsManifestFishmonger from "@/static/fixed_responses/getVendorInfo/DeimosHivemindCommisionsManifestFishmonger.json";
9
import DeimosHivemindCommisionsManifestPetVendor from "@/static/fixed_responses/getVendorInfo/DeimosHivemindCommisionsManifestPetVendor.json";
10
import DeimosHivemindCommisionsManifestProspector from "@/static/fixed_responses/getVendorInfo/DeimosHivemindCommisionsManifestProspector.json";
11
import DeimosHivemindCommisionsManifestTokenVendor from "@/static/fixed_responses/getVendorInfo/DeimosHivemindCommisionsManifestTokenVendor.json";
12
import DeimosHivemindCommisionsManifestWeaponsmith from "@/static/fixed_responses/getVendorInfo/DeimosHivemindCommisionsManifestWeaponsmith.json";
13
import DeimosHivemindTokenVendorManifest from "@/static/fixed_responses/getVendorInfo/DeimosHivemindTokenVendorManifest.json";
14
import DeimosPetVendorManifest from "@/static/fixed_responses/getVendorInfo/DeimosPetVendorManifest.json";
15
import DeimosProspectorVendorManifest from "@/static/fixed_responses/getVendorInfo/DeimosProspectorVendorManifest.json";
16
import DuviriAcrithisVendorManifest from "@/static/fixed_responses/getVendorInfo/DuviriAcrithisVendorManifest.json";
17
import EntratiLabsEntratiLabsCommisionsManifest from "@/static/fixed_responses/getVendorInfo/EntratiLabsEntratiLabsCommisionsManifest.json";
18
import EntratiLabsEntratiLabVendorManifest from "@/static/fixed_responses/getVendorInfo/EntratiLabsEntratiLabVendorManifest.json";
19
import GuildAdvertisementVendorManifest from "@/static/fixed_responses/getVendorInfo/GuildAdvertisementVendorManifest.json";
20
import HubsIronwakeDondaVendorManifest from "@/static/fixed_responses/getVendorInfo/HubsIronwakeDondaVendorManifest.json";
21
import HubsPerrinSequenceWeaponVendorManifest from "@/static/fixed_responses/getVendorInfo/HubsPerrinSequenceWeaponVendorManifest.json";
22
import HubsRailjackCrewMemberVendorManifest from "@/static/fixed_responses/getVendorInfo/HubsRailjackCrewMemberVendorManifest.json";
23
import MaskSalesmanManifest from "@/static/fixed_responses/getVendorInfo/MaskSalesmanManifest.json";
24
import Nova1999ConquestShopManifest from "@/static/fixed_responses/getVendorInfo/Nova1999ConquestShopManifest.json";
25
import OstronFishmongerVendorManifest from "@/static/fixed_responses/getVendorInfo/OstronFishmongerVendorManifest.json";
26
import OstronPetVendorManifest from "@/static/fixed_responses/getVendorInfo/OstronPetVendorManifest.json";
27
import OstronProspectorVendorManifest from "@/static/fixed_responses/getVendorInfo/OstronProspectorVendorManifest.json";
28
import RadioLegionIntermission12VendorManifest from "@/static/fixed_responses/getVendorInfo/RadioLegionIntermission12VendorManifest.json";
29
import SolarisDebtTokenVendorManifest from "@/static/fixed_responses/getVendorInfo/SolarisDebtTokenVendorManifest.json";
30
import SolarisDebtTokenVendorRepossessionsManifest from "@/static/fixed_responses/getVendorInfo/SolarisDebtTokenVendorRepossessionsManifest.json";
31
import SolarisFishmongerVendorManifest from "@/static/fixed_responses/getVendorInfo/SolarisFishmongerVendorManifest.json";
32
import SolarisProspectorVendorManifest from "@/static/fixed_responses/getVendorInfo/SolarisProspectorVendorManifest.json";
33
import TeshinHardModeVendorManifest from "@/static/fixed_responses/getVendorInfo/TeshinHardModeVendorManifest.json";
34
import ZarimanCommisionsManifestArchimedean from "@/static/fixed_responses/getVendorInfo/ZarimanCommisionsManifestArchimedean.json";
9
const getVendorManifestJson = (name: string): IVendorManifest => {
10
return JSONParse(fs.readFileSync(path.join(repoDir, `static/fixed_responses/getVendorInfo/${name}.json`), "utf-8"));
11
};
35
12
36
13
const vendorManifests: IVendorManifest[] = [
37
ArchimedeanVendorManifest,
38
DeimosEntratiFragmentVendorProductsManifest,
39
DeimosFishmongerVendorManifest,
40
DeimosHivemindCommisionsManifestFishmonger,
41
DeimosHivemindCommisionsManifestPetVendor,
42
DeimosHivemindCommisionsManifestProspector,
43
DeimosHivemindCommisionsManifestTokenVendor,
44
DeimosHivemindCommisionsManifestWeaponsmith,
45
DeimosHivemindTokenVendorManifest,
46
DeimosPetVendorManifest,
47
DeimosProspectorVendorManifest,
48
DuviriAcrithisVendorManifest,
49
EntratiLabsEntratiLabsCommisionsManifest,
50
EntratiLabsEntratiLabVendorManifest,
51
GuildAdvertisementVendorManifest, // uses preprocessing
52
HubsIronwakeDondaVendorManifest, // uses preprocessing
53
HubsPerrinSequenceWeaponVendorManifest,
54
HubsRailjackCrewMemberVendorManifest,
55
MaskSalesmanManifest,
56
Nova1999ConquestShopManifest,
57
OstronFishmongerVendorManifest,
58
OstronPetVendorManifest,
59
OstronProspectorVendorManifest,
60
RadioLegionIntermission12VendorManifest,
61
SolarisDebtTokenVendorManifest,
62
SolarisDebtTokenVendorRepossessionsManifest,
63
SolarisFishmongerVendorManifest,
64
SolarisProspectorVendorManifest,
65
TeshinHardModeVendorManifest, // uses preprocessing
66
ZarimanCommisionsManifestArchimedean
14
getVendorManifestJson("ArchimedeanVendorManifest"),
15
getVendorManifestJson("DeimosEntratiFragmentVendorProductsManifest"),
16
getVendorManifestJson("DeimosFishmongerVendorManifest"),
17
getVendorManifestJson("DeimosHivemindCommisionsManifestFishmonger"),
18
getVendorManifestJson("DeimosHivemindCommisionsManifestPetVendor"),
19
getVendorManifestJson("DeimosHivemindCommisionsManifestProspector"),
20
getVendorManifestJson("DeimosHivemindCommisionsManifestTokenVendor"),
21
getVendorManifestJson("DeimosHivemindCommisionsManifestWeaponsmith"),
22
getVendorManifestJson("DeimosHivemindTokenVendorManifest"),
23
getVendorManifestJson("DeimosPetVendorManifest"),
24
getVendorManifestJson("DeimosProspectorVendorManifest"),
25
getVendorManifestJson("DuviriAcrithisVendorManifest"),
26
getVendorManifestJson("EntratiLabsEntratiLabsCommisionsManifest"),
27
getVendorManifestJson("EntratiLabsEntratiLabVendorManifest"),
28
getVendorManifestJson("GuildAdvertisementVendorManifest"), // uses preprocessing
29
getVendorManifestJson("HubsIronwakeDondaVendorManifest"), // uses preprocessing
30
getVendorManifestJson("HubsPerrinSequenceWeaponVendorManifest"),
31
getVendorManifestJson("HubsRailjackCrewMemberVendorManifest"),
32
getVendorManifestJson("InfestedLichWeaponVendorManifest"),
33
getVendorManifestJson("MaskSalesmanManifest"),
34
getVendorManifestJson("Nova1999ConquestShopManifest"),
35
getVendorManifestJson("OstronFishmongerVendorManifest"),
36
getVendorManifestJson("OstronPetVendorManifest"),
37
getVendorManifestJson("OstronProspectorVendorManifest"),
38
getVendorManifestJson("RadioLegionIntermission12VendorManifest"),
39
getVendorManifestJson("SolarisDebtTokenVendorManifest"),
40
getVendorManifestJson("SolarisDebtTokenVendorRepossessionsManifest"),
41
getVendorManifestJson("SolarisFishmongerVendorManifest"),
42
getVendorManifestJson("SolarisProspectorVendorManifest"),
43
getVendorManifestJson("TeshinHardModeVendorManifest"), // uses preprocessing
44
getVendorManifestJson("ZarimanCommisionsManifestArchimedean")
67
45
];
68
46
69
47
export const getVendorManifestByTypeName = (typeName: string): IVendorManifest | undefined => {
@@ -19,6 +19,7 @@ interface IItemManifest {
19
19
PurchaseQuantityLimit?: number;
20
20
RotatedWeekly?: boolean;
21
21
AllowMultipurchase: boolean;
22
LocTagRandSeed?: number | bigint;
22
23
Id: IOid;
23
24
}
24
25
@@ -0,0 +1,157 @@
1
{
2
"VendorInfo": {
3
"_id": {
4
"$oid": "67dadc30e4b6e0e5979c8d84"
5
},
6
"TypeName": "/Lotus/Types/Game/VendorManifests/TheHex/InfestedLichWeaponVendorManifest",
7
"ItemManifest": [
8
{
9
"StoreItem": "/Lotus/StoreItems/Weapons/Infested/InfestedLich/LongGuns/1999InfShotgun/1999InfShotgunWeapon",
10
"ItemPrices": [
11
{
12
"ItemCount": 10,
13
"ItemType": "/Lotus/Types/Items/MiscItems/CodaWeaponBucks",
14
"ProductCategory": "MiscItems"
15
}
16
],
17
"Bin": "BIN_1",
18
"QuantityMultiplier": 1,
19
"Expiry": {
20
"$date": {
21
"$numberLong": "9999999999999"
22
}
23
},
24
"PurchaseQuantityLimit": 1,
25
"AllowMultipurchase": false,
26
"LocTagRandSeed": 65079176837546984,
27
"Id": {
28
"$oid": "67e9da12793a120dbbc1c193"
29
}
30
},
31
{
32
"StoreItem": "/Lotus/StoreItems/Weapons/Infested/InfestedLich/Melee/CodaCaustacyst/CodaCaustacyst",
33
"ItemPrices": [
34
{
35
"ItemCount": 10,
36
"ItemType": "/Lotus/Types/Items/MiscItems/CodaWeaponBucks",
37
"ProductCategory": "MiscItems"
38
}
39
],
40
"Bin": "BIN_1",
41
"QuantityMultiplier": 1,
42
"Expiry": {
43
"$date": {
44
"$numberLong": "9999999999999"
45
}
46
},
47
"PurchaseQuantityLimit": 1,
48
"AllowMultipurchase": false,
49
"LocTagRandSeed": 5687904240491804000,
50
"Id": {
51
"$oid": "67e9da12793a120dbbc1c194"
52
}
53
},
54
{
55
"StoreItem": "/Lotus/StoreItems/Weapons/Infested/InfestedLich/Melee/CodaPathocyst/CodaPathocyst",
56
"ItemPrices": [
57
{
58
"ItemCount": 10,
59
"ItemType": "/Lotus/Types/Items/MiscItems/CodaWeaponBucks",
60
"ProductCategory": "MiscItems"
61
}
62
],
63
"Bin": "BIN_1",
64
"QuantityMultiplier": 1,
65
"Expiry": {
66
"$date": {
67
"$numberLong": "9999999999999"
68
}
69
},
70
"PurchaseQuantityLimit": 1,
71
"AllowMultipurchase": false,
72
"LocTagRandSeed": 6177144662234093000,
73
"Id": {
74
"$oid": "67e9da12793a120dbbc1c195"
75
}
76
},
77
{
78
"StoreItem": "/Lotus/StoreItems/Weapons/Infested/InfestedLich/Pistols/CodaTysis",
79
"ItemPrices": [
80
{
81
"ItemCount": 10,
82
"ItemType": "/Lotus/Types/Items/MiscItems/CodaWeaponBucks",
83
"ProductCategory": "MiscItems"
84
}
85
],
86
"Bin": "BIN_1",
87
"QuantityMultiplier": 1,
88
"Expiry": {
89
"$date": {
90
"$numberLong": "9999999999999"
91
}
92
},
93
"PurchaseQuantityLimit": 1,
94
"AllowMultipurchase": false,
95
"LocTagRandSeed": 1988275604378227700,
96
"Id": {
97
"$oid": "67e9da12793a120dbbc1c196"
98
}
99
},
100
{
101
"StoreItem": "/Lotus/StoreItems/Weapons/Infested/InfestedLich/LongGuns/CodaSynapse",
102
"ItemPrices": [
103
{
104
"ItemCount": 10,
105
"ItemType": "/Lotus/Types/Items/MiscItems/CodaWeaponBucks",
106
"ProductCategory": "MiscItems"
107
}
108
],
109
"Bin": "BIN_1",
110
"QuantityMultiplier": 1,
111
"Expiry": {
112
"$date": {
113
"$numberLong": "9999999999999"
114
}
115
},
116
"PurchaseQuantityLimit": 1,
117
"AllowMultipurchase": false,
118
"LocTagRandSeed": 8607452585593957000,
119
"Id": {
120
"$oid": "67e9da12793a120dbbc1c197"
121
}
122
},
123
{
124
"StoreItem": "/Lotus/StoreItems/Weapons/Infested/InfestedLich/Melee/CodaHirudo",
125
"ItemPrices": [
126
{
127
"ItemCount": 10,
128
"ItemType": "/Lotus/Types/Items/MiscItems/CodaWeaponBucks",
129
"ProductCategory": "MiscItems"
130
}
131
],
132
"Bin": "BIN_1",
133
"QuantityMultiplier": 1,
134
"Expiry": {
135
"$date": {
136
"$numberLong": "9999999999999"
137
}
138
},
139
"PurchaseQuantityLimit": 1,
140
"AllowMultipurchase": false,
141
"LocTagRandSeed": 8385013066220909000,
142
"Id": {
143
"$oid": "67e9da12793a120dbbc1c198"
144
}
145
}
146
],
147
"PropertyTextHash": "77093DD05A8561A022DEC9A4B9BB4A56",
148
"RandomSeedType": "VRST_WEAPON",
149
"RequiredGoalTag": "",
150
"WeaponUpgradeValueAttenuationExponent": 2.25,
151
"Expiry": {
152
"$date": {
153
"$numberLong": "9999999999999"
154
}
155
}
156
}
157
}