From 79c2431e37d793d0592fe0951296c900bd258f5d Mon Sep 17 00:00:00 2001 From: AMelonInsideLemon <166175391+AMelonInsideLemon@users.noreply.github.com> Date: Mon, 10 Nov 2025 08:56:59 +0100 Subject: [PATCH] feat: initial foundry for U8 endpoints should work, but we don't have data for required recipe items for U8, so in most cases, an unknown error will occur in the game, and a more detailed error will occur in the server console. --- .../api/checkPendingRecipesController.ts | 24 +++++++ .../api/claimCompletedRecipeController.ts | 63 +++++++++++++------ src/controllers/api/startRecipeController.ts | 14 ++++- src/routes/api.ts | 3 + 4 files changed, 82 insertions(+), 22 deletions(-) create mode 100644 src/controllers/api/checkPendingRecipesController.ts diff --git a/src/controllers/api/checkPendingRecipesController.ts b/src/controllers/api/checkPendingRecipesController.ts new file mode 100644 index 00000000..4c5724d5 --- /dev/null +++ b/src/controllers/api/checkPendingRecipesController.ts @@ -0,0 +1,24 @@ +import { getAccountIdForRequest } from "../../services/loginService.ts"; +import type { RequestHandler } from "express"; +import { getInventory } from "../../services/inventoryService.ts"; + +export const checkPendingRecipesController: RequestHandler = async (req, res) => { + const accountId = await getAccountIdForRequest(req); + const inventory = await getInventory(accountId, "PendingRecipes"); + const now = Date.now(); + const resp: ICheckPendingRecipesResponse = { + PendingRecipes: inventory.PendingRecipes.map(recipe => ({ + ItemType: recipe.ItemType, + SecondsRemaining: Math.max(0, Math.floor((recipe.CompletionDate.getTime() - now) / 1000)) + })) + }; + + res.send(resp); +}; + +interface ICheckPendingRecipesResponse { + PendingRecipes: { + ItemType: string; + SecondsRemaining: number; + }[]; +} diff --git a/src/controllers/api/claimCompletedRecipeController.ts b/src/controllers/api/claimCompletedRecipeController.ts index 39782101..40966270 100644 --- a/src/controllers/api/claimCompletedRecipeController.ts +++ b/src/controllers/api/claimCompletedRecipeController.ts @@ -38,22 +38,47 @@ interface IClaimCompletedRecipeResponse { } export const claimCompletedRecipeController: RequestHandler = async (req, res) => { - const claimCompletedRecipeRequest = getJSONfromString(String(req.body)); const account = await getAccountForRequest(req); const inventory = await getInventory(account._id.toString()); const resp: IClaimCompletedRecipeResponse = { InventoryChanges: {} }; - for (const recipeId of claimCompletedRecipeRequest.RecipeIds) { - const pendingRecipe = inventory.PendingRecipes.id(fromOid(recipeId)); - if (!pendingRecipe) { - throw new Error(`no pending recipe found with id ${fromOid(recipeId)}`); - } + if (!req.query.recipeName) { + const claimCompletedRecipeRequest = getJSONfromString(String(req.body)); + for (const recipeId of claimCompletedRecipeRequest.RecipeIds) { + const pendingRecipe = inventory.PendingRecipes.id(fromOid(recipeId)); + if (!pendingRecipe) { + throw new Error(`no pending recipe found with id ${fromOid(recipeId)}`); + } - //check recipe is indeed ready to be completed - // if (pendingRecipe.CompletionDate > new Date()) { - // throw new Error(`recipe ${pendingRecipe._id} is not ready to be completed`); - // } + //check recipe is indeed ready to be completed + // if (pendingRecipe.CompletionDate > new Date()) { + // throw new Error(`recipe ${pendingRecipe._id} is not ready to be completed`); + // } + + inventory.PendingRecipes.pull(pendingRecipe._id); + + const recipe = getRecipe(pendingRecipe.ItemType); + if (!recipe) { + throw new Error(`no completed item found for recipe ${pendingRecipe._id.toString()}`); + } + + if (req.query.cancel) { + const inventoryChanges: IInventoryChanges = {}; + await refundRecipeIngredients(inventory, inventoryChanges, recipe, pendingRecipe); + await inventory.save(); + res.json(inventoryChanges); // Not a bug: In the specific case of cancelling a recipe, InventoryChanges are expected to be the root. + return; + } + + await claimCompletedRecipe(account, inventory, recipe, pendingRecipe, resp, req.query.rush); + } + } else { + const recipeName = String(req.query.recipeName); // U8 + const pendingRecipe = inventory.PendingRecipes.find(r => r.ItemType == recipeName); + if (!pendingRecipe) { + throw new Error(`no pending recipe found with ItemType ${recipeName}`); + } inventory.PendingRecipes.pull(pendingRecipe._id); @@ -61,16 +86,14 @@ export const claimCompletedRecipeController: RequestHandler = async (req, res) = if (!recipe) { throw new Error(`no completed item found for recipe ${pendingRecipe._id.toString()}`); } - - if (req.query.cancel) { - const inventoryChanges: IInventoryChanges = {}; - await refundRecipeIngredients(inventory, inventoryChanges, recipe, pendingRecipe); - await inventory.save(); - res.json(inventoryChanges); // Not a bug: In the specific case of cancelling a recipe, InventoryChanges are expected to be the root. - return; - } - - await claimCompletedRecipe(account, inventory, recipe, pendingRecipe, resp, req.query.rush); + await claimCompletedRecipe( + account, + inventory, + recipe, + pendingRecipe, + resp, + req.path.includes("instantCompleteRecipe.php") + ); } await inventory.save(); res.json(resp); diff --git a/src/controllers/api/startRecipeController.ts b/src/controllers/api/startRecipeController.ts index 90c0bdda..d21c8513 100644 --- a/src/controllers/api/startRecipeController.ts +++ b/src/controllers/api/startRecipeController.ts @@ -24,7 +24,8 @@ export const startRecipeController: RequestHandler = async (req, res) => { const accountId = await getAccountIdForRequest(req); - const recipeName = startRecipeRequest.RecipeName; + let recipeName = startRecipeRequest.RecipeName; + if (req.query.recipeName) recipeName = String(req.query.recipeName); // U8 const recipe = getRecipe(recipeName); if (!recipe) { @@ -65,7 +66,16 @@ export const startRecipeController: RequestHandler = async (req, res) => { freeUpSlot(inventory, InventorySlot.WEAPONS); } } else { - await addItem(inventory, recipe.ingredients[i].ItemType, recipe.ingredients[i].ItemCount * -1); + const itemType = recipe.ingredients[i].ItemType; + const itemCount = recipe.ingredients[i].ItemCount; + const inventoryItem = inventory.MiscItems.find(i => i.ItemType === itemType); + if (inventoryItem && inventoryItem.ItemCount >= itemCount) { + await addItem(inventory, itemType, itemCount * -1); + } else { + throw new Error( + `insufficient ${itemType} (in inventory ${inventoryItem?.ItemCount} - needed ${itemCount}) for recipe ${recipeName}` + ); + } } } diff --git a/src/routes/api.ts b/src/routes/api.ts index ee1f18fa..ec6f3aa7 100644 --- a/src/routes/api.ts +++ b/src/routes/api.ts @@ -20,6 +20,7 @@ import { cancelGuildAdvertisementController } from "../controllers/api/cancelGui import { changeDojoRootController } from "../controllers/api/changeDojoRootController.ts"; import { changeGuildRankController } from "../controllers/api/changeGuildRankController.ts"; import { checkDailyMissionBonusController } from "../controllers/api/checkDailyMissionBonusController.ts"; +import { checkPendingRecipesController } from "../controllers/api/checkPendingRecipesController.ts"; import { claimCompletedRecipeController } from "../controllers/api/claimCompletedRecipeController.ts"; import { claimJunctionChallengeRewardController } from "../controllers/api/claimJunctionChallengeRewardController.ts"; import { claimLibraryDailyTaskRewardController } from "../controllers/api/claimLibraryDailyTaskRewardController.ts"; @@ -184,6 +185,7 @@ apiRouter.get("/cancelGuildAdvertisement.php", cancelGuildAdvertisementControlle apiRouter.get("/changeDojoRoot.php", changeDojoRootController); apiRouter.get("/changeGuildRank.php", changeGuildRankController); apiRouter.get("/checkDailyMissionBonus.php", checkDailyMissionBonusController); +apiRouter.get("/checkPendingRecipes.php", checkPendingRecipesController); // U8 apiRouter.get("/claimLibraryDailyTaskReward.php", claimLibraryDailyTaskRewardController); apiRouter.get("/completeCalendarEvent.php", completeCalendarEventController); apiRouter.get("/confirmAllianceInvitation.php", confirmAllianceInvitationController); @@ -305,6 +307,7 @@ apiRouter.post("/guildTech.php", guildTechController); apiRouter.post("/hostSession.php", hostSessionController); apiRouter.post("/hubBlessing.php", hubBlessingController); apiRouter.post("/infestedFoundry.php", infestedFoundryController); +apiRouter.post("/instantCompleteRecipe.php", claimCompletedRecipeController); // U8 apiRouter.post("/inventorySlots.php", inventorySlotsController); apiRouter.post("/joinSession.php", joinSessionController); apiRouter.post("/login.php", loginController); -- 2.49.1