From 44f77ff9290ee02e5455c794c5bf7eb1f6485eae Mon Sep 17 00:00:00 2001 From: Sainan Date: Fri, 14 Mar 2025 15:10:42 +0100 Subject: [PATCH 01/10] improve guildTech request types --- src/controllers/api/guildTechController.ts | 50 ++++++++++------------ 1 file changed, 23 insertions(+), 27 deletions(-) diff --git a/src/controllers/api/guildTechController.ts b/src/controllers/api/guildTechController.ts index c51628af..e30539ec 100644 --- a/src/controllers/api/guildTechController.ts +++ b/src/controllers/api/guildTechController.ts @@ -28,8 +28,7 @@ export const guildTechController: RequestHandler = async (req, res) => { const inventory = await getInventory(accountId); const guild = await getGuildForRequestEx(req, inventory); const data = JSON.parse(String(req.body)) as TGuildTechRequest; - const action = data.Action.split(",")[0]; - if (action == "Sync") { + if (data.Action == "Sync") { let needSave = false; const techProjects: ITechProjectClient[] = []; if (guild.TechProjects) { @@ -53,18 +52,18 @@ export const guildTechController: RequestHandler = async (req, res) => { await guild.save(); } res.json({ TechProjects: techProjects }); - } else if (action == "Start") { + } else if (data.Action == "Start") { if (!hasAccessToDojo(inventory) || !(await hasGuildPermission(guild, accountId, GuildPermission.Fabricator))) { res.status(400).send("-1").end(); return; } - const recipe = ExportDojoRecipes.research[data.RecipeType!]; + const recipe = ExportDojoRecipes.research[data.RecipeType]; guild.TechProjects ??= []; if (!guild.TechProjects.find(x => x.ItemType == data.RecipeType)) { const techProject = guild.TechProjects[ guild.TechProjects.push({ - ItemType: data.RecipeType!, + ItemType: data.RecipeType, ReqCredits: config.noDojoResearchCosts ? 0 : scaleRequiredCount(recipe.price), ReqItems: recipe.ingredients.map(x => ({ ItemType: x.ItemType, @@ -80,12 +79,12 @@ export const guildTechController: RequestHandler = async (req, res) => { } await guild.save(); res.end(); - } else if (action == "Contribute") { + } else if (data.Action == "Contribute") { if (!hasAccessToDojo(inventory)) { res.status(400).send("-1").end(); return; } - const contributions = data as IGuildTechContributeFields; + const contributions = data; const techProject = guild.TechProjects!.find(x => x.ItemType == contributions.RecipeType)!; if (contributions.VaultCredits) { @@ -136,7 +135,7 @@ export const guildTechController: RequestHandler = async (req, res) => { if (techProject.ReqCredits == 0 && !techProject.ReqItems.find(x => x.ItemCount > 0)) { // This research is now fully funded. - const recipe = ExportDojoRecipes.research[data.RecipeType!]; + const recipe = ExportDojoRecipes.research[data.RecipeType]; processFundedProject(guild, techProject, recipe); } @@ -146,12 +145,12 @@ export const guildTechController: RequestHandler = async (req, res) => { InventoryChanges: inventoryChanges, Vault: getGuildVault(guild) }); - } else if (action == "Buy") { + } else if (data.Action.split(",")[0] == "Buy") { if (!hasAccessToDojo(inventory) || !(await hasGuildPermission(guild, accountId, GuildPermission.Fabricator))) { res.status(400).send("-1").end(); return; } - const purchase = data as IGuildTechBuyFields; + const purchase = data as IGuildTechBuyRequest; const quantity = parseInt(data.Action.split(",")[1]); const recipeChanges = [ { @@ -173,13 +172,12 @@ export const guildTechController: RequestHandler = async (req, res) => { Recipes: recipeChanges } }); - } else if (action == "Fabricate") { + } else if (data.Action == "Fabricate") { if (!hasAccessToDojo(inventory) || !(await hasGuildPermission(guild, accountId, GuildPermission.Fabricator))) { res.status(400).send("-1").end(); return; } - const payload = data as IGuildTechFabricateRequest; - const recipe = ExportDojoRecipes.fabrications[payload.RecipeType]; + const recipe = ExportDojoRecipes.fabrications[data.RecipeType]; const inventoryChanges: IInventoryChanges = updateCurrency(inventory, recipe.price, false); inventoryChanges.MiscItems = recipe.ingredients.map(x => ({ ItemType: x.ItemType, @@ -233,20 +231,24 @@ const setTechLogState = ( }; type TGuildTechRequest = - | ({ - Action: string; - } & Partial & - Partial) - | IGuildTechFabricateRequest; + | { Action: "Sync" | "SomethingElseThatWeMightNotKnowAbout" } + | IGuildTechBasicRequest + | IGuildTechContributeRequest; -interface IGuildTechStartFields { +interface IGuildTechBasicRequest { + Action: "Start" | "Fabricate"; Mode: "Guild"; RecipeType: string; } -type IGuildTechBuyFields = IGuildTechStartFields; +interface IGuildTechBuyRequest { + Action: string; + Mode: "Guild"; + RecipeType: string; +} -interface IGuildTechContributeFields { +interface IGuildTechContributeRequest { + Action: "Contribute"; ResearchId: ""; RecipeType: string; RegularCredits: number; @@ -254,9 +256,3 @@ interface IGuildTechContributeFields { VaultCredits: number; VaultMiscItems: IMiscItem[]; } - -interface IGuildTechFabricateRequest { - Action: "Fabricate"; - Mode: "Guild"; - RecipeType: string; -} -- 2.47.2 From 7e78f8b7587a072577536ff4f80b19afe94f1583 Mon Sep 17 00:00:00 2001 From: Sainan Date: Fri, 14 Mar 2025 15:13:09 +0100 Subject: [PATCH 02/10] feat: pause research --- src/controllers/api/guildTechController.ts | 9 ++++++++- src/services/guildService.ts | 14 ++++++++++++++ 2 files changed, 22 insertions(+), 1 deletion(-) diff --git a/src/controllers/api/guildTechController.ts b/src/controllers/api/guildTechController.ts index e30539ec..db7b2f80 100644 --- a/src/controllers/api/guildTechController.ts +++ b/src/controllers/api/guildTechController.ts @@ -4,6 +4,7 @@ import { getGuildVault, hasAccessToDojo, hasGuildPermission, + removePigmentsFromGuildMembers, scaleRequiredCount } from "@/src/services/guildService"; import { ExportDojoRecipes, IDojoResearch } from "warframe-public-export-plus"; @@ -188,6 +189,12 @@ export const guildTechController: RequestHandler = async (req, res) => { await inventory.save(); // Not a mistake: This response uses `inventoryChanges` instead of `InventoryChanges`. res.json({ inventoryChanges: inventoryChanges }); + } else if (data.Action == "Pause") { + const project = guild.TechProjects!.find(x => x.ItemType == data.RecipeType)!; + project.State = -2; + await guild.save(); + await removePigmentsFromGuildMembers(guild._id); + res.end(); } else { throw new Error(`unknown guildTech action: ${data.Action}`); } @@ -236,7 +243,7 @@ type TGuildTechRequest = | IGuildTechContributeRequest; interface IGuildTechBasicRequest { - Action: "Start" | "Fabricate"; + Action: "Start" | "Fabricate" | "Pause"; Mode: "Guild"; RecipeType: string; } diff --git a/src/services/guildService.ts b/src/services/guildService.ts index 8a7e0220..4af38962 100644 --- a/src/services/guildService.ts +++ b/src/services/guildService.ts @@ -349,3 +349,17 @@ export const hasGuildPermissionEx = ( const rank = guild.Ranks[member.rank]; return (rank.Permissions & perm) != 0; }; + +export const removePigmentsFromGuildMembers = async (guildId: string | Types.ObjectId): Promise => { + const members = await GuildMember.find({ guildId }, "accountId"); + for (const member of members) { + const inventory = await getInventory(member.accountId.toString(), "MiscItems"); + const index = inventory.MiscItems.findIndex( + x => x.ItemType == "/Lotus/Types/Items/Research/DojoColors/GenericDojoColorPigment" + ); + if (index != -1) { + inventory.MiscItems.splice(index, 1); + await inventory.save(); + } + } +}; -- 2.47.2 From 2572b665a6686a43c8d00db94133a96bc2c641b4 Mon Sep 17 00:00:00 2001 From: Sainan Date: Fri, 14 Mar 2025 15:14:37 +0100 Subject: [PATCH 03/10] feat: unpause research --- src/controllers/api/guildTechController.ts | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/controllers/api/guildTechController.ts b/src/controllers/api/guildTechController.ts index db7b2f80..ce1a9a56 100644 --- a/src/controllers/api/guildTechController.ts +++ b/src/controllers/api/guildTechController.ts @@ -195,6 +195,11 @@ export const guildTechController: RequestHandler = async (req, res) => { await guild.save(); await removePigmentsFromGuildMembers(guild._id); res.end(); + } else if (data.Action == "Unpause") { + const project = guild.TechProjects!.find(x => x.ItemType == data.RecipeType)!; + project.State = 0; + await guild.save(); + res.end(); } else { throw new Error(`unknown guildTech action: ${data.Action}`); } @@ -243,7 +248,7 @@ type TGuildTechRequest = | IGuildTechContributeRequest; interface IGuildTechBasicRequest { - Action: "Start" | "Fabricate" | "Pause"; + Action: "Start" | "Fabricate" | "Pause" | "Unpause"; Mode: "Guild"; RecipeType: string; } -- 2.47.2 From 0798eb2c561dba6d3342b117ae47187381a58e9f Mon Sep 17 00:00:00 2001 From: Sainan Date: Fri, 14 Mar 2025 15:22:39 +0100 Subject: [PATCH 04/10] manage ActiveDojoColorResearch --- src/controllers/api/guildTechController.ts | 5 +++++ src/models/guildModel.ts | 1 + src/services/guildService.ts | 1 + src/types/guildTypes.ts | 2 ++ 4 files changed, 9 insertions(+) diff --git a/src/controllers/api/guildTechController.ts b/src/controllers/api/guildTechController.ts index ce1a9a56..fc4c1d99 100644 --- a/src/controllers/api/guildTechController.ts +++ b/src/controllers/api/guildTechController.ts @@ -78,6 +78,9 @@ export const guildTechController: RequestHandler = async (req, res) => { processFundedProject(guild, techProject, recipe); } } + if (data.RecipeType.substring(0, 39) == "/Lotus/Types/Items/Research/DojoColors/") { + guild.ActiveDojoColorResearch = data.RecipeType; + } await guild.save(); res.end(); } else if (data.Action == "Contribute") { @@ -192,12 +195,14 @@ export const guildTechController: RequestHandler = async (req, res) => { } else if (data.Action == "Pause") { const project = guild.TechProjects!.find(x => x.ItemType == data.RecipeType)!; project.State = -2; + guild.ActiveDojoColorResearch = ""; await guild.save(); await removePigmentsFromGuildMembers(guild._id); res.end(); } else if (data.Action == "Unpause") { const project = guild.TechProjects!.find(x => x.ItemType == data.RecipeType)!; project.State = 0; + guild.ActiveDojoColorResearch = data.RecipeType; await guild.save(); res.end(); } else { diff --git a/src/models/guildModel.ts b/src/models/guildModel.ts index b8eb97af..dd035a64 100644 --- a/src/models/guildModel.ts +++ b/src/models/guildModel.ts @@ -151,6 +151,7 @@ const guildSchema = new Schema( VaultShipDecorations: { type: [typeCountSchema], default: undefined }, VaultFusionTreasures: { type: [fusionTreasuresSchema], default: undefined }, TechProjects: { type: [techProjectSchema], default: undefined }, + ActiveDojoColorResearch: { type: String, default: "" }, Class: { type: Number, default: 0 }, XP: { type: Number, default: 0 }, ClaimedXP: { type: [String], default: undefined }, diff --git a/src/services/guildService.ts b/src/services/guildService.ts index 4af38962..142d8514 100644 --- a/src/services/guildService.ts +++ b/src/services/guildService.ts @@ -92,6 +92,7 @@ export const getGuildClient = async (guild: TGuildDatabaseDocument, accountId: s Ranks: guild.Ranks, Tier: 1, Vault: getGuildVault(guild), + ActiveDojoColorResearch: guild.ActiveDojoColorResearch, Class: guild.Class, XP: guild.XP, IsContributor: !!guild.CeremonyContributors?.find(x => x.equals(accountId)), diff --git a/src/types/guildTypes.ts b/src/types/guildTypes.ts index 811f1813..b1ff5efb 100644 --- a/src/types/guildTypes.ts +++ b/src/types/guildTypes.ts @@ -14,6 +14,7 @@ export interface IGuildClient { }[]; Tier: number; Vault: IGuildVault; + ActiveDojoColorResearch: string; Class: number; XP: number; IsContributor: boolean; @@ -39,6 +40,7 @@ export interface IGuildDatabase { VaultFusionTreasures?: IFusionTreasure[]; TechProjects?: ITechProjectDatabase[]; + ActiveDojoColorResearch: string; Class: number; XP: number; -- 2.47.2 From fecae991d3dbf0e9f99c7a4e292d7cee113ee5d9 Mon Sep 17 00:00:00 2001 From: Sainan Date: Fri, 14 Mar 2025 15:23:29 +0100 Subject: [PATCH 05/10] refresh log entry dateTime when research is unpaused --- src/controllers/api/guildTechController.ts | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/controllers/api/guildTechController.ts b/src/controllers/api/guildTechController.ts index fc4c1d99..3d27c554 100644 --- a/src/controllers/api/guildTechController.ts +++ b/src/controllers/api/guildTechController.ts @@ -203,6 +203,10 @@ export const guildTechController: RequestHandler = async (req, res) => { const project = guild.TechProjects!.find(x => x.ItemType == data.RecipeType)!; project.State = 0; guild.ActiveDojoColorResearch = data.RecipeType; + const entry = guild.TechChanges?.find(x => x.details == data.RecipeType); + if (entry) { + entry.dateTime = new Date(); + } await guild.save(); res.end(); } else { -- 2.47.2 From a7f983231004dc1b2ed9662db410cdd60f5e5d04 Mon Sep 17 00:00:00 2001 From: Sainan Date: Fri, 14 Mar 2025 15:25:49 +0100 Subject: [PATCH 06/10] fix permission check for starting research --- src/controllers/api/guildTechController.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/controllers/api/guildTechController.ts b/src/controllers/api/guildTechController.ts index 3d27c554..c6a142ee 100644 --- a/src/controllers/api/guildTechController.ts +++ b/src/controllers/api/guildTechController.ts @@ -54,7 +54,7 @@ export const guildTechController: RequestHandler = async (req, res) => { } res.json({ TechProjects: techProjects }); } else if (data.Action == "Start") { - if (!hasAccessToDojo(inventory) || !(await hasGuildPermission(guild, accountId, GuildPermission.Fabricator))) { + if (!hasAccessToDojo(inventory) || !(await hasGuildPermission(guild, accountId, GuildPermission.Tech))) { res.status(400).send("-1").end(); return; } -- 2.47.2 From e73c6e40ebda7d2b69c6de15b1d99b715ac69aac Mon Sep 17 00:00:00 2001 From: Sainan Date: Fri, 14 Mar 2025 15:26:01 +0100 Subject: [PATCH 07/10] check permission for pausing & unpausing research --- src/controllers/api/guildTechController.ts | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/controllers/api/guildTechController.ts b/src/controllers/api/guildTechController.ts index c6a142ee..3c1835a8 100644 --- a/src/controllers/api/guildTechController.ts +++ b/src/controllers/api/guildTechController.ts @@ -193,6 +193,10 @@ export const guildTechController: RequestHandler = async (req, res) => { // Not a mistake: This response uses `inventoryChanges` instead of `InventoryChanges`. res.json({ inventoryChanges: inventoryChanges }); } else if (data.Action == "Pause") { + if (!hasAccessToDojo(inventory) || !(await hasGuildPermission(guild, accountId, GuildPermission.Tech))) { + res.status(400).send("-1").end(); + return; + } const project = guild.TechProjects!.find(x => x.ItemType == data.RecipeType)!; project.State = -2; guild.ActiveDojoColorResearch = ""; @@ -200,6 +204,10 @@ export const guildTechController: RequestHandler = async (req, res) => { await removePigmentsFromGuildMembers(guild._id); res.end(); } else if (data.Action == "Unpause") { + if (!hasAccessToDojo(inventory) || !(await hasGuildPermission(guild, accountId, GuildPermission.Tech))) { + res.status(400).send("-1").end(); + return; + } const project = guild.TechProjects!.find(x => x.ItemType == data.RecipeType)!; project.State = 0; guild.ActiveDojoColorResearch = data.RecipeType; -- 2.47.2 From 6f1e3c469f095a05243159932b80e63371811068 Mon Sep 17 00:00:00 2001 From: Sainan Date: Fri, 14 Mar 2025 15:34:54 +0100 Subject: [PATCH 08/10] don't set ActiveDojoColorResearch if noDojoResearchCosts is enabled --- src/controllers/api/guildTechController.ts | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/controllers/api/guildTechController.ts b/src/controllers/api/guildTechController.ts index 3c1835a8..8cca0cfc 100644 --- a/src/controllers/api/guildTechController.ts +++ b/src/controllers/api/guildTechController.ts @@ -76,11 +76,12 @@ export const guildTechController: RequestHandler = async (req, res) => { setTechLogState(guild, techProject.ItemType, 5); if (config.noDojoResearchCosts) { processFundedProject(guild, techProject, recipe); + } else { + if (data.RecipeType.substring(0, 39) == "/Lotus/Types/Items/Research/DojoColors/") { + guild.ActiveDojoColorResearch = data.RecipeType; + } } } - if (data.RecipeType.substring(0, 39) == "/Lotus/Types/Items/Research/DojoColors/") { - guild.ActiveDojoColorResearch = data.RecipeType; - } await guild.save(); res.end(); } else if (data.Action == "Contribute") { -- 2.47.2 From 9c3db63c14954a3273f9443365ace89c96bfdbbc Mon Sep 17 00:00:00 2001 From: Sainan Date: Fri, 14 Mar 2025 15:53:40 +0100 Subject: [PATCH 09/10] handle color research being fully funded --- src/controllers/api/guildTechController.ts | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/controllers/api/guildTechController.ts b/src/controllers/api/guildTechController.ts index 8cca0cfc..f9a2e9fb 100644 --- a/src/controllers/api/guildTechController.ts +++ b/src/controllers/api/guildTechController.ts @@ -142,6 +142,10 @@ export const guildTechController: RequestHandler = async (req, res) => { // This research is now fully funded. const recipe = ExportDojoRecipes.research[data.RecipeType]; processFundedProject(guild, techProject, recipe); + if (data.RecipeType.substring(0, 39) == "/Lotus/Types/Items/Research/DojoColors/") { + guild.ActiveDojoColorResearch = ""; + await removePigmentsFromGuildMembers(guild._id); + } } await guild.save(); -- 2.47.2 From cf7d0c3bbf0e9361dcd728059eabce8571403bc7 Mon Sep 17 00:00:00 2001 From: Sainan Date: Fri, 14 Mar 2025 17:57:47 +0100 Subject: [PATCH 10/10] only remove pigments from confirmed guild members --- src/services/guildService.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/services/guildService.ts b/src/services/guildService.ts index 142d8514..67f52c77 100644 --- a/src/services/guildService.ts +++ b/src/services/guildService.ts @@ -352,7 +352,7 @@ export const hasGuildPermissionEx = ( }; export const removePigmentsFromGuildMembers = async (guildId: string | Types.ObjectId): Promise => { - const members = await GuildMember.find({ guildId }, "accountId"); + const members = await GuildMember.find({ guildId, status: 0 }, "accountId"); for (const member of members) { const inventory = await getInventory(member.accountId.toString(), "MiscItems"); const index = inventory.MiscItems.findIndex( -- 2.47.2