Compare commits

..

10 Commits

Author SHA1 Message Date
9ff1202a8f deduplicate static data by weighting it
Some checks failed
Build / build (pull_request) Failing after 55s
2025-06-27 01:26:21 +02:00
bb9e0211cc Add noDarvoDealPurchaseLimit cheat 2025-06-27 01:26:21 +02:00
a14337be03 ensure stock is an int after multiplier 2025-06-27 01:26:21 +02:00
b0226d21cb feat: daily deal 2025-06-27 01:26:20 +02:00
4f1f9592b0 chore: use chokidar for configWatcherService (#2315)
All checks were successful
Build / build (push) Successful in 58s
Build Docker image / docker-amd64 (push) Successful in 1m13s
Build Docker image / docker-arm64 (push) Successful in 57s
It's just a lot snappier + works flawlessly under Bun.

Reviewed-on: #2315
Co-authored-by: Sainan <63328889+Sainan@users.noreply.github.com>
Co-committed-by: Sainan <63328889+Sainan@users.noreply.github.com>
2025-06-26 15:30:01 -07:00
764cdd1ab8 feat: worldState.allTheFissures (#2313)
All checks were successful
Build / build (push) Successful in 51s
Build Docker image / docker-arm64 (push) Successful in 1m3s
Build Docker image / docker-amd64 (push) Successful in 1m16s
Closes #2294

Reviewed-on: #2313
Co-authored-by: Sainan <63328889+Sainan@users.noreply.github.com>
Co-committed-by: Sainan <63328889+Sainan@users.noreply.github.com>
2025-06-26 14:32:26 -07:00
0ba641a2ac chore: update PE+ (#2311)
Some checks failed
Build Docker image / docker-amd64 (push) Waiting to run
Build Docker image / docker-arm64 (push) Waiting to run
Build / build (push) Has been cancelled
Closes #2309

Reviewed-on: #2311
Co-authored-by: Sainan <63328889+Sainan@users.noreply.github.com>
Co-committed-by: Sainan <63328889+Sainan@users.noreply.github.com>
2025-06-26 14:31:52 -07:00
eb7b51852b fix: use exact quantity when adding gear items by StoreItem (#2310)
Some checks failed
Build / build (push) Has been cancelled
Build Docker image / docker-arm64 (push) Has been cancelled
Build Docker image / docker-amd64 (push) Has been cancelled
Closes #2304

Reviewed-on: #2310
Co-authored-by: Sainan <63328889+Sainan@users.noreply.github.com>
Co-committed-by: Sainan <63328889+Sainan@users.noreply.github.com>
2025-06-26 14:31:40 -07:00
a3be376489 chore(webui): add Thalys to Incarnon List (#2299)
All checks were successful
Build Docker image / docker-arm64 (push) Successful in 58s
Build / build (push) Successful in 49s
Build Docker image / docker-amd64 (push) Successful in 1m12s
Closes #2298

Reviewed-on: #2299
Co-authored-by: AMelonInsideLemon <166175391+AMelonInsideLemon@users.noreply.github.com>
Co-committed-by: AMelonInsideLemon <166175391+AMelonInsideLemon@users.noreply.github.com>
2025-06-26 11:02:40 -07:00
d94cd38120 chore(webui): update Chinese translation (#2291)
All checks were successful
Build Docker image / docker-arm64 (push) Successful in 58s
Build / build (push) Successful in 52s
Build Docker image / docker-amd64 (push) Successful in 1m20s
Reviewed-on: #2291
Co-authored-by: Corvus <corvus@noreply.localhost>
Co-committed-by: Corvus <corvus@noreply.localhost>
2025-06-26 06:44:03 -07:00
12 changed files with 74 additions and 43 deletions

View File

@ -34,4 +34,5 @@ SpaceNinjaServer requires a `config.json`. To set it up, you can copy the [confi
- `RadioLegion2Syndicate` for The Emissary - `RadioLegion2Syndicate` for The Emissary
- `RadioLegionIntermissionSyndicate` for Intermission I - `RadioLegionIntermissionSyndicate` for Intermission I
- `RadioLegionSyndicate` for The Wolf of Saturn Six - `RadioLegionSyndicate` for The Wolf of Saturn Six
- `allTheFissures` can be set to `normal` or `hard` to enable all fissures either in normal or steel path, respectively.
- `worldState.circuitGameModes` can be provided with an array of valid game modes (`Survival`, `VoidFlood`, `Excavation`, `Defense`, `Exterminate`, `Assassination`, `Alchemy`) - `worldState.circuitGameModes` can be provided with an array of valid game modes (`Survival`, `VoidFlood`, `Excavation`, `Defense`, `Exterminate`, `Assassination`, `Alchemy`)

View File

@ -75,6 +75,7 @@
"vallisOverride": "", "vallisOverride": "",
"duviriOverride": "", "duviriOverride": "",
"nightwaveOverride": "", "nightwaveOverride": "",
"allTheFissures": "",
"circuitGameModes": null, "circuitGameModes": null,
"darvoStockMultiplier": 1 "darvoStockMultiplier": 1
}, },

12
package-lock.json generated
View File

@ -14,6 +14,7 @@
"@types/websocket": "^1.0.10", "@types/websocket": "^1.0.10",
"@types/ws": "^8.18.1", "@types/ws": "^8.18.1",
"@typescript/native-preview": "^7.0.0-dev.20250625.1", "@typescript/native-preview": "^7.0.0-dev.20250625.1",
"chokidar": "^4.0.3",
"crc-32": "^1.2.2", "crc-32": "^1.2.2",
"express": "^5", "express": "^5",
"json-with-bigint": "^3.4.4", "json-with-bigint": "^3.4.4",
@ -22,7 +23,7 @@
"ncp": "^2.0.0", "ncp": "^2.0.0",
"typescript": "^5.5", "typescript": "^5.5",
"undici": "^7.10.0", "undici": "^7.10.0",
"warframe-public-export-plus": "^0.5.71", "warframe-public-export-plus": "^0.5.72",
"warframe-riven-info": "^0.1.2", "warframe-riven-info": "^0.1.2",
"winston": "^3.17.0", "winston": "^3.17.0",
"winston-daily-rotate-file": "^5.0.0", "winston-daily-rotate-file": "^5.0.0",
@ -31,7 +32,6 @@
"devDependencies": { "devDependencies": {
"@typescript-eslint/eslint-plugin": "^8.28.0", "@typescript-eslint/eslint-plugin": "^8.28.0",
"@typescript-eslint/parser": "^8.28.0", "@typescript-eslint/parser": "^8.28.0",
"chokidar": "^4.0.3",
"eslint": "^8", "eslint": "^8",
"eslint-plugin-prettier": "^5.2.5", "eslint-plugin-prettier": "^5.2.5",
"prettier": "^3.5.3", "prettier": "^3.5.3",
@ -997,7 +997,6 @@
"version": "4.0.3", "version": "4.0.3",
"resolved": "https://registry.npmjs.org/chokidar/-/chokidar-4.0.3.tgz", "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-4.0.3.tgz",
"integrity": "sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==", "integrity": "sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==",
"dev": true,
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"readdirp": "^4.0.1" "readdirp": "^4.0.1"
@ -2803,7 +2802,6 @@
"version": "4.1.2", "version": "4.1.2",
"resolved": "https://registry.npmjs.org/readdirp/-/readdirp-4.1.2.tgz", "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-4.1.2.tgz",
"integrity": "sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg==", "integrity": "sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg==",
"dev": true,
"license": "MIT", "license": "MIT",
"engines": { "engines": {
"node": ">= 14.18.0" "node": ">= 14.18.0"
@ -3388,9 +3386,9 @@
} }
}, },
"node_modules/warframe-public-export-plus": { "node_modules/warframe-public-export-plus": {
"version": "0.5.71", "version": "0.5.72",
"resolved": "https://registry.npmjs.org/warframe-public-export-plus/-/warframe-public-export-plus-0.5.71.tgz", "resolved": "https://registry.npmjs.org/warframe-public-export-plus/-/warframe-public-export-plus-0.5.72.tgz",
"integrity": "sha512-TCS2wPRsBzuURJlIMDhygAHaLsKVZ7dGuC73WZ/iMyn3gKVwA98nnaIj24D+UceWS08fwq4ilWAfUzHJd6X29A==" "integrity": "sha512-oOZgtU6L0MGcPRKfA6+bonu+Db1kie1lVdLmA7/DbheTPweNkBEx3Hx3Seib+hEaFW+nLj3T5GtmGxGcFHCHfg=="
}, },
"node_modules/warframe-riven-info": { "node_modules/warframe-riven-info": {
"version": "0.1.2", "version": "0.1.2",

View File

@ -28,6 +28,7 @@
"@types/websocket": "^1.0.10", "@types/websocket": "^1.0.10",
"@types/ws": "^8.18.1", "@types/ws": "^8.18.1",
"@typescript/native-preview": "^7.0.0-dev.20250625.1", "@typescript/native-preview": "^7.0.0-dev.20250625.1",
"chokidar": "^4.0.3",
"crc-32": "^1.2.2", "crc-32": "^1.2.2",
"express": "^5", "express": "^5",
"json-with-bigint": "^3.4.4", "json-with-bigint": "^3.4.4",
@ -36,7 +37,7 @@
"ncp": "^2.0.0", "ncp": "^2.0.0",
"typescript": "^5.5", "typescript": "^5.5",
"undici": "^7.10.0", "undici": "^7.10.0",
"warframe-public-export-plus": "^0.5.71", "warframe-public-export-plus": "^0.5.72",
"warframe-riven-info": "^0.1.2", "warframe-riven-info": "^0.1.2",
"winston": "^3.17.0", "winston": "^3.17.0",
"winston-daily-rotate-file": "^5.0.0", "winston-daily-rotate-file": "^5.0.0",
@ -45,7 +46,6 @@
"devDependencies": { "devDependencies": {
"@typescript-eslint/eslint-plugin": "^8.28.0", "@typescript-eslint/eslint-plugin": "^8.28.0",
"@typescript-eslint/parser": "^8.28.0", "@typescript-eslint/parser": "^8.28.0",
"chokidar": "^4.0.3",
"eslint": "^8", "eslint": "^8",
"eslint-plugin-prettier": "^5.2.5", "eslint-plugin-prettier": "^5.2.5",
"prettier": "^3.5.3", "prettier": "^3.5.3",

View File

@ -82,6 +82,7 @@ export interface IConfig {
vallisOverride?: string; vallisOverride?: string;
duviriOverride?: string; duviriOverride?: string;
nightwaveOverride?: string; nightwaveOverride?: string;
allTheFissures?: string;
circuitGameModes?: string[]; circuitGameModes?: string[];
darvoStockMultiplier?: number; darvoStockMultiplier?: number;
}; };

View File

@ -1,4 +1,4 @@
import fs from "fs"; import chokidar from "chokidar";
import fsPromises from "fs/promises"; import fsPromises from "fs/promises";
import { logger } from "../utils/logger"; import { logger } from "../utils/logger";
import { config, configPath, loadConfig } from "./configService"; import { config, configPath, loadConfig } from "./configService";
@ -6,12 +6,7 @@ import { getWebPorts, sendWsBroadcast, startWebServer, stopWebServer } from "./w
import { Inbox } from "../models/inboxModel"; import { Inbox } from "../models/inboxModel";
let amnesia = false; let amnesia = false;
fs.watchFile(configPath, (now, then) => { chokidar.watch(configPath).on("change", () => {
// https://github.com/oven-sh/bun/issues/20542
if (process.versions.bun && now.mtimeMs == then.mtimeMs) {
return;
}
if (amnesia) { if (amnesia) {
amnesia = false; amnesia = false;
} else { } else {

View File

@ -499,6 +499,7 @@ export const addItem = async (
// - Blueprints for Ancient Protector Specter, Shield Osprey Specter, etc. have num=1 despite giving their purchaseQuantity. // - Blueprints for Ancient Protector Specter, Shield Osprey Specter, etc. have num=1 despite giving their purchaseQuantity.
if (!exactQuantity) { if (!exactQuantity) {
quantity *= ExportGear[typeName].purchaseQuantity ?? 1; quantity *= ExportGear[typeName].purchaseQuantity ?? 1;
logger.debug(`non-exact acquisition of ${typeName}; factored quantity is ${quantity}`);
} }
const consumablesChanges = [ const consumablesChanges = [
{ {

View File

@ -398,18 +398,28 @@ export const handleStoreItemAcquisition = async (
} else { } else {
const storeCategory = getStoreItemCategory(storeItemName); const storeCategory = getStoreItemCategory(storeItemName);
const internalName = fromStoreItem(storeItemName); const internalName = fromStoreItem(storeItemName);
logger.debug(`store category ${storeCategory}`);
if (!ignorePurchaseQuantity) { if (!ignorePurchaseQuantity) {
if (internalName in ExportGear) { if (internalName in ExportGear) {
quantity *= ExportGear[internalName].purchaseQuantity || 1; quantity *= ExportGear[internalName].purchaseQuantity || 1;
logger.debug(`factored quantity is ${quantity}`);
} else if (internalName in ExportResources) { } else if (internalName in ExportResources) {
quantity *= ExportResources[internalName].purchaseQuantity || 1; quantity *= ExportResources[internalName].purchaseQuantity || 1;
logger.debug(`factored quantity is ${quantity}`);
} }
} }
logger.debug(`store category ${storeCategory}`);
switch (storeCategory) { switch (storeCategory) {
default: { default: {
purchaseResponse = { purchaseResponse = {
InventoryChanges: await addItem(inventory, internalName, quantity, premiumPurchase, seed) InventoryChanges: await addItem(
inventory,
internalName,
quantity,
premiumPurchase,
seed,
undefined,
true
)
}; };
break; break;
} }
@ -551,7 +561,9 @@ const handleTypesPurchase = async (
logger.debug(`type category ${typeCategory}`); logger.debug(`type category ${typeCategory}`);
switch (typeCategory) { switch (typeCategory) {
default: default:
return { InventoryChanges: await addItem(inventory, typesName, quantity, premiumPurchase, seed) }; return {
InventoryChanges: await addItem(inventory, typesName, quantity, premiumPurchase, seed, undefined, true)
};
case "BoosterPacks": case "BoosterPacks":
return handleBoosterPackPurchase(typesName, inventory, quantity); return handleBoosterPackPurchase(typesName, inventory, quantity);
case "SlotItems": case "SlotItems":

View File

@ -1526,6 +1526,25 @@ export const getWorldState = (buildLabel?: string): IWorldState => {
}; };
export const populateFissures = async (worldState: IWorldState): Promise<void> => { export const populateFissures = async (worldState: IWorldState): Promise<void> => {
if (config.worldState?.allTheFissures) {
let i = 0;
for (const [tier, nodes] of Object.entries(fissureMissions)) {
for (const node of nodes) {
const meta = ExportRegions[node];
worldState.ActiveMissions.push({
_id: { $oid: (i++).toString().padStart(8, "0") + "8e0c70ba050f1eb7" },
Region: meta.systemIndex + 1,
Seed: 1337,
Activation: { $date: { $numberLong: "1000000000000" } },
Expiry: { $date: { $numberLong: "2000000000000" } },
Node: node,
MissionType: eMissionType[meta.missionIndex].tag,
Modifier: tier,
Hard: config.worldState.allTheFissures == "hard"
});
}
}
} else {
const fissures = await Fissure.find({}); const fissures = await Fissure.find({});
for (const fissure of fissures) { for (const fissure of fissures) {
const meta = ExportRegions[fissure.Node]; const meta = ExportRegions[fissure.Node];
@ -1541,6 +1560,7 @@ export const populateFissures = async (worldState: IWorldState): Promise<void> =
Hard: fissure.Hard Hard: fissure.Hard
}); });
} }
}
}; };
export const populateDailyDeal = async (worldState: IWorldState): Promise<void> => { export const populateDailyDeal = async (worldState: IWorldState): Promise<void> => {

View File

@ -45,5 +45,6 @@
"/Lotus/Weapons/Tenno/Zariman/Melee/Tonfas/ZarimanTonfaWeapon", "/Lotus/Weapons/Tenno/Zariman/Melee/Tonfas/ZarimanTonfaWeapon",
"/Lotus/Weapons/Tenno/Zariman/Pistols/HeavyPistol/ZarimanHeavyPistol", "/Lotus/Weapons/Tenno/Zariman/Pistols/HeavyPistol/ZarimanHeavyPistol",
"/Lotus/Weapons/Thanotech/EntFistIncarnon/EntFistIncarnon", "/Lotus/Weapons/Thanotech/EntFistIncarnon/EntFistIncarnon",
"/Lotus/Weapons/Thanotech/EntratiWristGun/EntratiWristGunWeapon" "/Lotus/Weapons/Thanotech/EntratiWristGun/EntratiWristGunWeapon",
"/Lotus/Weapons/Tenno/Zariman/Melee/HeavyScythe/ZarimanHeavyScythe/ZarimanHeavyScytheWeapon"
] ]

View File

@ -248,7 +248,8 @@ const permanentEvolutionWeapons = new Set([
"/Lotus/Weapons/Tenno/Zariman/Melee/Tonfas/ZarimanTonfaWeapon", "/Lotus/Weapons/Tenno/Zariman/Melee/Tonfas/ZarimanTonfaWeapon",
"/Lotus/Weapons/Tenno/Zariman/Pistols/HeavyPistol/ZarimanHeavyPistol", "/Lotus/Weapons/Tenno/Zariman/Pistols/HeavyPistol/ZarimanHeavyPistol",
"/Lotus/Weapons/Thanotech/EntFistIncarnon/EntFistIncarnon", "/Lotus/Weapons/Thanotech/EntFistIncarnon/EntFistIncarnon",
"/Lotus/Weapons/Thanotech/EntratiWristGun/EntratiWristGunWeapon" "/Lotus/Weapons/Thanotech/EntratiWristGun/EntratiWristGunWeapon",
"/Lotus/Weapons/Tenno/Zariman/Melee/HeavyScythe/ZarimanHeavyScythe/ZarimanHeavyScytheWeapon"
]); ]);
let uniqueLevelCaps = {}; let uniqueLevelCaps = {};

View File

@ -74,9 +74,9 @@ dict = {
inventory_longGuns: `主要武器`, inventory_longGuns: `主要武器`,
inventory_pistols: `次要武器`, inventory_pistols: `次要武器`,
inventory_melee: `近战武器`, inventory_melee: `近战武器`,
inventory_spaceSuits: `Archwings`, inventory_spaceSuits: `载具`,
inventory_spaceGuns: `Archwing主武器`, inventory_spaceGuns: `载具主武器`,
inventory_spaceMelee: `Archwing近战武器`, inventory_spaceMelee: `载具近战武器`,
inventory_mechSuits: `殁世机甲`, inventory_mechSuits: `殁世机甲`,
inventory_sentinels: `守护`, inventory_sentinels: `守护`,
inventory_sentinelWeapons: `守护武器`, inventory_sentinelWeapons: `守护武器`,
@ -88,15 +88,15 @@ dict = {
inventory_Boosters: `加成器`, inventory_Boosters: `加成器`,
inventory_bulkAddSuits: `添加缺失战甲`, inventory_bulkAddSuits: `添加缺失战甲`,
inventory_bulkAddWeapons: `添加缺失武器`, inventory_bulkAddWeapons: `添加缺失武器`,
inventory_bulkAddSpaceSuits: `添加缺失Archwing`, inventory_bulkAddSpaceSuits: `添加缺失载具`,
inventory_bulkAddSpaceWeapons: `添加缺失Archwing武器`, inventory_bulkAddSpaceWeapons: `添加缺失载具武器`,
inventory_bulkAddSentinels: `添加缺失守护`, inventory_bulkAddSentinels: `添加缺失守护`,
inventory_bulkAddSentinelWeapons: `添加缺失守护武器`, inventory_bulkAddSentinelWeapons: `添加缺失守护武器`,
inventory_bulkAddEvolutionProgress: `添加缺失的灵化之源`, inventory_bulkAddEvolutionProgress: `添加缺失的灵化之源`,
inventory_bulkRankUpSuits: `所有战甲升满级`, inventory_bulkRankUpSuits: `所有战甲升满级`,
inventory_bulkRankUpWeapons: `所有武器升满级`, inventory_bulkRankUpWeapons: `所有武器升满级`,
inventory_bulkRankUpSpaceSuits: `所有Archwing升满级`, inventory_bulkRankUpSpaceSuits: `所有载具升满级`,
inventory_bulkRankUpSpaceWeapons: `所有Archwing武器升满级`, inventory_bulkRankUpSpaceWeapons: `所有载具武器升满级`,
inventory_bulkRankUpSentinels: `所有守护升满级`, inventory_bulkRankUpSentinels: `所有守护升满级`,
inventory_bulkRankUpSentinelWeapons: `所有守护武器升满级`, inventory_bulkRankUpSentinelWeapons: `所有守护武器升满级`,
inventory_bulkRankUpEvolutionProgress: `所有灵化之源最大等级`, inventory_bulkRankUpEvolutionProgress: `所有灵化之源最大等级`,
@ -182,7 +182,7 @@ dict = {
cheats_account: `账户`, cheats_account: `账户`,
cheats_unlockAllFocusSchools: `解锁所有专精学派`, cheats_unlockAllFocusSchools: `解锁所有专精学派`,
cheats_helminthUnlockAll: `完全升级Helminth`, cheats_helminthUnlockAll: `完全升级Helminth`,
cheats_addMissingSubsumedAbilities: `[UNTRANSLATED] Add Missing Subsumed Abilities`, cheats_addMissingSubsumedAbilities: `添加Helminth未汲取的战甲技能`,
cheats_intrinsicsUnlockAll: `所有内源之力最大等级`, cheats_intrinsicsUnlockAll: `所有内源之力最大等级`,
cheats_changeSupportedSyndicate: `支持的集团`, cheats_changeSupportedSyndicate: `支持的集团`,
cheats_changeButton: `更改`, cheats_changeButton: `更改`,