diff --git a/src/controllers/api/artifactsController.ts b/src/controllers/api/artifactsController.ts index 4fb2155d..362aa971 100644 --- a/src/controllers/api/artifactsController.ts +++ b/src/controllers/api/artifactsController.ts @@ -3,6 +3,7 @@ import { getAccountIdForRequest } from "../../services/loginService.ts"; import type { RequestHandler } from "express"; import type { IInventoryClient, IUpgradeClient } from "../../types/inventoryTypes/inventoryTypes.ts"; import { addMods, getInventory } from "../../services/inventoryService.ts"; +import { broadcastInventoryUpdate } from "../../services/wsService.ts"; export const artifactsController: RequestHandler = async (req, res) => { const accountId = await getAccountIdForRequest(req); @@ -57,6 +58,7 @@ export const artifactsController: RequestHandler = async (req, res) => { } res.send(itemId); + broadcastInventoryUpdate(req); }; interface IArtifactsRequest { diff --git a/src/controllers/api/gildWeaponController.ts b/src/controllers/api/gildWeaponController.ts index 0bd921b3..dea62765 100644 --- a/src/controllers/api/gildWeaponController.ts +++ b/src/controllers/api/gildWeaponController.ts @@ -1,6 +1,6 @@ import type { RequestHandler } from "express"; import { getAccountIdForRequest } from "../../services/loginService.ts"; -import { sendWsBroadcastTo } from "../../services/wsService.ts"; +import { broadcastInventoryUpdate } from "../../services/wsService.ts"; import { getJSONfromString } from "../../helpers/stringHelpers.ts"; import { addMiscItems, getInventory } from "../../services/inventoryService.ts"; import type { TEquipmentKey } from "../../types/inventoryTypes/inventoryTypes.ts"; @@ -75,5 +75,5 @@ export const gildWeaponController: RequestHandler = async (req, res) => { InventoryChanges: inventoryChanges, AffiliationMods: affiliationMods }); - sendWsBroadcastTo(accountId, { update_inventory: true }); + broadcastInventoryUpdate(req); }; diff --git a/src/controllers/api/infestedFoundryController.ts b/src/controllers/api/infestedFoundryController.ts index de0b714e..f46abd6c 100644 --- a/src/controllers/api/infestedFoundryController.ts +++ b/src/controllers/api/infestedFoundryController.ts @@ -20,6 +20,7 @@ import { applyCheatsToInfestedFoundry, handleSubsumeCompletion } from "../../services/infestedFoundryService.ts"; +import { sendWsBroadcastToGame } from "../../services/wsService.ts"; export const infestedFoundryController: RequestHandler = async (req, res) => { const account = await getAccountForRequest(req); @@ -363,6 +364,7 @@ export const infestedFoundryController: RequestHandler = async (req, res) => { ); addRecipes(inventory, recipeChanges); await inventory.save(); + sendWsBroadcastToGame(account._id.toString(), { sync_inventory: true }); } res.end(); break; diff --git a/src/controllers/api/loginController.ts b/src/controllers/api/loginController.ts index d93a9229..beac741e 100644 --- a/src/controllers/api/loginController.ts +++ b/src/controllers/api/loginController.ts @@ -8,7 +8,7 @@ import { createAccount, createNonce, getUsernameFromEmail, isCorrectPassword } f import type { IDatabaseAccountJson, ILoginRequest, ILoginResponse } from "../../types/loginTypes.ts"; import { logger } from "../../utils/logger.ts"; import { version_compare } from "../../helpers/inventoryHelpers.ts"; -import { sendWsBroadcastTo } from "../../services/wsService.ts"; +import { handleNonceInvalidation } from "../../services/wsService.ts"; export const loginController: RequestHandler = async (request, response) => { const loginRequest = JSON.parse(String(request.body)) as ILoginRequest; // parse octet stream of json data to json object @@ -74,7 +74,7 @@ export const loginController: RequestHandler = async (request, response) => { account.LastLogin = new Date(); await account.save(); - sendWsBroadcastTo(account._id.toString(), { nonce_updated: true }); + handleNonceInvalidation(account._id.toString()); response.json(createLoginResponse(myAddress, myUrlBase, account.toJSON(), buildLabel)); }; diff --git a/src/controllers/api/logoutController.ts b/src/controllers/api/logoutController.ts index d1c13c2d..371d76fa 100644 --- a/src/controllers/api/logoutController.ts +++ b/src/controllers/api/logoutController.ts @@ -1,6 +1,6 @@ import type { RequestHandler } from "express"; import { Account } from "../../models/loginModel.ts"; -import { sendWsBroadcastTo } from "../../services/wsService.ts"; +import { handleNonceInvalidation } from "../../services/wsService.ts"; export const logoutController: RequestHandler = async (req, res) => { if (!req.query.accountId) { @@ -21,7 +21,7 @@ export const logoutController: RequestHandler = async (req, res) => { } ); if (stat.modifiedCount) { - sendWsBroadcastTo(req.query.accountId as string, { nonce_updated: true }); + handleNonceInvalidation(req.query.accountId as string); } res.writeHead(200, { diff --git a/src/controllers/api/maturePetController.ts b/src/controllers/api/maturePetController.ts index 9ddc2786..38c59523 100644 --- a/src/controllers/api/maturePetController.ts +++ b/src/controllers/api/maturePetController.ts @@ -2,6 +2,7 @@ import { getJSONfromString } from "../../helpers/stringHelpers.ts"; import { getInventory } from "../../services/inventoryService.ts"; import { getAccountIdForRequest } from "../../services/loginService.ts"; import type { RequestHandler } from "express"; +import { broadcastInventoryUpdate } from "../../services/wsService.ts"; export const maturePetController: RequestHandler = async (req, res) => { const accountId = await getAccountIdForRequest(req); @@ -19,6 +20,7 @@ export const maturePetController: RequestHandler = async (req, res) => { : [details.DominantTraits.FurPattern, details.DominantTraits.FurPattern, details.DominantTraits.FurPattern], unmature: data.revert }); + broadcastInventoryUpdate(req); }; interface IMaturePetRequest { diff --git a/src/controllers/api/modularWeaponCraftingController.ts b/src/controllers/api/modularWeaponCraftingController.ts index 7e235241..52559fb8 100644 --- a/src/controllers/api/modularWeaponCraftingController.ts +++ b/src/controllers/api/modularWeaponCraftingController.ts @@ -1,6 +1,6 @@ import type { RequestHandler } from "express"; import { getAccountIdForRequest } from "../../services/loginService.ts"; -import { sendWsBroadcastTo } from "../../services/wsService.ts"; +import { broadcastInventoryUpdate } from "../../services/wsService.ts"; import { getJSONfromString } from "../../helpers/stringHelpers.ts"; import { getInventory, @@ -197,5 +197,5 @@ export const modularWeaponCraftingController: RequestHandler = async (req, res) MiscItems: miscItemChanges } }); - sendWsBroadcastTo(accountId, { update_inventory: true }); + broadcastInventoryUpdate(req); }; diff --git a/src/controllers/api/nameWeaponController.ts b/src/controllers/api/nameWeaponController.ts index ece87fef..aecf7ea6 100644 --- a/src/controllers/api/nameWeaponController.ts +++ b/src/controllers/api/nameWeaponController.ts @@ -3,7 +3,7 @@ import { getAccountIdForRequest } from "../../services/loginService.ts"; import { getInventory, updateCurrency } from "../../services/inventoryService.ts"; import { getJSONfromString } from "../../helpers/stringHelpers.ts"; import type { TEquipmentKey } from "../../types/inventoryTypes/inventoryTypes.ts"; -import { sendWsBroadcastTo } from "../../services/wsService.ts"; +import { broadcastInventoryUpdate } from "../../services/wsService.ts"; interface INameWeaponRequest { ItemName: string; @@ -28,5 +28,5 @@ export const nameWeaponController: RequestHandler = async (req, res) => { res.json({ InventoryChanges: currencyChanges }); - sendWsBroadcastTo(accountId, { update_inventory: true }); + broadcastInventoryUpdate(req); }; diff --git a/src/controllers/api/releasePetController.ts b/src/controllers/api/releasePetController.ts index fb707b7a..f034f731 100644 --- a/src/controllers/api/releasePetController.ts +++ b/src/controllers/api/releasePetController.ts @@ -1,7 +1,7 @@ import { getJSONfromString } from "../../helpers/stringHelpers.ts"; import { getInventory, updateCurrency } from "../../services/inventoryService.ts"; import { getAccountIdForRequest } from "../../services/loginService.ts"; -import { sendWsBroadcastTo } from "../../services/wsService.ts"; +import { broadcastInventoryUpdate } from "../../services/wsService.ts"; import type { RequestHandler } from "express"; export const releasePetController: RequestHandler = async (req, res) => { @@ -20,7 +20,7 @@ export const releasePetController: RequestHandler = async (req, res) => { await inventory.save(); res.json({ inventoryChanges }); // Not a mistake; it's "inventoryChanges" here. - sendWsBroadcastTo(accountId, { update_inventory: true }); + broadcastInventoryUpdate(req); }; interface IReleasePetRequest { diff --git a/src/controllers/api/renamePetController.ts b/src/controllers/api/renamePetController.ts index d1f0cc3b..73e54172 100644 --- a/src/controllers/api/renamePetController.ts +++ b/src/controllers/api/renamePetController.ts @@ -1,7 +1,7 @@ import { getJSONfromString } from "../../helpers/stringHelpers.ts"; import { getInventory, updateCurrency } from "../../services/inventoryService.ts"; import { getAccountIdForRequest } from "../../services/loginService.ts"; -import { sendWsBroadcastTo } from "../../services/wsService.ts"; +import { broadcastInventoryUpdate } from "../../services/wsService.ts"; import type { IInventoryChanges } from "../../types/purchaseTypes.ts"; import type { RequestHandler } from "express"; @@ -23,7 +23,7 @@ export const renamePetController: RequestHandler = async (req, res) => { ...data, inventoryChanges: inventoryChanges }); - sendWsBroadcastTo(accountId, { update_inventory: true }); + broadcastInventoryUpdate(req); }; interface IRenamePetRequest { diff --git a/src/controllers/api/sellController.ts b/src/controllers/api/sellController.ts index dd160c5a..cf74ca92 100644 --- a/src/controllers/api/sellController.ts +++ b/src/controllers/api/sellController.ts @@ -17,7 +17,7 @@ import { InventorySlot } from "../../types/inventoryTypes/inventoryTypes.ts"; import { ExportDojoRecipes } from "warframe-public-export-plus"; import type { IInventoryChanges } from "../../types/purchaseTypes.ts"; import type { TInventoryDatabaseDocument } from "../../models/inventoryModels/inventoryModel.ts"; -import { sendWsBroadcastEx } from "../../services/wsService.ts"; +import { broadcastInventoryUpdate } from "../../services/wsService.ts"; import { parseFusionTreasure } from "../../helpers/inventoryHelpers.ts"; export const sellController: RequestHandler = async (req, res) => { @@ -307,7 +307,7 @@ export const sellController: RequestHandler = async (req, res) => { res.json({ inventoryChanges: inventoryChanges // "inventoryChanges" for this response instead of the usual "InventoryChanges" }); - sendWsBroadcastEx({ update_inventory: true }, accountId, parseInt(String(req.query.wsid))); + broadcastInventoryUpdate(req); }; interface ISellRequest { diff --git a/src/controllers/api/setSupportedSyndicateController.ts b/src/controllers/api/setSupportedSyndicateController.ts index 53db10b9..8357f424 100644 --- a/src/controllers/api/setSupportedSyndicateController.ts +++ b/src/controllers/api/setSupportedSyndicateController.ts @@ -1,6 +1,7 @@ import type { RequestHandler } from "express"; import { getAccountIdForRequest } from "../../services/loginService.ts"; import { Inventory } from "../../models/inventoryModels/inventoryModel.ts"; +import { broadcastInventoryUpdate } from "../../services/wsService.ts"; export const setSupportedSyndicateController: RequestHandler = async (req, res) => { const accountId = await getAccountIdForRequest(req); @@ -15,4 +16,5 @@ export const setSupportedSyndicateController: RequestHandler = async (req, res) ); res.end(); + broadcastInventoryUpdate(req); }; diff --git a/src/controllers/custom/abilityOverrideController.ts b/src/controllers/custom/abilityOverrideController.ts index 47b7e13a..135e0eb9 100644 --- a/src/controllers/custom/abilityOverrideController.ts +++ b/src/controllers/custom/abilityOverrideController.ts @@ -1,5 +1,6 @@ import { getInventory } from "../../services/inventoryService.ts"; import { getAccountIdForRequest } from "../../services/loginService.ts"; +import { broadcastInventoryUpdate } from "../../services/wsService.ts"; import type { TEquipmentKey } from "../../types/inventoryTypes/inventoryTypes.ts"; import type { RequestHandler } from "express"; @@ -19,6 +20,7 @@ export const abilityOverrideController: RequestHandler = async (req, res) => { } } res.end(); + broadcastInventoryUpdate(req); }; interface IAbilityOverrideRequest { diff --git a/src/controllers/custom/addCurrencyController.ts b/src/controllers/custom/addCurrencyController.ts index 3df52a64..c186818a 100644 --- a/src/controllers/custom/addCurrencyController.ts +++ b/src/controllers/custom/addCurrencyController.ts @@ -3,6 +3,7 @@ import { getAccountIdForRequest } from "../../services/loginService.ts"; import { addFusionPoints, getInventory } from "../../services/inventoryService.ts"; import { getGuildForRequestEx, hasGuildPermission } from "../../services/guildService.ts"; import { GuildPermission } from "../../types/guildTypes.ts"; +import { broadcastInventoryUpdate } from "../../services/wsService.ts"; export const addCurrencyController: RequestHandler = async (req, res) => { const accountId = await getAccountIdForRequest(req); @@ -24,6 +25,7 @@ export const addCurrencyController: RequestHandler = async (req, res) => { } if (!request.currency.startsWith("Vault")) { await inventory.save(); + broadcastInventoryUpdate(req); } res.end(); }; diff --git a/src/controllers/custom/addItemsController.ts b/src/controllers/custom/addItemsController.ts index 1dc76718..4add37ac 100644 --- a/src/controllers/custom/addItemsController.ts +++ b/src/controllers/custom/addItemsController.ts @@ -1,6 +1,7 @@ import { getAccountIdForRequest } from "../../services/loginService.ts"; import { getInventory, addItem } from "../../services/inventoryService.ts"; import type { RequestHandler } from "express"; +import { broadcastInventoryUpdate } from "../../services/wsService.ts"; export const addItemsController: RequestHandler = async (req, res) => { const accountId = await getAccountIdForRequest(req); @@ -11,6 +12,7 @@ export const addItemsController: RequestHandler = async (req, res) => { } await inventory.save(); res.end(); + broadcastInventoryUpdate(req); }; interface IAddItemRequest { diff --git a/src/controllers/custom/addMissingHelminthBlueprintsController.ts b/src/controllers/custom/addMissingHelminthBlueprintsController.ts index 27f6dec8..3aae37a3 100644 --- a/src/controllers/custom/addMissingHelminthBlueprintsController.ts +++ b/src/controllers/custom/addMissingHelminthBlueprintsController.ts @@ -2,6 +2,7 @@ import { getAccountIdForRequest } from "../../services/loginService.ts"; import { getInventory, addRecipes } from "../../services/inventoryService.ts"; import type { RequestHandler } from "express"; import { ExportRecipes } from "warframe-public-export-plus"; +import { broadcastInventoryUpdate } from "../../services/wsService.ts"; export const addMissingHelminthBlueprintsController: RequestHandler = async (req, res) => { const accountId = await getAccountIdForRequest(req); @@ -21,4 +22,5 @@ export const addMissingHelminthBlueprintsController: RequestHandler = async (req await inventory.save(); res.end(); + broadcastInventoryUpdate(req); }; diff --git a/src/controllers/custom/addMissingMaxRankModsController.ts b/src/controllers/custom/addMissingMaxRankModsController.ts index 46a299a3..ddff0cf7 100644 --- a/src/controllers/custom/addMissingMaxRankModsController.ts +++ b/src/controllers/custom/addMissingMaxRankModsController.ts @@ -2,6 +2,7 @@ import { getInventory } from "../../services/inventoryService.ts"; import { getAccountIdForRequest } from "../../services/loginService.ts"; import type { RequestHandler } from "express"; import { ExportArcanes, ExportUpgrades } from "warframe-public-export-plus"; +import { broadcastInventoryUpdate } from "../../services/wsService.ts"; export const addMissingMaxRankModsController: RequestHandler = async (req, res) => { const accountId = await getAccountIdForRequest(req); @@ -41,4 +42,5 @@ export const addMissingMaxRankModsController: RequestHandler = async (req, res) await inventory.save(); res.end(); + broadcastInventoryUpdate(req); }; diff --git a/src/controllers/custom/addXpController.ts b/src/controllers/custom/addXpController.ts index 0b469168..7ca1d2d9 100644 --- a/src/controllers/custom/addXpController.ts +++ b/src/controllers/custom/addXpController.ts @@ -1,5 +1,6 @@ import { applyClientEquipmentUpdates, getInventory } from "../../services/inventoryService.ts"; import { getAccountIdForRequest } from "../../services/loginService.ts"; +import { broadcastInventoryUpdate } from "../../services/wsService.ts"; import type { IOid } from "../../types/commonTypes.ts"; import type { IEquipmentClient } from "../../types/equipmentTypes.ts"; import type { TEquipmentKey } from "../../types/inventoryTypes/inventoryTypes.ts"; @@ -25,6 +26,7 @@ export const addXpController: RequestHandler = async (req, res) => { } await inventory.save(); res.end(); + broadcastInventoryUpdate(req); }; type IAddXpRequest = { diff --git a/src/controllers/custom/changeModularPartsController.ts b/src/controllers/custom/changeModularPartsController.ts index df63da13..32e17fda 100644 --- a/src/controllers/custom/changeModularPartsController.ts +++ b/src/controllers/custom/changeModularPartsController.ts @@ -1,5 +1,6 @@ import { getInventory } from "../../services/inventoryService.ts"; import { getAccountIdForRequest } from "../../services/loginService.ts"; +import { broadcastInventoryUpdate } from "../../services/wsService.ts"; import type { TEquipmentKey } from "../../types/inventoryTypes/inventoryTypes.ts"; import type { RequestHandler } from "express"; @@ -20,6 +21,7 @@ export const changeModularPartsController: RequestHandler = async (req, res) => await inventory.save(); } res.end(); + broadcastInventoryUpdate(req); }; interface IUpdateFingerPrintRequest { diff --git a/src/controllers/custom/editSuitInvigorationUpgradeController.ts b/src/controllers/custom/editSuitInvigorationUpgradeController.ts index 200b403a..713b1c0e 100644 --- a/src/controllers/custom/editSuitInvigorationUpgradeController.ts +++ b/src/controllers/custom/editSuitInvigorationUpgradeController.ts @@ -1,6 +1,7 @@ import { getAccountIdForRequest } from "../../services/loginService.ts"; import { getInventory } from "../../services/inventoryService.ts"; import type { RequestHandler } from "express"; +import { broadcastInventoryUpdate } from "../../services/wsService.ts"; const DEFAULT_UPGRADE_EXPIRY_MS = 7 * 24 * 60 * 60 * 1000; // 7 days @@ -31,4 +32,5 @@ export const editSuitInvigorationUpgradeController: RequestHandler = async (req, } await inventory.save(); res.end(); + broadcastInventoryUpdate(req); }; diff --git a/src/controllers/custom/importController.ts b/src/controllers/custom/importController.ts index 02528e64..1dda3189 100644 --- a/src/controllers/custom/importController.ts +++ b/src/controllers/custom/importController.ts @@ -3,6 +3,7 @@ import { getInventory } from "../../services/inventoryService.ts"; import { getLoadout } from "../../services/loadoutService.ts"; import { getAccountIdForRequest } from "../../services/loginService.ts"; import { getPersonalRooms } from "../../services/personalRoomsService.ts"; +import { broadcastInventoryUpdate } from "../../services/wsService.ts"; import type { IInventoryClient } from "../../types/inventoryTypes/inventoryTypes.ts"; import type { IGetShipResponse } from "../../types/personalRoomsTypes.ts"; import type { RequestHandler } from "express"; @@ -32,6 +33,7 @@ export const importController: RequestHandler = async (req, res) => { } res.end(); + broadcastInventoryUpdate(req); }; interface IImportRequest { diff --git a/src/controllers/custom/manageQuestsController.ts b/src/controllers/custom/manageQuestsController.ts index 4c7d614d..b1cbde84 100644 --- a/src/controllers/custom/manageQuestsController.ts +++ b/src/controllers/custom/manageQuestsController.ts @@ -9,6 +9,7 @@ import { import { logger } from "../../utils/logger.ts"; import type { RequestHandler } from "express"; import { ExportKeys } from "warframe-public-export-plus"; +import { broadcastInventoryUpdate } from "../../services/wsService.ts"; export const manageQuestsController: RequestHandler = async (req, res) => { const accountId = await getAccountIdForRequest(req); @@ -157,4 +158,5 @@ export const manageQuestsController: RequestHandler = async (req, res) => { await inventory.save(); res.status(200).end(); + broadcastInventoryUpdate(req); }; diff --git a/src/controllers/custom/popArchonCrystalUpgradeController.ts b/src/controllers/custom/popArchonCrystalUpgradeController.ts index 2d5e40b9..9fdb12ff 100644 --- a/src/controllers/custom/popArchonCrystalUpgradeController.ts +++ b/src/controllers/custom/popArchonCrystalUpgradeController.ts @@ -1,6 +1,7 @@ import type { RequestHandler } from "express"; import { getAccountIdForRequest } from "../../services/loginService.ts"; import { getInventory } from "../../services/inventoryService.ts"; +import { broadcastInventoryUpdate } from "../../services/wsService.ts"; export const popArchonCrystalUpgradeController: RequestHandler = async (req, res) => { const accountId = await getAccountIdForRequest(req); @@ -12,6 +13,7 @@ export const popArchonCrystalUpgradeController: RequestHandler = async (req, res ); await inventory.save(); res.end(); + broadcastInventoryUpdate(req); return; } res.status(400).end(); diff --git a/src/controllers/custom/pushArchonCrystalUpgradeController.ts b/src/controllers/custom/pushArchonCrystalUpgradeController.ts index 6eb7276e..aae46b31 100644 --- a/src/controllers/custom/pushArchonCrystalUpgradeController.ts +++ b/src/controllers/custom/pushArchonCrystalUpgradeController.ts @@ -1,6 +1,7 @@ import type { RequestHandler } from "express"; import { getAccountIdForRequest } from "../../services/loginService.ts"; import { getInventory } from "../../services/inventoryService.ts"; +import { broadcastInventoryUpdate } from "../../services/wsService.ts"; export const pushArchonCrystalUpgradeController: RequestHandler = async (req, res) => { const accountId = await getAccountIdForRequest(req); @@ -15,6 +16,7 @@ export const pushArchonCrystalUpgradeController: RequestHandler = async (req, re } await inventory.save(); res.end(); + broadcastInventoryUpdate(req); return; } } diff --git a/src/controllers/custom/setAccountCheatController.ts b/src/controllers/custom/setAccountCheatController.ts index 5a188777..07a1da4f 100644 --- a/src/controllers/custom/setAccountCheatController.ts +++ b/src/controllers/custom/setAccountCheatController.ts @@ -1,5 +1,6 @@ import { getInventory } from "../../services/inventoryService.ts"; import { getAccountIdForRequest } from "../../services/loginService.ts"; +import { sendWsBroadcastTo } from "../../services/wsService.ts"; import type { IAccountCheats } from "../../types/inventoryTypes/inventoryTypes.ts"; import type { RequestHandler } from "express"; @@ -10,6 +11,9 @@ export const setAccountCheatController: RequestHandler = async (req, res) => { inventory[payload.key] = payload.value; await inventory.save(); res.end(); + if (["infiniteCredits", "infinitePlatinum", "infiniteEndo", "infiniteRegalAya"].indexOf(payload.key) != -1) { + sendWsBroadcastTo(accountId, { update_inventory: true, sync_inventory: true }); + } }; interface ISetAccountCheatRequest { diff --git a/src/controllers/custom/setBoosterController.ts b/src/controllers/custom/setBoosterController.ts index 756aad98..b3109a2e 100644 --- a/src/controllers/custom/setBoosterController.ts +++ b/src/controllers/custom/setBoosterController.ts @@ -2,6 +2,7 @@ import { getAccountIdForRequest } from "../../services/loginService.ts"; import { getInventory } from "../../services/inventoryService.ts"; import type { RequestHandler } from "express"; import { ExportBoosters } from "warframe-public-export-plus"; +import { broadcastInventoryUpdate } from "../../services/wsService.ts"; const I32_MAX = 0x7fffffff; @@ -42,4 +43,5 @@ export const setBoosterController: RequestHandler = async (req, res) => { } await inventory.save(); res.end(); + broadcastInventoryUpdate(req); }; diff --git a/src/controllers/custom/setEvolutionProgressController.ts b/src/controllers/custom/setEvolutionProgressController.ts index c68ccaff..2c6a28a7 100644 --- a/src/controllers/custom/setEvolutionProgressController.ts +++ b/src/controllers/custom/setEvolutionProgressController.ts @@ -1,6 +1,7 @@ import { getInventory } from "../../services/inventoryService.ts"; import { getAccountIdForRequest } from "../../services/loginService.ts"; import type { RequestHandler } from "express"; +import { broadcastInventoryUpdate } from "../../services/wsService.ts"; export const setEvolutionProgressController: RequestHandler = async (req, res) => { const accountId = await getAccountIdForRequest(req); @@ -25,6 +26,7 @@ export const setEvolutionProgressController: RequestHandler = async (req, res) = await inventory.save(); res.end(); + broadcastInventoryUpdate(req); }; type ISetEvolutionProgressRequest = { diff --git a/src/controllers/custom/unlockAllCapturaScenesController.ts b/src/controllers/custom/unlockAllCapturaScenesController.ts index 45dffcf0..a5eb6215 100644 --- a/src/controllers/custom/unlockAllCapturaScenesController.ts +++ b/src/controllers/custom/unlockAllCapturaScenesController.ts @@ -2,19 +2,25 @@ import type { RequestHandler } from "express"; import { ExportResources, ExportVirtuals } from "warframe-public-export-plus"; import { getAccountIdForRequest } from "../../services/loginService.ts"; import { addItem, getInventory } from "../../services/inventoryService.ts"; +import { sendWsBroadcastToGame } from "../../services/wsService.ts"; export const unlockAllCapturaScenesController: RequestHandler = async (req, res) => { const accountId = await getAccountIdForRequest(req); const inventory = await getInventory(accountId); + let needSync = false; for (const uniqueName of Object.keys(ExportResources)) { if (resourceInheritsFrom(uniqueName, "/Lotus/Types/Items/MiscItems/PhotoboothTile")) { await addItem(inventory, uniqueName, 1); + needSync = true; } } await inventory.save(); res.end(); + if (needSync) { + sendWsBroadcastToGame(accountId, { sync_inventory: true }); + } }; const resourceInheritsFrom = (resourceName: string, targetName: string): boolean => { diff --git a/src/controllers/custom/unlockAllIntrinsicsController.ts b/src/controllers/custom/unlockAllIntrinsicsController.ts index 8dd484ce..6e61596f 100644 --- a/src/controllers/custom/unlockAllIntrinsicsController.ts +++ b/src/controllers/custom/unlockAllIntrinsicsController.ts @@ -1,6 +1,7 @@ import { getInventory } from "../../services/inventoryService.ts"; import { getAccountIdForRequest } from "../../services/loginService.ts"; import type { RequestHandler } from "express"; +import { sendWsBroadcastToGame } from "../../services/wsService.ts"; export const unlockAllIntrinsicsController: RequestHandler = async (req, res) => { const accountId = await getAccountIdForRequest(req); @@ -16,4 +17,5 @@ export const unlockAllIntrinsicsController: RequestHandler = async (req, res) => inventory.PlayerSkills.LPS_DRIFT_ENDURANCE = 10; await inventory.save(); res.end(); + sendWsBroadcastToGame(accountId, { sync_inventory: true }); }; diff --git a/src/controllers/custom/unlockAllProfitTakerStagesController.ts b/src/controllers/custom/unlockAllProfitTakerStagesController.ts index 79fe3b87..a46dd23b 100644 --- a/src/controllers/custom/unlockAllProfitTakerStagesController.ts +++ b/src/controllers/custom/unlockAllProfitTakerStagesController.ts @@ -1,6 +1,7 @@ import { getInventory } from "../../services/inventoryService.ts"; import { getAccountIdForRequest } from "../../services/loginService.ts"; import type { RequestHandler } from "express"; +import { sendWsBroadcastToGame } from "../../services/wsService.ts"; const allEudicoHeistJobs = [ "/Lotus/Types/Gameplay/Venus/Jobs/Heists/HeistProfitTakerBountyOne", @@ -21,4 +22,5 @@ export const unlockAllProfitTakerStagesController: RequestHandler = async (req, } await inventory.save(); res.end(); + sendWsBroadcastToGame(accountId, { sync_inventory: true }); }; diff --git a/src/controllers/custom/unlockAllSimarisResearchEntriesController.ts b/src/controllers/custom/unlockAllSimarisResearchEntriesController.ts index fae3c55d..3e013292 100644 --- a/src/controllers/custom/unlockAllSimarisResearchEntriesController.ts +++ b/src/controllers/custom/unlockAllSimarisResearchEntriesController.ts @@ -1,6 +1,7 @@ import type { RequestHandler } from "express"; import { getAccountIdForRequest } from "../../services/loginService.ts"; import { getInventory } from "../../services/inventoryService.ts"; +import { sendWsBroadcastToGame } from "../../services/wsService.ts"; export const unlockAllSimarisResearchEntriesController: RequestHandler = async (req, res) => { const accountId = await getAccountIdForRequest(req); @@ -17,4 +18,5 @@ export const unlockAllSimarisResearchEntriesController: RequestHandler = async ( ].map(type => ({ TargetType: type, Scans: 10, Completed: true })); await inventory.save(); res.end(); + sendWsBroadcastToGame(accountId, { sync_inventory: true }); }; diff --git a/src/controllers/custom/updateFingerprintController.ts b/src/controllers/custom/updateFingerprintController.ts index f600b578..8278744e 100644 --- a/src/controllers/custom/updateFingerprintController.ts +++ b/src/controllers/custom/updateFingerprintController.ts @@ -2,6 +2,7 @@ import { getInventory } from "../../services/inventoryService.ts"; import type { WeaponTypeInternal } from "../../services/itemDataService.ts"; import { getAccountIdForRequest } from "../../services/loginService.ts"; import type { RequestHandler } from "express"; +import { sendWsBroadcastToGame } from "../../services/wsService.ts"; export const updateFingerprintController: RequestHandler = async (req, res) => { const accountId = await getAccountIdForRequest(req); @@ -22,6 +23,7 @@ export const updateFingerprintController: RequestHandler = async (req, res) => { await inventory.save(); } res.end(); + sendWsBroadcastToGame(accountId, { sync_inventory: true }); }; interface IUpdateFingerPrintRequest { diff --git a/src/services/wsService.ts b/src/services/wsService.ts index b80cd81e..0e35d0c2 100644 --- a/src/services/wsService.ts +++ b/src/services/wsService.ts @@ -1,12 +1,13 @@ import type http from "http"; import type https from "https"; -import type { default as ws } from "ws"; +import type { WebSocket } from "ws"; import { WebSocketServer } from "ws"; import { Account } from "../models/loginModel.ts"; import { createAccount, createNonce, getUsernameFromEmail, isCorrectPassword } from "./loginService.ts"; import type { IDatabaseAccountJson } from "../types/loginTypes.ts"; import type { HydratedDocument } from "mongoose"; -import { logError } from "../utils/logger.ts"; +import { logError, logger } from "../utils/logger.ts"; +import type { Request } from "express"; let wsServer: WebSocketServer | undefined; let wssServer: WebSocketServer | undefined; @@ -44,9 +45,10 @@ export const stopWsServers = (promises: Promise[]): void => { let lastWsid: number = 0; -interface IWsCustomData extends ws { +interface IWsCustomData extends WebSocket { id: number; accountId?: string; + isGame?: boolean; } interface IWsMsgFromClient { @@ -55,11 +57,19 @@ interface IWsMsgFromClient { password: string; isRegister: boolean; }; + auth_game?: { + accountId: string; + nonce: number; + }; logout?: boolean; + sync_inventory?: boolean; } interface IWsMsgToClient { - //wsid?: number; + // common + wsid?: number; + + // to webui reload?: boolean; ports?: { http: number | undefined; @@ -77,9 +87,13 @@ interface IWsMsgToClient { nonce_updated?: boolean; update_inventory?: boolean; logged_out?: boolean; + have_game_ws?: boolean; + + // to game + sync_inventory?: boolean; } -const wsOnConnect = (ws: ws, req: http.IncomingMessage): void => { +const wsOnConnect = (ws: WebSocket, req: http.IncomingMessage): void => { if (req.url == "/custom/selftest") { ws.send("SpaceNinjaServer"); ws.close(); @@ -87,11 +101,12 @@ const wsOnConnect = (ws: ws, req: http.IncomingMessage): void => { } (ws as IWsCustomData).id = ++lastWsid; - ws.send(JSON.stringify({ wsid: lastWsid })); + ws.send(JSON.stringify({ wsid: lastWsid } satisfies IWsMsgToClient)); // eslint-disable-next-line @typescript-eslint/no-misused-promises ws.on("message", async msg => { try { + //console.log(String(msg)); const data = JSON.parse(String(msg)) as IWsMsgFromClient; if (data.auth) { let account: IDatabaseAccountJson | null = await Account.findOne({ email: data.auth.email }); @@ -124,7 +139,8 @@ const wsOnConnect = (ws: ws, req: http.IncomingMessage): void => { id: account.id, DisplayName: account.DisplayName, Nonce: account.Nonce - } + }, + have_game_ws: haveGameWs(account.id) } satisfies IWsMsgToClient) ); } else { @@ -137,77 +153,152 @@ const wsOnConnect = (ws: ws, req: http.IncomingMessage): void => { ); } } + if (data.auth_game) { + (ws as IWsCustomData).isGame = true; + if (data.auth_game.nonce) { + const account: IDatabaseAccountJson | null = await Account.findOne({ + _id: data.auth_game.accountId, + Nonce: data.auth_game.nonce + }); + if (account) { + (ws as IWsCustomData).accountId = account.id; + logger.debug(`got bootstrapper connection for ${account.id}`); + sendWsBroadcastToWebui({ have_game_ws: true }, account.id); + } + } + } if (data.logout) { const accountId = (ws as IWsCustomData).accountId; - (ws as IWsCustomData).accountId = undefined; - await Account.updateOne( - { - _id: accountId, - ClientType: "webui" - }, - { - Nonce: 0 - } - ); + if (accountId) { + (ws as IWsCustomData).accountId = undefined; + await Account.updateOne( + { + _id: accountId, + ClientType: "webui" + }, + { + Nonce: 0 + } + ); + } + } + if (data.sync_inventory) { + const accountId = (ws as IWsCustomData).accountId; + if (accountId) { + sendWsBroadcastToGame(accountId, { sync_inventory: true }); + } } } catch (e) { logError(e as Error, `processing websocket message`); } }); + // eslint-disable-next-line @typescript-eslint/no-misused-promises + ws.on("close", async () => { + if ((ws as IWsCustomData).isGame && (ws as IWsCustomData).accountId) { + logger.debug(`lost bootstrapper connection for ${(ws as IWsCustomData).accountId}`); + sendWsBroadcastToWebui({ have_game_ws: false }, (ws as IWsCustomData).accountId); + await Account.updateOne( + { + _id: (ws as IWsCustomData).accountId + }, + { + Dropped: true + } + ); + } + }); +}; + +const forEachClient = (cb: (client: IWsCustomData) => void): void => { + if (wsServer) { + for (const client of wsServer.clients) { + cb(client as IWsCustomData); + } + } + if (wssServer) { + for (const client of wssServer.clients) { + cb(client as IWsCustomData); + } + } +}; + +export const haveGameWs = (accountId: string): boolean => { + let ret = false; + forEachClient(client => { + if (client.isGame && client.accountId == accountId) { + ret = true; + } + }); + return ret; }; export const sendWsBroadcast = (data: IWsMsgToClient): void => { const msg = JSON.stringify(data); - if (wsServer) { - for (const client of wsServer.clients) { - client.send(msg); - } - } - if (wssServer) { - for (const client of wssServer.clients) { - client.send(msg); - } - } + forEachClient(client => { + client.send(msg); + }); }; export const sendWsBroadcastTo = (accountId: string, data: IWsMsgToClient): void => { const msg = JSON.stringify(data); - if (wsServer) { - for (const client of wsServer.clients) { - if ((client as IWsCustomData).accountId == accountId) { - client.send(msg); - } + forEachClient(client => { + if (client.accountId == accountId) { + client.send(msg); } - } - if (wssServer) { - for (const client of wssServer.clients) { - if ((client as IWsCustomData).accountId == accountId) { - client.send(msg); - } + }); +}; + +export const sendWsBroadcastToGame = (accountId: string, data: IWsMsgToClient): void => { + const msg = JSON.stringify(data); + forEachClient(client => { + if (client.isGame && client.accountId == accountId) { + client.send(msg); } - } + }); }; export const sendWsBroadcastEx = (data: IWsMsgToClient, accountId?: string, excludeWsid?: number): void => { const msg = JSON.stringify(data); - if (wsServer) { - for (const client of wsServer.clients) { - if ( - (!accountId || (client as IWsCustomData).accountId == accountId) && - (client as IWsCustomData).id != excludeWsid - ) { - client.send(msg); - } + forEachClient(client => { + if ((!accountId || client.accountId == accountId) && client.id != excludeWsid) { + client.send(msg); } - } - if (wssServer) { - for (const client of wssServer.clients) { - if ( - (!accountId || (client as IWsCustomData).accountId == accountId) && - (client as IWsCustomData).id != excludeWsid - ) { - client.send(msg); - } + }); +}; + +export const sendWsBroadcastToWebui = (data: IWsMsgToClient, accountId?: string, excludeWsid?: number): void => { + const msg = JSON.stringify(data); + forEachClient(client => { + if (!client.isGame && (!accountId || client.accountId == accountId) && client.id != excludeWsid) { + client.send(msg); } + }); +}; + +export const broadcastInventoryUpdate = (req: Request): void => { + const accountId = req.query.accountId as string; + if (req.query.wsid) { + // for webui requests, let other tabs and the game know + sendWsBroadcastEx( + { sync_inventory: true, update_inventory: true }, + accountId, + parseInt(String(req.query.wsid)) + ); + } else { + // for game requests, let all webui tabs know + sendWsBroadcastToWebui({ update_inventory: true }, accountId, parseInt(String(req.query.wsid))); } }; + +export const handleNonceInvalidation = (accountId: string): void => { + forEachClient(client => { + if (client.accountId == accountId) { + if (client.isGame) { + client.accountId = undefined; // prevent processing of the close event + client.close(); + } else { + client.send(JSON.stringify({ nonce_updated: true, have_game_ws: false } satisfies IWsMsgToClient)); + } + } + }); +}; diff --git a/static/webui/index.html b/static/webui/index.html index fb09e919..2ad2751d 100644 --- a/static/webui/index.html +++ b/static/webui/index.html @@ -93,7 +93,7 @@
-

+

-

+

@@ -771,7 +771,7 @@
-

+

diff --git a/static/webui/script.js b/static/webui/script.js index 4497e073..0497ec33 100644 --- a/static/webui/script.js +++ b/static/webui/script.js @@ -63,7 +63,7 @@ function openWebSocket() { } $(".displayname").text(data.DisplayName); window.accountId = data.id; - window.authz = "accountId=" + data.id + "&nonce=" + data.Nonce; + window.authz = "accountId=" + data.id + "&nonce=" + data.Nonce + "&wsid=" + wsid; if (window.dict) { updateLocElements(); } @@ -90,6 +90,14 @@ function openWebSocket() { if ("logged_out" in msg) { logout(); } + if ("have_game_ws" in msg) { + window.have_game_ws = msg.have_game_ws; + if (window.dict) { + $(".inventory-update-note").text( + loc(msg.have_game_ws ? "general_inventoryUpdateNoteGameWs" : "general_inventoryUpdateNote") + ); + } + } }; window.ws.onclose = function () { ws_is_open = false; @@ -223,6 +231,9 @@ function updateLocElements() { document.querySelectorAll("[data-loc-replace]").forEach(elm => { elm.innerHTML = elm.innerHTML.replace("|VAL|", elm.getAttribute("data-loc-replace")); }); + $(".inventory-update-note").text( + loc(window.have_game_ws ? "general_inventoryUpdateNoteGameWs" : "general_inventoryUpdateNote") + ); } function setActiveLanguage(lang) { @@ -2582,7 +2593,7 @@ function disposeOfGear(category, oid) { ]; revalidateAuthz().then(() => { $.post({ - url: "/api/sell.php?" + window.authz + "&wsid=" + wsid, + url: "/api/sell.php?" + window.authz, contentType: "text/plain", data: JSON.stringify(data) }); @@ -2604,7 +2615,7 @@ function disposeOfItems(category, type, count) { ]; revalidateAuthz().then(() => { $.post({ - url: "/api/sell.php?" + window.authz + "&wsid=" + wsid, + url: "/api/sell.php?" + window.authz, contentType: "text/plain", data: JSON.stringify(data) }); @@ -2858,7 +2869,7 @@ for (const id of uiConfigs) { value = parseInt(value); } $.post({ - url: "/custom/setConfig?" + window.authz + "&wsid=" + wsid, + url: "/custom/setConfig?" + window.authz, contentType: "application/json", data: JSON.stringify({ [id]: value }) }); @@ -2866,13 +2877,9 @@ for (const id of uiConfigs) { } else if (elm.type == "checkbox") { elm.onchange = function () { $.post({ - url: "/custom/setConfig?" + window.authz + "&wsid=" + wsid, + url: "/custom/setConfig?" + window.authz, contentType: "application/json", data: JSON.stringify({ [id]: this.checked }) - }).then(() => { - if (["infiniteCredits", "infinitePlatinum", "infiniteEndo", "infiniteRegalAya"].indexOf(id) != -1) { - updateInventory(); - } }); }; } @@ -2893,7 +2900,7 @@ document.querySelectorAll(".config-form .input-group").forEach(grp => { function doSaveConfigInt(id) { $.post({ - url: "/custom/setConfig?" + window.authz + "&wsid=" + wsid, + url: "/custom/setConfig?" + window.authz, contentType: "application/json", data: JSON.stringify({ [id]: parseInt(document.getElementById(id).value) @@ -2903,7 +2910,7 @@ function doSaveConfigInt(id) { function doSaveConfigFloat(id) { $.post({ - url: "/custom/setConfig?" + window.authz + "&wsid=" + wsid, + url: "/custom/setConfig?" + window.authz, contentType: "application/json", data: JSON.stringify({ [id]: parseFloat(document.getElementById(id).value) @@ -2913,7 +2920,7 @@ function doSaveConfigFloat(id) { function doSaveConfigStringArray(id) { $.post({ - url: "/custom/setConfig?" + window.authz + "&wsid=" + wsid, + url: "/custom/setConfig?" + window.authz, contentType: "application/json", data: JSON.stringify({ [id]: document @@ -2997,6 +3004,9 @@ function doUnlockAllFocusSchools() { toast(loc("code_focusAllUnlocked")); } else { toast(loc("code_focusUnlocked").split("|COUNT|").join(Object.keys(missingFocusUpgrades).length)); + if (ws_is_open) { + window.ws.send(JSON.stringify({ sync_inventory: true })); + } } }); }); @@ -3040,7 +3050,7 @@ document.querySelectorAll("#account-cheats input[type=checkbox]").forEach(elm => elm.onchange = function () { revalidateAuthz().then(() => { $.post({ - url: "/custom/setAccountCheat?" + window.authz /*+ "&wsid=" + wsid*/, + url: "/custom/setAccountCheat?" + window.authz, contentType: "application/json", data: JSON.stringify({ key: elm.id, @@ -3112,7 +3122,7 @@ function doRemoveUnrankedMods() { req.done(inventory => { window.itemListPromise.then(itemMap => { $.post({ - url: "/api/sell.php?" + window.authz + "&wsid=" + wsid, + url: "/api/sell.php?" + window.authz, contentType: "text/plain", data: JSON.stringify({ SellCurrency: "SC_RegularCredits", @@ -3469,13 +3479,13 @@ async function doUnlockAllScans() { async function doUnlockAllShipFeatures() { await revalidateAuthz(); await fetch("/custom/unlockAllShipFeatures?" + window.authz); - toast(loc("cheats_unlockSuccInventory")); + toast(loc(window.have_game_ws ? "code_succAdded" : "cheats_unlockSuccInventory")); } async function doUnlockAllCapturaScenes() { await revalidateAuthz(); await fetch("/custom/unlockAllCapturaScenes?" + window.authz); - toast(loc("cheats_unlockSuccInventory")); + toast(loc(window.have_game_ws ? "code_succAdded" : "cheats_unlockSuccInventory")); } async function unlockAllMissions() { @@ -3487,13 +3497,13 @@ async function unlockAllMissions() { async function unlockAllProfitTakerStages() { await revalidateAuthz(); await fetch("/custom/unlockAllProfitTakerStages?" + window.authz); - toast(loc("cheats_unlockSuccInventory")); + toast(loc(window.have_game_ws ? "code_succAdded" : "cheats_unlockSuccInventory")); } async function unlockAllSimarisResearchEntries() { await revalidateAuthz(); await fetch("/custom/unlockAllSimarisResearchEntries?" + window.authz); - toast(loc("cheats_unlockSuccInventory")); + toast(loc(window.have_game_ws ? "code_succAdded" : "cheats_unlockSuccInventory")); } const importSamples = { diff --git a/static/webui/translations/de.js b/static/webui/translations/de.js index cb23eda8..d370142c 100644 --- a/static/webui/translations/de.js +++ b/static/webui/translations/de.js @@ -1,6 +1,7 @@ // German translation by Animan8000 dict = { general_inventoryUpdateNote: `Hinweis: Um Änderungen im Spiel zu sehen, musst du dein Inventar neu synchronisieren, z. B. mit dem /sync Befehl des Bootstrappers, durch Besuch eines Dojo/Relais oder durch erneutes Einloggen.`, + general_inventoryUpdateNoteGameWs: `[UNTRANSLATED] Note: You may need to reopen any menu you are on for changes to be reflected.`, general_addButton: `Hinzufügen`, general_setButton: `Festlegen`, general_none: `Keines`, diff --git a/static/webui/translations/en.js b/static/webui/translations/en.js index d47ebf90..5f82beca 100644 --- a/static/webui/translations/en.js +++ b/static/webui/translations/en.js @@ -1,5 +1,6 @@ dict = { general_inventoryUpdateNote: `Note: To see changes in-game, you need to resync your inventory, e.g. using the bootstrapper's /sync command, visiting a dojo/relay, or relogging.`, + general_inventoryUpdateNoteGameWs: `Note: You may need to reopen any menu you are on for changes to be reflected.`, general_addButton: `Add`, general_setButton: `Set`, general_none: `None`, diff --git a/static/webui/translations/es.js b/static/webui/translations/es.js index 546c019d..337cc27a 100644 --- a/static/webui/translations/es.js +++ b/static/webui/translations/es.js @@ -1,6 +1,7 @@ // Spanish translation by hxedcl dict = { general_inventoryUpdateNote: `Para ver los cambios en el juego, necesitas volver a sincronizar tu inventario, por ejemplo, usando el comando /sync del bootstrapper, visitando un dojo o repetidor, o volviendo a iniciar sesión.`, + general_inventoryUpdateNoteGameWs: `[UNTRANSLATED] Note: You may need to reopen any menu you are on for changes to be reflected.`, general_addButton: `Agregar`, general_setButton: `Establecer`, general_none: `Ninguno`, diff --git a/static/webui/translations/fr.js b/static/webui/translations/fr.js index d2570117..6230c60d 100644 --- a/static/webui/translations/fr.js +++ b/static/webui/translations/fr.js @@ -1,6 +1,7 @@ // French translation by Vitruvio dict = { general_inventoryUpdateNote: `Note : Pour voir les changements en jeu, l'inventaire doit être actualisé. Cela se fait en tapant /sync dans le tchat, en visitant un dojo/relais ou en se reconnectant.`, + general_inventoryUpdateNoteGameWs: `[UNTRANSLATED] Note: You may need to reopen any menu you are on for changes to be reflected.`, general_addButton: `Ajouter`, general_setButton: `Définir`, general_none: `Aucun`, diff --git a/static/webui/translations/ru.js b/static/webui/translations/ru.js index 2b26fd38..c1157079 100644 --- a/static/webui/translations/ru.js +++ b/static/webui/translations/ru.js @@ -1,6 +1,7 @@ // Russian translation by AMelonInsideLemon, LoseFace dict = { general_inventoryUpdateNote: `Примечание: Чтобы увидеть изменения в игре, вам нужно повторно синхронизировать свой инвентарь, например, используя команду /sync загрузчика, посетив Додзё/Реле или перезагрузив игру.`, + general_inventoryUpdateNoteGameWs: `[UNTRANSLATED] Note: You may need to reopen any menu you are on for changes to be reflected.`, general_addButton: `Добавить`, general_setButton: `Установить`, general_none: `Отсутствует`, diff --git a/static/webui/translations/uk.js b/static/webui/translations/uk.js index f223590f..7981eae1 100644 --- a/static/webui/translations/uk.js +++ b/static/webui/translations/uk.js @@ -1,6 +1,7 @@ // Ukrainian translation by LoseFace dict = { general_inventoryUpdateNote: `Пам'ятка: Щоб побачити зміни в грі, вам потрібно повторно синхронізувати своє спорядження, наприклад, використовуючи команду /sync завантажувача, відвідавши Доджьо/Реле або перезавантаживши гру.`, + general_inventoryUpdateNoteGameWs: `[UNTRANSLATED] Note: You may need to reopen any menu you are on for changes to be reflected.`, general_addButton: `Добавити`, general_setButton: `Встановити`, general_none: `Відсутній`, diff --git a/static/webui/translations/zh.js b/static/webui/translations/zh.js index 24e65f82..4b806799 100644 --- a/static/webui/translations/zh.js +++ b/static/webui/translations/zh.js @@ -1,6 +1,7 @@ // Chinese translation by meb154, bishan178, nyaoouo, qianlishun, CrazyZhang, Corvus, & qingchun dict = { general_inventoryUpdateNote: `注意: 要在游戏中查看更改,您需要重新同步库存,例如使用客户端的 /sync 命令,访问道场/中继站或重新登录.`, + general_inventoryUpdateNoteGameWs: `[UNTRANSLATED] Note: You may need to reopen any menu you are on for changes to be reflected.`, general_addButton: `添加`, general_setButton: `设置`, general_none: `无`,