feat: initial foundry for U8 (#3014)
Some checks failed
Build Docker image / docker (push) Has been cancelled
Build / build (push) Has been cancelled

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.

Reviewed-on: #3014
Reviewed-by: Sainan <63328889+sainan@users.noreply.github.com>
Co-authored-by: AMelonInsideLemon <166175391+AMelonInsideLemon@users.noreply.github.com>
Co-committed-by: AMelonInsideLemon <166175391+AMelonInsideLemon@users.noreply.github.com>
This commit was merged in pull request #3014.
This commit is contained in:
2025-11-10 23:50:51 -08:00
committed by Sainan
parent f63954c796
commit 48b7138c1c
4 changed files with 82 additions and 22 deletions

View File

@@ -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;
}[];
}

View File

@@ -38,22 +38,47 @@ interface IClaimCompletedRecipeResponse {
}
export const claimCompletedRecipeController: RequestHandler = async (req, res) => {
const claimCompletedRecipeRequest = getJSONfromString<IClaimCompletedRecipeRequest>(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<IClaimCompletedRecipeRequest>(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);

View File

@@ -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) {
@@ -68,7 +69,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}`
);
}
}
}

View File

@@ -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);