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: U5 mods (#3631)

Reviewed-on: https://onlyg.it/OpenWF/SpaceNinjaServer/pulls/3631 Reviewed-by: Sainan <63328889+sainan@users.noreply.github.com> Co-authored-by: AMelonInsideLemon <166175391+AMelonInsideLemon@users.noreply.github.com> Co-committed-by: AMelonInsideLemon <166175391+AMelonInsideLemon@users.noreply.github.com>

1e26a006
AMelonInsideLemon <166175391+AMelonInsideLemon@users.noreply.github.com>
提交于

代码差异

14 个文件 +190 -42
Modified src/controllers/api/inventoryController.ts +26 -29
@@ -38,6 +38,7 @@ import { Ship } from "../../models/shipModel.ts";
38 38 import {
39 39 convertIColorToLegacyColors,
40 40 convertIColorToLegacyColorsWithAtt,
41 convertToLegacyFingerprint,
41 42 fromOid,
42 43 toLegacyOid,
43 44 toOid,
@@ -621,19 +622,21 @@ export const getInventoryResponse = async (
621 622 if (version_compare(buildLabel, "2014.02.05.00.00") < 0) {
622 623 // Pre-U12 builds store mods in an array called Cards, and have no concept of RawUpgrades
623 624 inventoryResponse.Cards = [];
624 for (const rawUpgrade of inventoryResponse.RawUpgrades) {
625 const id = inventory.RawUpgrades.find(x => x.ItemType == rawUpgrade.ItemType)?._id;
626 if (id) {
627 for (let i = 0; i < rawUpgrade.ItemCount; i++) {
628 const card = {
629 ItemType: rawUpgrade.ItemType,
630 ItemId: toOid2(id, buildLabel),
631 Rank: 0,
632 AmountRemaining: rawUpgrade.ItemCount
633 } as IUpgradeClient;
634 // Client doesn't see the mods unless they are in both Cards and Upgrades
635 inventoryResponse.Cards.push(card);
636 inventoryResponse.Upgrades.push(card);
625 if (version_compare(buildLabel, gameToBuildVersion["7.3.0"]) >= 0) {
626 for (const rawUpgrade of inventoryResponse.RawUpgrades) {
627 const id = inventory.RawUpgrades.find(x => x.ItemType == rawUpgrade.ItemType)?._id;
628 if (id) {
629 for (let i = 0; i < rawUpgrade.ItemCount; i++) {
630 const card = {
631 ItemType: rawUpgrade.ItemType,
632 ItemId: toOid2(id, buildLabel),
633 Rank: 0,
634 AmountRemaining: rawUpgrade.ItemCount
635 } as IUpgradeClient;
636 // Client doesn't see the mods unless they are in both Cards and Upgrades
637 inventoryResponse.Cards.push(card);
638 inventoryResponse.Upgrades.push(card);
639 }
637 640 }
638 641 }
639 642 }
@@ -661,17 +664,15 @@ export const getInventoryResponse = async (
661 664 toLegacyOid(upgrade.ItemId);
662 665 if (version_compare(buildLabel, gameToBuildVersion["18.18.0"]) < 0) {
663 666 // Pre-U18.18 builds use a different UpgradeFingerprint format
664 let rank: number = 0;
665 if (upgrade.UpgradeFingerprint) {
666 rank = Number.parseFloat(
667 upgrade.UpgradeFingerprint.substring(
668 upgrade.UpgradeFingerprint.indexOf(":") + 1,
669 upgrade.UpgradeFingerprint.lastIndexOf("}")
670 )
671 );
672 }
673 upgrade.UpgradeFingerprint = `lvl=${rank}|`;
674 if (version_compare(buildLabel, gameToBuildVersion["13.0.0"]) < 0) {
667 const json = JSON.parse(upgrade.UpgradeFingerprint || '{"lvl":0}') as { lvl?: number };
668 const rank: number = json.lvl ?? 0;
669 upgrade.UpgradeFingerprint = convertToLegacyFingerprint(
670 upgrade.UpgradeFingerprint || '{"lvl":0}'
671 );
672 if (
673 version_compare(buildLabel, gameToBuildVersion["7.3.0"]) >= 0 &&
674 version_compare(buildLabel, gameToBuildVersion["13.0.0"]) < 0
675 ) {
675 676 // Pre-U10 builds
676 677 if (
677 678 !upgrade.AmountRemaining ||
@@ -803,11 +804,7 @@ export const getInventoryResponse = async (
803 804 inventoryResponse.Upgrades = inventoryResponse.Upgrades.filter(x =>
804 805 allowedMods.includes(x.ItemType)
805 806 );
806 if (inventoryResponse.Cards) {
807 inventoryResponse.Cards = inventoryResponse.Cards.filter(x =>
808 allowedMods.includes(x.ItemType)
809 );
810 }
807 if (inventoryResponse.Cards) inventoryResponse.Cards = [];
811 808 }
812 809 }
813 810 }
Modified src/controllers/api/sellController.ts +1 -1
@@ -312,7 +312,7 @@ export const sellController: RequestHandler = async (req, res) => {
312 312 break;
313 313 case "Upgrades":
314 314 payload.Items.Upgrades.forEach(sellItem => {
315 if (sellItem.Count == 0) {
315 if (sellItem.Count == 0 || !sellItem.String.includes("/")) {
316 316 inventory.Upgrades.pull({ _id: sellItem.String });
317 317 } else {
318 318 addMods(inventory, [
Modified src/controllers/api/upgradesController.ts +3 -0
@@ -156,6 +156,9 @@ export const upgradesController: RequestHandler = async (req, res) => {
156 156 const item = inventory[payload.Category].id(itemId)!;
157 157 item.UpgradeNodes = payload.Weapon.UpgradeNodes;
158 158 }
159 if (payload.Cost) {
160 updateCurrency(inventory, payload.Cost, false);
161 }
159 162 }
160 163 } else {
161 164 const payload = JSON.parse(String(req.body)) as IUpgradesRequest;
Modified src/helpers/inventoryHelpers.ts +81 -0
@@ -105,6 +105,87 @@ export const convertIColorToLegacyColorsWithAtt = (
105 105 return convertedColors;
106 106 };
107 107
108 // ChatGPT wrote that and seems it looks fine
109 export const convertFromLegacyFingerprint = (s: string): string => {
110 let index = 0;
111
112 const parseBlock = (isArrayItem = false): Record<string, unknown> | Record<string, unknown>[] => {
113 const obj: Record<string, unknown> = {};
114
115 while (index < s.length) {
116 if (s[index] === "|") {
117 index++;
118 continue;
119 }
120 if (s[index] === "}") {
121 index++;
122 break;
123 }
124
125 const eqPos = s.indexOf("=", index);
126 if (eqPos === -1) break;
127 const key = s.slice(index, eqPos);
128 index = eqPos + 1;
129
130 if (s[index] === "{" && s[index + 1] === "|") {
131 index += 2;
132 const items: Record<string, unknown>[] = [];
133 while (index < s.length && !(s[index] === "|" && s[index + 1] === "}")) {
134 if (s[index] === "{" && s[index + 1] === "|") index += 2;
135 const item = parseBlock(true) as Record<string, unknown>;
136 items.push(item);
137 }
138 index += 2;
139 obj[key] = items;
140 continue;
141 }
142
143 if (s[index] === "{") {
144 index++;
145 obj[key] = parseBlock() as Record<string, unknown>;
146 continue;
147 }
148
149 const nextSep = s.indexOf("|", index);
150 const value = nextSep === -1 ? s.slice(index) : s.slice(index, nextSep);
151 index = nextSep === -1 ? s.length : nextSep;
152 obj[key] = value;
153 }
154
155 return isArrayItem ? obj : obj;
156 };
157
158 const result = parseBlock() as Record<string, unknown>;
159 return JSON.stringify(result);
160 };
161
162 export const convertToLegacyFingerprint = (s: string): string => {
163 // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
164 const obj: Record<string, unknown> = JSON.parse(s);
165 const serialize = (o: unknown): string => {
166 if (Array.isArray(o)) {
167 return o.map(item => `{|${serialize(item)}|}`).join("|");
168 } else if (typeof o === "object" && o !== null) {
169 const parts: string[] = [];
170 for (const k in o as Record<string, unknown>) {
171 const v = (o as Record<string, unknown>)[k];
172 if (Array.isArray(v)) {
173 parts.push(`${k}={|${serialize(v)}|}`);
174 } else if (typeof v === "object" && v !== null) {
175 parts.push(`${k}={|${serialize(v)}|}`);
176 } else {
177 // eslint-disable-next-line @typescript-eslint/restrict-template-expressions
178 parts.push(`${k}=${v}`);
179 }
180 }
181 return parts.join("|");
182 } else {
183 return String(o);
184 }
185 };
186 return serialize(obj) + "|";
187 };
188
108 189 export const convertLegacyColorsToIColor = (colors: number[] | undefined): IColor => {
109 190 if (colors) {
110 191 return { t0: colors[0], t1: colors[1], t2: colors[2], t3: colors[3], en: colors[4] };
Modified src/services/missionInventoryUpdateService.ts +27 -11
@@ -95,7 +95,13 @@ import {
95 95 import { config } from "./configService.ts";
96 96 import libraryDailyTasks from "../../static/fixed_responses/libraryDailyTasks.json" with { type: "json" };
97 97 import type { IGoal, ISyndicateJob, ISyndicateMissionInfo } from "../types/worldStateTypes.ts";
98 import { fromOid, toObjectId, toOid2, version_compare } from "../helpers/inventoryHelpers.ts";
98 import {
99 convertFromLegacyFingerprint,
100 fromOid,
101 toObjectId,
102 toOid2,
103 version_compare
104 } from "../helpers/inventoryHelpers.ts";
99 105 import type { TAccountDocument } from "./loginService.ts";
100 106 import type { ITypeCount } from "../types/commonTypes.ts";
101 107 import type { IEquipmentClient } from "../types/equipmentTypes.ts";
@@ -531,10 +537,9 @@ export const addMissionInventoryUpdates = async (
531 537 clientUpgrade.ItemCount ??= 1;
532 538 if (account.BuildLabel && version_compare(account.BuildLabel, gameToBuildVersion["18.18.0"]) < 0) {
533 539 // Acquired Mods have a different UpgradeFingerprint format in pre-U18.18.0 builds, this converts them to the format the database expects
534 clientUpgrade.UpgradeFingerprint = `{"lvl":${clientUpgrade.UpgradeFingerprint.substring(
535 clientUpgrade.UpgradeFingerprint.indexOf("=") + 1,
536 clientUpgrade.UpgradeFingerprint.lastIndexOf("|")
537 )}}`;
540 clientUpgrade.UpgradeFingerprint = convertFromLegacyFingerprint(
541 clientUpgrade.UpgradeFingerprint
542 );
538 543 }
539 544 // Handle Fusion Core drops
540 545 const parsedFingerprint = JSON.parse(clientUpgrade.UpgradeFingerprint) as { lvl?: number };
@@ -544,13 +549,24 @@ export const addMissionInventoryUpdates = async (
544 549 UpgradeFingerprint: clientUpgrade.UpgradeFingerprint
545 550 });
546 551 } else if (id == "") {
547 // U19 does not provide RawUpgrades and instead interleaves them with riven progress here
548 addMods(inventory, [
549 {
552 if (
553 account.BuildLabel &&
554 version_compare(account.BuildLabel, gameToBuildVersion["7.3.0"]) >= 0
555 ) {
556 // U19 does not provide RawUpgrades and instead interleaves them with riven progress here
557 addMods(inventory, [
558 {
559 ItemType: clientUpgrade.ItemType,
560 ItemCount: clientUpgrade.ItemCount
561 }
562 ]);
563 } else {
564 // U5 Mods
565 inventory.Upgrades.push({
550 566 ItemType: clientUpgrade.ItemType,
551 ItemCount: clientUpgrade.ItemCount
552 }
553 ]);
567 UpgradeFingerprint: clientUpgrade.UpgradeFingerprint
568 });
569 }
554 570 } else {
555 571 const upgrade = inventory.Upgrades.id(id)!;
556 572 upgrade.UpgradeFingerprint = clientUpgrade.UpgradeFingerprint; // primitive way to copy over the riven challenge progress
Modified src/types/requestTypes.ts +1 -0
@@ -266,6 +266,7 @@ export interface IUpgradesRequestLegacy {
266 266 PolarityRemap: IPolarity[];
267 267 UpgradesToAttach?: IUpgradeClient[];
268 268 UpgradesToDetach?: IUpgradeClient[];
269 Cost?: number;
269 270 }
270 271 export interface IUpgradeOperation {
271 272 OperationType: string;
Modified static/webui/script.js +16 -1
@@ -793,6 +793,21 @@ function fetchItemList() {
793 793 },
794 794 "/Lotus/Language/Game/Rank_Utility": {
795 795 name: loc("guildView_rank_utility")
796 },
797 "/Lotus/Upgrades/Modules/GrineerMeleeModule": {
798 name: loc("code_meleeMod")
799 },
800 "/Lotus/Upgrades/Modules/GrineerPistolModule": {
801 name: loc("code_pistolMod")
802 },
803 "/Lotus/Upgrades/Modules/GrineerRifleModule": {
804 name: loc("code_rifleMod")
805 },
806 "/Lotus/Upgrades/Modules/GrineerShotgunModule": {
807 name: loc("code_shotgunMod")
808 },
809 "/Lotus/Upgrades/Modules/OrokinWarframeModule": {
810 name: loc("code_warframeMod")
796 811 }
797 812 };
798 813 for (const [type, items] of Object.entries(data)) {
@@ -1670,7 +1685,7 @@ function translateInventoryDataToDom() {
1670 1685 td.textContent = itemMap[item.ItemType]?.name ?? item.ItemType;
1671 1686 if (itemMap[item.ItemType]?.badReason == "notraw") {
1672 1687 // Assuming this is a riven with a pending challenge, so rank would be N/A, but otherwise it's fine.
1673 } else {
1688 } else if (!Number.isNaN(rank)) {
1674 1689 td.innerHTML += " <span title='" + loc("code_rank") + "'>★ " + rank + "/" + maxRank + "</span>";
1675 1690 }
1676 1691 tr.appendChild(td);
Modified static/webui/translations/de.js +5 -0
@@ -30,6 +30,11 @@ dict = {
30 30 code_ancientFusionCoreCommon: `Antiker Fusionskern (Gewöhnlich)`,
31 31 code_ancientFusionCoreUncommon: `Antiker Fusionskern (Ungewöhnlich)`,
32 32 code_ancientFusionCoreRare: `Antiker Fusionskern (Selten)`,
33 code_meleeMod: `[UNTRANSLATED] Melee Mod`,
34 code_pistolMod: `[UNTRANSLATED] Pistol Mod`,
35 code_rifleMod: `[UNTRANSLATED] Rifle Mod`,
36 code_shotgunMod: `[UNTRANSLATED] Shotgun Mod`,
37 code_warframeMod: `[UNTRANSLATED] Warframe Mod`,
33 38 code_legendaryCore: `Legendärer Kern`,
34 39 code_traumaticPeculiar: `Kuriose Mod: Traumatisch`,
35 40 code_starter: `|MOD| (Defekt)`,
Modified static/webui/translations/en.js +5 -0
@@ -29,6 +29,11 @@ dict = {
29 29 code_ancientFusionCoreCommon: `Ancient Fusion Core (Common)`,
30 30 code_ancientFusionCoreUncommon: `Ancient Fusion Core (Uncommon)`,
31 31 code_ancientFusionCoreRare: `Ancient Fusion Core (Rare)`,
32 code_meleeMod: `Melee Mod`,
33 code_pistolMod: `Pistol Mod`,
34 code_rifleMod: `Rifle Mod`,
35 code_shotgunMod: `Shotgun Mod`,
36 code_warframeMod: `Warframe Mod`,
32 37 code_legendaryCore: `Legendary Core`,
33 38 code_traumaticPeculiar: `Traumatic Peculiar`,
34 39 code_starter: `|MOD| (Flawed)`,
Modified static/webui/translations/es.js +5 -0
@@ -30,6 +30,11 @@ dict = {
30 30 code_ancientFusionCoreCommon: `[UNTRANSLATED] Ancient Fusion Core (Common)`,
31 31 code_ancientFusionCoreUncommon: `[UNTRANSLATED] Ancient Fusion Core (Uncommon)`,
32 32 code_ancientFusionCoreRare: `[UNTRANSLATED] Ancient Fusion Core (Rare)`,
33 code_meleeMod: `[UNTRANSLATED] Melee Mod`,
34 code_pistolMod: `[UNTRANSLATED] Pistol Mod`,
35 code_rifleMod: `[UNTRANSLATED] Rifle Mod`,
36 code_shotgunMod: `[UNTRANSLATED] Shotgun Mod`,
37 code_warframeMod: `[UNTRANSLATED] Warframe Mod`,
33 38 code_legendaryCore: `Núcleo legendario`,
34 39 code_traumaticPeculiar: `Traumatismo peculiar`,
35 40 code_starter: `|MOD| (Defectuoso)`,
Modified static/webui/translations/fr.js +5 -0
@@ -30,6 +30,11 @@ dict = {
30 30 code_ancientFusionCoreCommon: `[UNTRANSLATED] Ancient Fusion Core (Common)`,
31 31 code_ancientFusionCoreUncommon: `[UNTRANSLATED] Ancient Fusion Core (Uncommon)`,
32 32 code_ancientFusionCoreRare: `[UNTRANSLATED] Ancient Fusion Core (Rare)`,
33 code_meleeMod: `[UNTRANSLATED] Melee Mod`,
34 code_pistolMod: `[UNTRANSLATED] Pistol Mod`,
35 code_rifleMod: `[UNTRANSLATED] Rifle Mod`,
36 code_shotgunMod: `[UNTRANSLATED] Shotgun Mod`,
37 code_warframeMod: `[UNTRANSLATED] Warframe Mod`,
33 38 code_legendaryCore: `Coeur Légendaire`,
34 39 code_traumaticPeculiar: `Traumatisme Atypique`,
35 40 code_starter: `|MOD| (Défectueux)`,
Modified static/webui/translations/ru.js +5 -0
@@ -30,6 +30,11 @@ dict = {
30 30 code_ancientFusionCoreCommon: `[UNTRANSLATED] Ancient Fusion Core (Common)`,
31 31 code_ancientFusionCoreUncommon: `[UNTRANSLATED] Ancient Fusion Core (Uncommon)`,
32 32 code_ancientFusionCoreRare: `[UNTRANSLATED] Ancient Fusion Core (Rare)`,
33 code_meleeMod: `[UNTRANSLATED] Melee Mod`,
34 code_pistolMod: `[UNTRANSLATED] Pistol Mod`,
35 code_rifleMod: `[UNTRANSLATED] Rifle Mod`,
36 code_shotgunMod: `[UNTRANSLATED] Shotgun Mod`,
37 code_warframeMod: `[UNTRANSLATED] Warframe Mod`,
33 38 code_legendaryCore: `Легендарное ядро`,
34 39 code_traumaticPeculiar: `Травмирующая Странность`,
35 40 code_starter: `|MOD| (Повреждённый)`,
Modified static/webui/translations/uk.js +5 -0
Modified static/webui/translations/zh.js +5 -0