From 5a083f5b13bd16d1bdb28ff2ef428c72f6aaa2e9 Mon Sep 17 00:00:00 2001 From: Vampire Kitten <95658710+VampireKitten@users.noreply.github.com> Date: Tue, 11 Jun 2024 12:56:51 +0200 Subject: [PATCH 1/7] Fixed Typo in config.json (#291) --- config.json.example | 2 +- src/controllers/api/getShipController.ts | 2 +- src/services/configService.ts | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/config.json.example b/config.json.example index c3a7eddd..e874d450 100644 --- a/config.json.example +++ b/config.json.example @@ -16,7 +16,7 @@ "unlockAllQuests": true, "completeAllQuests": false, "infiniteResources": true, - "unlockallShipFeatures": true, + "unlockAllShipFeatures": true, "unlockAllShipDecorations": true, "unlockAllFlavourItems": true, "unlockAllSkins": true, diff --git a/src/controllers/api/getShipController.ts b/src/controllers/api/getShipController.ts index f0ba4468..65483101 100644 --- a/src/controllers/api/getShipController.ts +++ b/src/controllers/api/getShipController.ts @@ -31,7 +31,7 @@ export const getShipController: RequestHandler = async (req, res) => { Apartment: personalRooms.Apartment }; - if (config.unlockallShipFeatures) { + if (config.unlockAllShipFeatures) { getShipResponse.Ship.Features = allShipFeatures; } diff --git a/src/services/configService.ts b/src/services/configService.ts index b70e24df..e5199a3c 100644 --- a/src/services/configService.ts +++ b/src/services/configService.ts @@ -14,7 +14,7 @@ interface IConfig { unlockAllQuests?: boolean; completeAllQuests?: boolean; infiniteResources?: boolean; - unlockallShipFeatures?: boolean; + unlockAllShipFeatures?: boolean; unlockAllShipDecorations?: boolean; unlockAllFlavourItems?: boolean; unlockAllSkins?: boolean; From b1f92c854e5eedea940f101cfc70468af45534ed Mon Sep 17 00:00:00 2001 From: Sainan Date: Tue, 11 Jun 2024 13:18:59 +0200 Subject: [PATCH 2/7] fix(webui): being unable to add Kuva (#292) Co-authored-by: Sainan --- static/webui/script.js | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/static/webui/script.js b/static/webui/script.js index 6d1e0a52..4220234a 100644 --- a/static/webui/script.js +++ b/static/webui/script.js @@ -102,7 +102,10 @@ window.itemListPromise = new Promise(resolve => { items.forEach(item => { if (item.uniqueName in data.badItems) { item.name += " (Imposter)"; - } else if (item.uniqueName.substr(0, 18) != "/Lotus/Types/Game/") { + } else if ( + item.uniqueName.substr(0, 18) != "/Lotus/Types/Game/" && + item.uniqueName.substr(0, 18) != "/Lotus/StoreItems/" + ) { const option = document.createElement("option"); option.setAttribute("data-key", item.uniqueName); option.value = item.name; From 60c7d5b5b064a8fe145c3f3f28d1e970c2b0d9ca Mon Sep 17 00:00:00 2001 From: Sainan Date: Wed, 12 Jun 2024 13:59:37 +0200 Subject: [PATCH 3/7] chore: npm audit fix (#293) --- package-lock.json | 28 +++++++++++++++++++++------- 1 file changed, 21 insertions(+), 7 deletions(-) diff --git a/package-lock.json b/package-lock.json index 1fe81846..14580188 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1153,12 +1153,12 @@ } }, "node_modules/braces": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", - "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", + "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", "dev": true, "dependencies": { - "fill-range": "^7.0.1" + "fill-range": "^7.1.1" }, "engines": { "node": ">=8" @@ -1922,9 +1922,9 @@ } }, "node_modules/fill-range": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", - "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", + "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", "dev": true, "dependencies": { "to-regex-range": "^5.0.1" @@ -2022,6 +2022,20 @@ "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==" }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "dev": true, + "hasInstallScript": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, "node_modules/function-bind": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", From 1c6412a6a41aff074d9ab789330c15ba63ed6c83 Mon Sep 17 00:00:00 2001 From: Vampire Kitten <95658710+VampireKitten@users.noreply.github.com> Date: Sat, 15 Jun 2024 00:38:12 +0200 Subject: [PATCH 4/7] feat(webui): change server config through UI (#294) --- .../custom/getConfigDataController.ts | 8 ++ .../custom/updateConfigDataController.ts | 16 ++++ src/routes/custom.ts | 7 +- src/routes/webui.ts | 3 + static/webui/index.html | 78 ++++++++++++++++++- static/webui/script.js | 45 +++++++++++ 6 files changed, 152 insertions(+), 5 deletions(-) create mode 100644 src/controllers/custom/getConfigDataController.ts create mode 100644 src/controllers/custom/updateConfigDataController.ts diff --git a/src/controllers/custom/getConfigDataController.ts b/src/controllers/custom/getConfigDataController.ts new file mode 100644 index 00000000..8d946384 --- /dev/null +++ b/src/controllers/custom/getConfigDataController.ts @@ -0,0 +1,8 @@ +import { RequestHandler } from "express"; +import configFile from "@/config.json"; + +const getConfigDataController: RequestHandler = (_req, res) => { + res.json(configFile); +}; + +export { getConfigDataController }; diff --git a/src/controllers/custom/updateConfigDataController.ts b/src/controllers/custom/updateConfigDataController.ts new file mode 100644 index 00000000..38d6ca35 --- /dev/null +++ b/src/controllers/custom/updateConfigDataController.ts @@ -0,0 +1,16 @@ +import { RequestHandler } from "express"; +import path from "path"; +import fs from "fs"; +const rootDir = path.join(__dirname, "../../.."); + +const updateConfigDataController: RequestHandler = (req) => { + const updateSettingsData = req.body; + + fs.writeFile(path.join(rootDir, "config.json"), updateSettingsData, function(err:any) { + if(err) { + return console.log(err); + } + }); +}; + +export { updateConfigDataController }; diff --git a/src/routes/custom.ts b/src/routes/custom.ts index 8a4218d7..6c0a7416 100644 --- a/src/routes/custom.ts +++ b/src/routes/custom.ts @@ -2,6 +2,8 @@ import express from "express"; import { getItemListsController } from "@/src/controllers/custom/getItemListsController"; import { createAccountController } from "@/src/controllers/custom/createAccountController"; import { addItemController } from "@/src/controllers/custom/addItemController"; +import { getConfigDataController } from "@/src/controllers/custom/getConfigDataController"; +import { updateConfigDataController } from "@/src/controllers/custom/updateConfigDataController"; const customRouter = express.Router(); @@ -10,4 +12,7 @@ customRouter.get("/getItemLists", getItemListsController); customRouter.post("/createAccount", createAccountController); customRouter.post("/addItem", addItemController); -export { customRouter }; +customRouter.get("/config", getConfigDataController); +customRouter.post("/config", updateConfigDataController); + +export { customRouter }; \ No newline at end of file diff --git a/src/routes/webui.ts b/src/routes/webui.ts index 677031c6..93005f34 100644 --- a/src/routes/webui.ts +++ b/src/routes/webui.ts @@ -19,6 +19,9 @@ webuiRouter.use("/webui", (req, res, next) => { }); // Serve virtual routes +webuiRouter.get("/webui/settings", (_req, res) => { + res.sendFile(path.join(rootDir, "static/webui/index.html")); +}); webuiRouter.get("/webui/inventory", (_req, res) => { res.sendFile(path.join(rootDir, "static/webui/index.html")); }); diff --git a/static/webui/index.html b/static/webui/index.html index a680fd33..6ab05bad 100644 --- a/static/webui/index.html +++ b/static/webui/index.html @@ -67,15 +67,21 @@ Mods +
-

- Note: Changes made here will only be reflected in-game when the game re-downloads your inventory. - Visiting the navigation should be the easiest way to trigger that. -

Login using your OpenWF account credentials.

@@ -89,6 +95,10 @@
+

+ Note: Changes made here will only be reflected in-game when the game re-downloads your inventory. + Visiting the navigation should be the easiest way to trigger that. +

Add Items
@@ -133,6 +143,10 @@
+

+ Note: Changes made here will only be reflected in-game when the game re-downloads your inventory. + Visiting the navigation should be the easiest way to trigger that. +

@@ -189,6 +203,62 @@
+
+
+
Change Settings
+ +
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+ + +
+
diff --git a/static/webui/script.js b/static/webui/script.js index 4220234a..b03de1d2 100644 --- a/static/webui/script.js +++ b/static/webui/script.js @@ -15,6 +15,7 @@ function loginFromLocalStorage() { window.accountId = data.id; window.authz = "accountId=" + data.id + "&nonce=" + data.Nonce; updateInventory(); + fetchSettings(); }, () => { logout(); @@ -618,3 +619,47 @@ function doAcquireMod() { $("#mod-to-acquire").on("input", () => { $("#mod-to-acquire").removeClass("is-invalid"); }); + +function fetchSettings() { + fetch('/custom/config') + .then((response) => response.json()) + .then((json) => Object.entries(json).forEach((entry) => { + const [key, value] = entry; + var x = document.getElementById(`${key}`); + if (x!=null) { + if (x.type == "checkbox") { + if (value === true) { + x.setAttribute("checked", "checked") + } + } else if (x.type == "number") { + x.setAttribute("value", `${value}`) + } + } + })); +} + +function doChangeSettings() { + fetch('/custom/config') + .then((response) => response.json()) + .then((json) => { + for(var i in json) { + var x = document.getElementById(`${i}`); + if (x!=null) { + if (x.type == "checkbox") { + if (x.checked === true) { + json[i]=true; + } else { + json[i]=false; + } + } else if (x.type == "number") { + json[i]=parseInt(x.value); + } + } + } + $.post({ + url: "/custom/config", + contentType: "text/plain", + data: JSON.stringify(json, null, 2) + }) + }) +} \ No newline at end of file From ba8a3afce5c2a5dda60b1b365f089a98e18f1c2b Mon Sep 17 00:00:00 2001 From: OrdisPrime Date: Fri, 14 Jun 2024 22:38:33 +0000 Subject: [PATCH 5/7] Apply prettier changes --- .../custom/updateConfigDataController.ts | 8 +-- src/routes/custom.ts | 2 +- static/webui/index.html | 67 ++++++++++++------- static/webui/script.js | 52 +++++++------- 4 files changed, 76 insertions(+), 53 deletions(-) diff --git a/src/controllers/custom/updateConfigDataController.ts b/src/controllers/custom/updateConfigDataController.ts index 38d6ca35..1b13cc60 100644 --- a/src/controllers/custom/updateConfigDataController.ts +++ b/src/controllers/custom/updateConfigDataController.ts @@ -3,11 +3,11 @@ import path from "path"; import fs from "fs"; const rootDir = path.join(__dirname, "../../.."); -const updateConfigDataController: RequestHandler = (req) => { +const updateConfigDataController: RequestHandler = req => { const updateSettingsData = req.body; - - fs.writeFile(path.join(rootDir, "config.json"), updateSettingsData, function(err:any) { - if(err) { + + fs.writeFile(path.join(rootDir, "config.json"), updateSettingsData, function (err: any) { + if (err) { return console.log(err); } }); diff --git a/src/routes/custom.ts b/src/routes/custom.ts index 6c0a7416..476c5997 100644 --- a/src/routes/custom.ts +++ b/src/routes/custom.ts @@ -15,4 +15,4 @@ customRouter.post("/addItem", addItemController); customRouter.get("/config", getConfigDataController); customRouter.post("/config", updateConfigDataController); -export { customRouter }; \ No newline at end of file +export { customRouter }; diff --git a/static/webui/index.html b/static/webui/index.html index 6ab05bad..df711a5f 100644 --- a/static/webui/index.html +++ b/static/webui/index.html @@ -96,8 +96,8 @@

- Note: Changes made here will only be reflected in-game when the game re-downloads your inventory. - Visiting the navigation should be the easiest way to trigger that. + Note: Changes made here will only be reflected in-game when the game re-downloads your + inventory. Visiting the navigation should be the easiest way to trigger that.

Add Items
@@ -144,8 +144,8 @@

- Note: Changes made here will only be reflected in-game when the game re-downloads your inventory. - Visiting the navigation should be the easiest way to trigger that. + Note: Changes made here will only be reflected in-game when the game re-downloads your + inventory. Visiting the navigation should be the easiest way to trigger that.

@@ -208,51 +208,72 @@
Change Settings
- - + +
- +
- +
- - + +
- +
- - + +
- - + +
- - + +
- - + +
- - + +
- +
- +
diff --git a/static/webui/script.js b/static/webui/script.js index b03de1d2..37fa5177 100644 --- a/static/webui/script.js +++ b/static/webui/script.js @@ -621,38 +621,40 @@ $("#mod-to-acquire").on("input", () => { }); function fetchSettings() { - fetch('/custom/config') - .then((response) => response.json()) - .then((json) => Object.entries(json).forEach((entry) => { - const [key, value] = entry; - var x = document.getElementById(`${key}`); - if (x!=null) { - if (x.type == "checkbox") { - if (value === true) { - x.setAttribute("checked", "checked") - } - } else if (x.type == "number") { - x.setAttribute("value", `${value}`) + fetch("/custom/config") + .then(response => response.json()) + .then(json => + Object.entries(json).forEach(entry => { + const [key, value] = entry; + var x = document.getElementById(`${key}`); + if (x != null) { + if (x.type == "checkbox") { + if (value === true) { + x.setAttribute("checked", "checked"); + } + } else if (x.type == "number") { + x.setAttribute("value", `${value}`); + } } - } - })); + }) + ); } function doChangeSettings() { - fetch('/custom/config') - .then((response) => response.json()) - .then((json) => { - for(var i in json) { + fetch("/custom/config") + .then(response => response.json()) + .then(json => { + for (var i in json) { var x = document.getElementById(`${i}`); - if (x!=null) { + if (x != null) { if (x.type == "checkbox") { if (x.checked === true) { - json[i]=true; + json[i] = true; } else { - json[i]=false; + json[i] = false; } } else if (x.type == "number") { - json[i]=parseInt(x.value); + json[i] = parseInt(x.value); } } } @@ -660,6 +662,6 @@ function doChangeSettings() { url: "/custom/config", contentType: "text/plain", data: JSON.stringify(json, null, 2) - }) - }) -} \ No newline at end of file + }); + }); +} From b08fff190687e9210fdf08d79fbb069267a6a586 Mon Sep 17 00:00:00 2001 From: Sainan Date: Sat, 15 Jun 2024 02:50:43 +0200 Subject: [PATCH 6/7] feat: rushing recipes, refactor: addItem (#248) --- .../api/claimCompletedRecipeController.ts | 46 +++-- .../api/inventorySlotsController.ts | 4 +- src/services/inventoryService.ts | 131 +++++++++++- src/services/purchaseService.ts | 186 ++---------------- src/types/inventoryTypes/inventoryTypes.ts | 8 + src/types/purchaseTypes.ts | 8 - 6 files changed, 174 insertions(+), 209 deletions(-) diff --git a/src/controllers/api/claimCompletedRecipeController.ts b/src/controllers/api/claimCompletedRecipeController.ts index 52236166..034b2e10 100644 --- a/src/controllers/api/claimCompletedRecipeController.ts +++ b/src/controllers/api/claimCompletedRecipeController.ts @@ -3,11 +3,11 @@ import { RequestHandler } from "express"; import { logger } from "@/src/utils/logger"; -import { getItemByBlueprint, getItemCategoryByUniqueName } from "@/src/services/itemDataService"; +import { getItemByBlueprint } from "@/src/services/itemDataService"; import { IOid } from "@/src/types/commonTypes"; import { getJSONfromString } from "@/src/helpers/stringHelpers"; import { getAccountIdForRequest } from "@/src/services/loginService"; -import { getInventory } from "@/src/services/inventoryService"; +import { getInventory, updateCurrency, addItem } from "@/src/services/inventoryService"; export interface IClaimCompletedRecipeRequest { RecipeIds: IOid[]; @@ -19,12 +19,10 @@ export const claimCompletedRecipeController: RequestHandler = async (req, res) = const accountId = await getAccountIdForRequest(req); if (!accountId) throw new Error("no account id"); - console.log(claimCompletedRecipeRequest); const inventory = await getInventory(accountId); const pendingRecipe = inventory.PendingRecipes.find( recipe => recipe._id?.toString() === claimCompletedRecipeRequest.RecipeIds[0].$oid ); - console.log(pendingRecipe); if (!pendingRecipe) { logger.error(`no pending recipe found with id ${claimCompletedRecipeRequest.RecipeIds[0].$oid}`); throw new Error(`no pending recipe found with id ${claimCompletedRecipeRequest.RecipeIds[0].$oid}`); @@ -36,29 +34,29 @@ export const claimCompletedRecipeController: RequestHandler = async (req, res) = // throw new Error(`recipe ${pendingRecipe._id} is not ready to be completed`); // } - //get completed Items - const completedItemName = getItemByBlueprint(pendingRecipe.ItemType)?.uniqueName; + inventory.PendingRecipes.pull(pendingRecipe._id); + await inventory.save(); - if (!completedItemName) { + const buildable = getItemByBlueprint(pendingRecipe.ItemType); + if (!buildable) { logger.error(`no completed item found for recipe ${pendingRecipe._id}`); throw new Error(`no completed item found for recipe ${pendingRecipe._id}`); } - const itemCategory = getItemCategoryByUniqueName(completedItemName) as keyof typeof inventory; - console.log(itemCategory); - //TODO: remove all Schema.Mixed for inventory[itemCategory] not to be any - //add item - //inventory[itemCategory]. - //add additional item components like mods or weapons for a sentinel. - //const additionalItemComponents = itemComponents[uniqueName] - //add these items to inventory - //return changes as InventoryChanges - - //remove pending recipe - inventory.PendingRecipes.pull(pendingRecipe._id); - // await inventory.save(); - - logger.debug("Claiming Completed Recipe", { completedItemName }); - - res.json({ InventoryChanges: {} }); + if (req.query.cancel) { + // TODO: Refund items + res.json({}); + } else { + logger.debug("Claiming Recipe", { buildable, pendingRecipe }); + let currencyChanges = {}; + if (req.query.rush && buildable.skipBuildTimePrice) { + currencyChanges = await updateCurrency(buildable.skipBuildTimePrice, true, accountId); + } + res.json({ + InventoryChanges: { + ...currencyChanges, + ...(await addItem(accountId, buildable.uniqueName, buildable.buildQuantity)).InventoryChanges + } + }); + } }; diff --git a/src/controllers/api/inventorySlotsController.ts b/src/controllers/api/inventorySlotsController.ts index fc3c68a7..b026077b 100644 --- a/src/controllers/api/inventorySlotsController.ts +++ b/src/controllers/api/inventorySlotsController.ts @@ -2,7 +2,7 @@ import { getAccountIdForRequest } from "@/src/services/loginService"; import { updateCurrency } from "@/src/services/inventoryService"; import { RequestHandler } from "express"; import { updateSlots } from "@/src/services/inventoryService"; -import { SlotNameToInventoryName } from "@/src/types/purchaseTypes"; +import { InventorySlot } from "@/src/types/inventoryTypes/inventoryTypes"; /* loadout slots are additionally purchased slots only @@ -28,7 +28,7 @@ export const inventorySlotsController: RequestHandler = async (req, res) => { //TODO: check which slot was purchased because pvpBonus is also possible const currencyChanges = await updateCurrency(20, true, accountId); - await updateSlots(accountId, SlotNameToInventoryName.LOADOUT, 1, 1); + await updateSlots(accountId, InventorySlot.PVE_LOADOUTS, 1, 1); //console.log({ InventoryChanges: currencyChanges }, " added loadout changes:"); diff --git a/src/services/inventoryService.ts b/src/services/inventoryService.ts index 357a2120..f21ea25a 100644 --- a/src/services/inventoryService.ts +++ b/src/services/inventoryService.ts @@ -14,7 +14,8 @@ import { IMission, IRawUpgrade, ISeasonChallengeHistory, - ITypeCount + ITypeCount, + InventorySlot } from "@/src/types/inventoryTypes/inventoryTypes"; import { IGenericUpdate } from "../types/genericUpdate"; import { @@ -24,7 +25,7 @@ import { IUpdateChallengeProgressRequest } from "../types/requestTypes"; import { logger } from "@/src/utils/logger"; -import { WeaponTypeInternal, getExalted } from "@/src/services/itemDataService"; +import { WeaponTypeInternal, getWeaponType, getExalted } from "@/src/services/itemDataService"; import { ISyndicateSacrifice, ISyndicateSacrificeResponse } from "../types/syndicateTypes"; export const createInventory = async ( @@ -65,6 +66,132 @@ export const getInventory = async (accountOwnerId: string) => { return inventory; }; +export const addItem = async ( + accountId: string, + typeName: string, + quantity: number = 1 +): Promise<{ InventoryChanges: object }> => { + switch (typeName.substr(1).split("/")[1]) { + case "Powersuits": + if (typeName.includes("EntratiMech")) { + const mechSuit = await addMechSuit(typeName, accountId); + await updateSlots(accountId, InventorySlot.MECHSUITS, 0, 1); + logger.debug("mech suit", mechSuit); + return { + InventoryChanges: { + MechBin: { + count: 1, + platinum: 0, + Slots: -1 + }, + MechSuits: [mechSuit] + } + }; + } + const suit = await addPowerSuit(typeName, accountId); + await updateSlots(accountId, InventorySlot.SUITS, 0, 1); + return { + InventoryChanges: { + SuitBin: { + count: 1, + platinum: 0, + Slots: -1 + }, + Suits: [suit] + } + }; + case "Weapons": + const weaponType = getWeaponType(typeName); + const weapon = await addWeapon(weaponType, typeName, accountId); + await updateSlots(accountId, InventorySlot.WEAPONS, 0, 1); + return { + InventoryChanges: { + WeaponBin: { count: 1, platinum: 0, Slots: -1 }, + [weaponType]: [weapon] + } + }; + case "Interface": + return { + InventoryChanges: { + FlavourItems: [await addCustomization(typeName, accountId)] + } + }; + case "Types": + switch (typeName.substr(1).split("/")[2]) { + case "AvatarImages": + case "SuitCustomizations": + return { + InventoryChanges: { + FlavourItems: [await addCustomization(typeName, accountId)] + } + }; + case "Sentinels": + // TOOD: Sentinels should also grant their DefaultUpgrades & SentinelWeapon. + const sentinel = await addSentinel(typeName, accountId); + await updateSlots(accountId, InventorySlot.SENTINELS, 0, 1); + return { + InventoryChanges: { + SentinelBin: { count: 1, platinum: 0, Slots: -1 }, + Sentinels: [sentinel] + } + }; + case "Items": { + const inventory = await getInventory(accountId); + const miscItemChanges = [ + { + ItemType: typeName, + ItemCount: quantity + } satisfies IMiscItem + ]; + addMiscItems(inventory, miscItemChanges); + await inventory.save(); + return { + InventoryChanges: { + MiscItems: miscItemChanges + } + }; + } + case "Recipes": + case "Consumables": { + // Blueprints for Ciphers, Antitoxins + const inventory = await getInventory(accountId); + const recipeChanges = [ + { + ItemType: typeName, + ItemCount: quantity + } satisfies ITypeCount + ]; + addRecipes(inventory, recipeChanges); + await inventory.save(); + return { + InventoryChanges: { + Recipes: recipeChanges + } + }; + } + case "Restoratives": // Codex Scanner, Remote Observer, Starburst + const inventory = await getInventory(accountId); + const consumablesChanges = [ + { + ItemType: typeName, + ItemCount: quantity + } satisfies IConsumable + ]; + addConsumables(inventory, consumablesChanges); + await inventory.save(); + return { + InventoryChanges: { + Consumables: consumablesChanges + } + }; + } + break; + } + const errorMessage = `unable to add item: ${typeName}`; + logger.error(errorMessage); + throw new Error(errorMessage); +}; + //TODO: maybe genericMethod for all the add methods, they share a lot of logic export const addSentinel = async (sentinelName: string, accountId: string) => { const inventory = await getInventory(accountId); diff --git a/src/services/purchaseService.ts b/src/services/purchaseService.ts index d9ca8e14..ba13666a 100644 --- a/src/services/purchaseService.ts +++ b/src/services/purchaseService.ts @@ -1,22 +1,7 @@ import { parseSlotPurchaseName } from "@/src/helpers/purchaseHelpers"; -import { getWeaponType } from "@/src/services/itemDataService"; import { getSubstringFromKeyword } from "@/src/helpers/stringHelpers"; -import { - addBooster, - addConsumables, - addCustomization, - addMechSuit, - addMiscItems, - addPowerSuit, - addRecipes, - addSentinel, - addWeapon, - getInventory, - updateCurrency, - updateSlots -} from "@/src/services/inventoryService"; -import { IConsumable, IMiscItem, ITypeCount } from "@/src/types/inventoryTypes/inventoryTypes"; -import { IPurchaseRequest, IPurchaseResponse, SlotNameToInventoryName, SlotPurchase } from "@/src/types/purchaseTypes"; +import { addItem, addBooster, updateCurrency, updateSlots } from "@/src/services/inventoryService"; +import { IPurchaseRequest, SlotPurchase } from "@/src/types/purchaseTypes"; import { logger } from "@/src/utils/logger"; export const getStoreItemCategory = (storeItem: string) => { @@ -40,34 +25,24 @@ export const handlePurchase = async (purchaseRequest: IPurchaseRequest, accountI const internalName = purchaseRequest.PurchaseParams.StoreItem.replace("/StoreItems", ""); logger.debug(`store category ${storeCategory}`); - let inventoryChanges; + let purchaseResponse; switch (storeCategory) { - case "Powersuits": - inventoryChanges = await handlePowersuitPurchase(internalName, accountId); - break; - case "Weapons": - inventoryChanges = await handleWeaponsPurchase(internalName, accountId); + default: + purchaseResponse = await addItem(accountId, internalName); break; case "Types": - inventoryChanges = await handleTypesPurchase( + purchaseResponse = await handleTypesPurchase( internalName, accountId, purchaseRequest.PurchaseParams.Quantity ); break; case "Boosters": - inventoryChanges = await handleBoostersPurchase(internalName, accountId); + purchaseResponse = await handleBoostersPurchase(internalName, accountId); break; - case "Interface": - inventoryChanges = await handleCustomizationPurchase(internalName, accountId); - break; - default: - const errorMessage = `unknown store category: ${storeCategory} not implemented or new`; - logger.error(errorMessage); - throw new Error(errorMessage); } - if (!inventoryChanges) throw new Error("purchase response was undefined"); + if (!purchaseResponse) throw new Error("purchase response was undefined"); const currencyChanges = await updateCurrency( purchaseRequest.PurchaseParams.ExpectedPrice, @@ -75,12 +50,12 @@ export const handlePurchase = async (purchaseRequest: IPurchaseRequest, accountI accountId ); - inventoryChanges.InventoryChanges = { + purchaseResponse.InventoryChanges = { ...currencyChanges, - ...inventoryChanges.InventoryChanges + ...purchaseResponse.InventoryChanges }; - return inventoryChanges; + return purchaseResponse; }; export const slotPurchaseNameToSlotName: SlotPurchase = { @@ -126,102 +101,18 @@ const handleSlotPurchase = async (slotPurchaseNameFull: string, accountId: strin }; }; -const handleWeaponsPurchase = async (weaponName: string, accountId: string) => { - const weaponType = getWeaponType(weaponName); - const addedWeapon = await addWeapon(weaponType, weaponName, accountId); - - await updateSlots(accountId, SlotNameToInventoryName.WEAPON, 0, 1); - - return { - InventoryChanges: { - WeaponBin: { count: 1, platinum: 0, Slots: -1 }, - [weaponType]: [addedWeapon] - } - } as IPurchaseResponse; -}; - -const handlePowersuitPurchase = async (powersuitName: string, accountId: string) => { - if (powersuitName.includes("EntratiMech")) { - const mechSuit = await addMechSuit(powersuitName, accountId); - - await updateSlots(accountId, SlotNameToInventoryName.MECHSUIT, 0, 1); - logger.debug("mech suit", mechSuit); - - return { - InventoryChanges: { - MechBin: { - count: 1, - platinum: 0, - Slots: -1 - }, - MechSuits: [mechSuit] - } - } as IPurchaseResponse; - } - - const suit = await addPowerSuit(powersuitName, accountId); - await updateSlots(accountId, SlotNameToInventoryName.SUIT, 0, 1); - - return { - InventoryChanges: { - SuitBin: { - count: 1, - platinum: 0, - Slots: -1 - }, - Suits: [suit] - } - }; -}; - //TODO: change to getInventory, apply changes then save at the end const handleTypesPurchase = async (typesName: string, accountId: string, quantity: number) => { const typeCategory = getStoreItemTypesCategory(typesName); logger.debug(`type category ${typeCategory}`); switch (typeCategory) { - case "AvatarImages": - case "SuitCustomizations": - return await handleCustomizationPurchase(typesName, accountId); - case "Sentinels": - return await handleSentinelPurchase(typesName, accountId); + default: + return await addItem(accountId, typesName, quantity); case "SlotItems": return await handleSlotPurchase(typesName, accountId); - case "Items": - return await handleMiscItemPurchase(typesName, accountId, quantity); - case "Recipes": - case "Consumables": // Blueprints for Ciphers, Antitoxins - return await handleRecipesPurchase(typesName, accountId, quantity); - case "Restoratives": // Codex Scanner, Remote Observer, Starburst - return await handleRestorativesPurchase(typesName, accountId, quantity); - break; - default: - throw new Error(`unknown Types category: ${typeCategory} not implemented or new`); } }; -const handleSentinelPurchase = async (sentinelName: string, accountId: string) => { - const sentinel = await addSentinel(sentinelName, accountId); - - await updateSlots(accountId, SlotNameToInventoryName.SENTINEL, 0, 1); - - return { - InventoryChanges: { - SentinelBin: { count: 1, platinum: 0, Slots: -1 }, - Sentinels: [sentinel] - } - }; -}; - -const handleCustomizationPurchase = async (customizationName: string, accountId: string) => { - const customization = await addCustomization(customizationName, accountId); - - return { - InventoryChanges: { - FlavourItems: [customization] - } - }; -}; - const boosterCollection = [ "/Lotus/Types/Boosters/ResourceAmountBooster", "/Lotus/Types/Boosters/AffinityBooster", @@ -247,54 +138,3 @@ const handleBoostersPurchase = async (boosterStoreName: string, accountId: strin } }; }; - -const handleMiscItemPurchase = async (uniqueName: string, accountId: string, quantity: number) => { - const inventory = await getInventory(accountId); - const miscItemChanges = [ - { - ItemType: uniqueName, - ItemCount: quantity - } satisfies IMiscItem - ]; - addMiscItems(inventory, miscItemChanges); - await inventory.save(); - return { - InventoryChanges: { - MiscItems: miscItemChanges - } - }; -}; - -const handleRecipesPurchase = async (uniqueName: string, accountId: string, quantity: number) => { - const inventory = await getInventory(accountId); - const recipeChanges = [ - { - ItemType: uniqueName, - ItemCount: quantity - } satisfies ITypeCount - ]; - addRecipes(inventory, recipeChanges); - await inventory.save(); - return { - InventoryChanges: { - Recipes: recipeChanges - } - }; -}; - -const handleRestorativesPurchase = async (uniqueName: string, accountId: string, quantity: number) => { - const inventory = await getInventory(accountId); - const consumablesChanges = [ - { - ItemType: uniqueName, - ItemCount: quantity - } satisfies IConsumable - ]; - addConsumables(inventory, consumablesChanges); - await inventory.save(); - return { - InventoryChanges: { - Consumables: consumablesChanges - } - }; -}; diff --git a/src/types/inventoryTypes/inventoryTypes.ts b/src/types/inventoryTypes/inventoryTypes.ts index 66f863ee..3bc903cd 100644 --- a/src/types/inventoryTypes/inventoryTypes.ts +++ b/src/types/inventoryTypes/inventoryTypes.ts @@ -419,6 +419,14 @@ export interface ICrewShipHarnessConfig { Upgrades?: string[]; } +export enum InventorySlot { + SUITS = "SuitBin", + WEAPONS = "WeaponBin", + MECHSUITS = "MechBin", + PVE_LOADOUTS = "PveBonusLoadoutBin", + SENTINELS = "SentinelBin" +} + export interface ISlots { Extra: number; // can be undefined, but not if used via mongoose Slots: number; diff --git a/src/types/purchaseTypes.ts b/src/types/purchaseTypes.ts index c6b5d648..73ee085c 100644 --- a/src/types/purchaseTypes.ts +++ b/src/types/purchaseTypes.ts @@ -42,14 +42,6 @@ export type IBinChanges = { Extra?: number; }; -export enum SlotNameToInventoryName { - SUIT = "SuitBin", - WEAPON = "WeaponBin", - MECHSUIT = "MechBin", - LOADOUT = "PveBonusLoadoutBin", - SENTINEL = "SentinelBin" -} - export type SlotPurchaseName = | "SuitSlotItem" | "TwoSentinelSlotItem" From e5451d52270d7d11bc51ec8f72b9f63c4fa622ff Mon Sep 17 00:00:00 2001 From: Sainan Date: Sat, 15 Jun 2024 02:52:45 +0200 Subject: [PATCH 7/7] fix: config change on WebUI causing hot reload in dev mode (#297) --- .../custom/getConfigDataController.ts | 4 +- .../custom/updateConfigDataController.ts | 15 +- src/services/configService.ts | 25 +++- static/webui/index.html | 132 ++++++++---------- 4 files changed, 86 insertions(+), 90 deletions(-) diff --git a/src/controllers/custom/getConfigDataController.ts b/src/controllers/custom/getConfigDataController.ts index 8d946384..9a8684ca 100644 --- a/src/controllers/custom/getConfigDataController.ts +++ b/src/controllers/custom/getConfigDataController.ts @@ -1,8 +1,8 @@ import { RequestHandler } from "express"; -import configFile from "@/config.json"; +import { config } from "@/src/services/configService"; const getConfigDataController: RequestHandler = (_req, res) => { - res.json(configFile); + res.json(config); }; export { getConfigDataController }; diff --git a/src/controllers/custom/updateConfigDataController.ts b/src/controllers/custom/updateConfigDataController.ts index 1b13cc60..b7521a1a 100644 --- a/src/controllers/custom/updateConfigDataController.ts +++ b/src/controllers/custom/updateConfigDataController.ts @@ -1,16 +1,9 @@ import { RequestHandler } from "express"; -import path from "path"; -import fs from "fs"; -const rootDir = path.join(__dirname, "../../.."); +import { updateConfig } from "@/src/services/configService"; -const updateConfigDataController: RequestHandler = req => { - const updateSettingsData = req.body; - - fs.writeFile(path.join(rootDir, "config.json"), updateSettingsData, function (err: any) { - if (err) { - return console.log(err); - } - }); +const updateConfigDataController: RequestHandler = async (req, res) => { + await updateConfig(req.body.toString()); + res.end(); }; export { updateConfigDataController }; diff --git a/src/services/configService.ts b/src/services/configService.ts index e5199a3c..8290047b 100644 --- a/src/services/configService.ts +++ b/src/services/configService.ts @@ -1,4 +1,22 @@ -import rawConfig from "@/config.json"; +import path from "path"; +import fs from "fs"; +import fsPromises from "fs/promises"; +import { logger } from "@/src/utils/logger"; + +const rootDir = path.join(__dirname, "../.."); +const repoDir = path.basename(rootDir) == "build" ? path.join(rootDir, "..") : rootDir; +const configPath = path.join(repoDir, "config.json"); +export const config: IConfig = JSON.parse(fs.readFileSync(configPath, "utf-8")); + +let amnesia = false; +fs.watchFile(configPath, () => { + if (amnesia) { + amnesia = false; + } else { + logger.info("Detected a change to config.json, reloading its contents."); + Object.assign(config, JSON.parse(fs.readFileSync(configPath, "utf-8"))); + } +}); interface IConfig { mongodbUrl: string; @@ -26,4 +44,7 @@ interface ILoggerConfig { level: string; // "fatal" | "error" | "warn" | "info" | "http" | "debug" | "trace"; } -export const config: IConfig = rawConfig; +export const updateConfig = async (data: string) => { + amnesia = true; + return await fsPromises.writeFile(configPath, data); +}; diff --git a/static/webui/index.html b/static/webui/index.html index df711a5f..d19f125a 100644 --- a/static/webui/index.html +++ b/static/webui/index.html @@ -204,81 +204,63 @@
-
-
Change Settings
- -
- - -
-
- - -
-
- - -
-
- - -
-
- - -
-
- - -
-
- - -
-
- - -
-
- - -
-
- - -
-
- - -
-
- - -
- - -
+
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+ +