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: 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
Sainan <63328889+Sainan@users.noreply.github.com>
提交于

代码差异

10 个文件 +247 -82
Added src/helpers/pathHelper.ts +4 -0
@@ -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;
Modified src/routes/webui.ts +1 -2
@@ -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) => {
Modified src/services/buildConfigService.ts +1 -2
@@ -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);
Modified src/services/configService.ts +2 -3
@@ -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
Modified src/services/inventoryService.ts +23 -12
@@ -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 });
Modified src/services/purchaseService.ts +11 -3
@@ -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":
Modified src/services/rngService.ts +9 -0
@@ -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 }
Modified src/services/serversideVendorsService.ts +38 -60
@@ -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 => {
Modified src/types/vendorTypes.ts +1 -0
@@ -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
Added static/fixed_responses/getVendorInfo/InfestedLichWeaponVendorManifest.json +157 -0
@@ -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 }