From 0c0677698510b5d26b47d37552a9ac00bbe975a0 Mon Sep 17 00:00:00 2001 From: Sainan Date: Fri, 14 Mar 2025 02:07:08 -0700 Subject: [PATCH] feat: track RoomChanges in clan log (#1174) Final part for clan log; closes #1152 Reviewed-on: https://onlyg.it/OpenWF/SpaceNinjaServer/pulls/1174 --- .../contributeToDojoComponentController.ts | 7 +++- .../api/dojoComponentRushController.ts | 5 +++ src/controllers/api/getGuildLogController.ts | 7 ++++ .../api/startDojoRecipeController.ts | 20 +++++++++-- src/models/guildModel.ts | 15 +++++++- src/services/guildService.ts | 36 +++++++++++++++++-- src/types/guildTypes.ts | 6 ++++ 7 files changed, 89 insertions(+), 7 deletions(-) diff --git a/src/controllers/api/contributeToDojoComponentController.ts b/src/controllers/api/contributeToDojoComponentController.ts index 21be9c825..6f13ab541 100644 --- a/src/controllers/api/contributeToDojoComponentController.ts +++ b/src/controllers/api/contributeToDojoComponentController.ts @@ -4,7 +4,8 @@ import { getDojoClient, getGuildForRequestEx, processDojoBuildMaterialsGathered, - scaleRequiredCount + scaleRequiredCount, + setDojoRoomLogFunded } from "@/src/services/guildService"; import { addMiscItems, getInventory, updateCurrency } from "@/src/services/inventoryService"; import { getAccountIdForRequest } from "@/src/services/loginService"; @@ -40,6 +41,10 @@ export const contributeToDojoComponentController: RequestHandler = async (req, r } const meta = Object.values(ExportDojoRecipes.rooms).find(x => x.resultType == component.pf)!; processContribution(guild, request, inventory, inventoryChanges, meta, component); + // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition + if (component.CompletionTime) { + setDojoRoomLogFunded(guild, component); + } } else { // Room is past "Collecting Materials" if (request.DecoId) { diff --git a/src/controllers/api/dojoComponentRushController.ts b/src/controllers/api/dojoComponentRushController.ts index e72531f43..934b8d983 100644 --- a/src/controllers/api/dojoComponentRushController.ts +++ b/src/controllers/api/dojoComponentRushController.ts @@ -35,6 +35,11 @@ export const dojoComponentRushController: RequestHandler = async (req, res) => { } else { const meta = Object.values(ExportDojoRecipes.rooms).find(x => x.resultType == component.pf)!; processContribution(component, meta, platinumDonated); + + const entry = guild.RoomChanges?.find(x => x.componentId.equals(component._id)); + if (entry) { + entry.dateTime = component.CompletionTime!; + } } await guild.save(); diff --git a/src/controllers/api/getGuildLogController.ts b/src/controllers/api/getGuildLogController.ts index 9d4b82c5b..47c94b631 100644 --- a/src/controllers/api/getGuildLogController.ts +++ b/src/controllers/api/getGuildLogController.ts @@ -18,6 +18,13 @@ export const getGuildLogController: RequestHandler = async (req, res) => { StandingsUpdates: [], ClassChanges: [] }; + guild.RoomChanges?.forEach(entry => { + log.RoomChanges.push({ + dateTime: toMongoDate(entry.dateTime), + entryType: entry.entryType, + details: entry.details + }); + }); guild.TechChanges?.forEach(entry => { log.TechChanges.push({ dateTime: toMongoDate(entry.dateTime), diff --git a/src/controllers/api/startDojoRecipeController.ts b/src/controllers/api/startDojoRecipeController.ts index ee7bb2029..d492c09a0 100644 --- a/src/controllers/api/startDojoRecipeController.ts +++ b/src/controllers/api/startDojoRecipeController.ts @@ -1,6 +1,11 @@ import { RequestHandler } from "express"; import { IDojoComponentClient } from "@/src/types/guildTypes"; -import { getDojoClient, getGuildForRequest, processDojoBuildMaterialsGathered } from "@/src/services/guildService"; +import { + getDojoClient, + getGuildForRequest, + processDojoBuildMaterialsGathered, + setDojoRoomLogFunded +} from "@/src/services/guildService"; import { Types } from "mongoose"; import { ExportDojoRecipes } from "warframe-public-export-plus"; import { config } from "@/src/services/configService"; @@ -21,10 +26,20 @@ export const startDojoRecipeController: RequestHandler = async (req, res) => { guild.DojoEnergy += room.energy; } + const componentId = new Types.ObjectId(); + + guild.RoomChanges ??= []; + guild.RoomChanges.push({ + dateTime: new Date(), + entryType: 2, + details: request.PlacedComponent.pf, + componentId: componentId + }); + const component = guild.DojoComponents[ guild.DojoComponents.push({ - _id: new Types.ObjectId(), + _id: componentId, pf: request.PlacedComponent.pf, ppf: request.PlacedComponent.ppf, pi: new Types.ObjectId(request.PlacedComponent.pi!.$oid), @@ -38,6 +53,7 @@ export const startDojoRecipeController: RequestHandler = async (req, res) => { if (room) { processDojoBuildMaterialsGathered(guild, room); } + setDojoRoomLogFunded(guild, component); } await guild.save(); res.json(await getDojoClient(guild, 0)); diff --git a/src/models/guildModel.ts b/src/models/guildModel.ts index 195fe611b..b8eb97afb 100644 --- a/src/models/guildModel.ts +++ b/src/models/guildModel.ts @@ -7,7 +7,8 @@ import { IGuildMemberDatabase, IGuildLogEntryNumber, IGuildLogEntryString, - IGuildRank + IGuildRank, + IGuildLogRoomChange } from "@/src/types/guildTypes"; import { Document, Model, model, Schema, Types } from "mongoose"; import { fusionTreasuresSchema, typeCountSchema } from "./inventoryModels/inventoryModel"; @@ -34,6 +35,7 @@ const dojoComponentSchema = new Schema({ RegularCredits: Number, MiscItems: { type: [typeCountSchema], default: undefined }, CompletionTime: Date, + CompletionLogPending: Boolean, RushPlatinum: Number, DestructionTime: Date, Decos: [dojoDecoSchema], @@ -115,6 +117,16 @@ const guildLogEntryStringSchema = new Schema( { _id: false } ); +const guildLogRoomChangeSchema = new Schema( + { + dateTime: Date, + entryType: Number, + details: String, + componentId: Types.ObjectId + }, + { _id: false } +); + const guildLogEntryNumberSchema = new Schema( { dateTime: Date, @@ -146,6 +158,7 @@ const guildSchema = new Schema( CeremonyContributors: { type: [Types.ObjectId], default: undefined }, CeremonyResetDate: Date, CeremonyEndo: Number, + RoomChanges: { type: [guildLogRoomChangeSchema], default: undefined }, TechChanges: { type: [guildLogEntryStringSchema], default: undefined }, RosterActivity: { type: [guildLogEntryStringSchema], default: undefined }, ClassChanges: { type: [guildLogEntryNumberSchema], default: undefined } diff --git a/src/services/guildService.ts b/src/services/guildService.ts index 061e685f2..bb5803838 100644 --- a/src/services/guildService.ts +++ b/src/services/guildService.ts @@ -6,6 +6,7 @@ import { TInventoryDatabaseDocument } from "@/src/models/inventoryModels/invento import { IDojoClient, IDojoComponentClient, + IDojoComponentDatabase, IDojoContributable, IDojoDecoClient, IGuildClient, @@ -126,7 +127,8 @@ export const getDojoClient = async ( DojoComponents: [] }; const roomsToRemove: Types.ObjectId[] = []; - guild.DojoComponents.forEach(dojoComponent => { + let needSave = false; + for (const dojoComponent of guild.DojoComponents) { if (!componentId || dojoComponent._id.equals(componentId)) { const clientComponent: IDojoComponentClient = { id: toOid(dojoComponent._id), @@ -143,10 +145,18 @@ export const getDojoClient = async ( } if (dojoComponent.CompletionTime) { clientComponent.CompletionTime = toMongoDate(dojoComponent.CompletionTime); + if (dojoComponent.CompletionLogPending && Date.now() >= dojoComponent.CompletionTime.getTime()) { + const entry = guild.RoomChanges?.find(x => x.componentId.equals(dojoComponent._id)); + if (entry) { + dojoComponent.CompletionLogPending = undefined; + entry.entryType = 1; + needSave = true; + } + } if (dojoComponent.DestructionTime) { if (Date.now() >= dojoComponent.DestructionTime.getTime()) { roomsToRemove.push(dojoComponent._id); - return; + continue; } clientComponent.DestructionTime = toMongoDate(dojoComponent.DestructionTime); } @@ -175,12 +185,15 @@ export const getDojoClient = async ( } dojo.DojoComponents.push(clientComponent); } - }); + } if (roomsToRemove.length) { logger.debug(`removing now-destroyed rooms`, roomsToRemove); for (const id of roomsToRemove) { removeDojoRoom(guild, id); } + needSave = true; + } + if (needSave) { await guild.save(); } return dojo; @@ -203,6 +216,13 @@ export const removeDojoRoom = (guild: TGuildDatabaseDocument, componentId: Types } moveResourcesToVault(guild, component); component.Decos?.forEach(deco => moveResourcesToVault(guild, deco)); + + if (guild.RoomChanges) { + const index = guild.RoomChanges.findIndex(x => x.componentId.equals(component._id)); + if (index != -1) { + guild.RoomChanges.splice(index, 1); + } + } }; export const removeDojoDeco = ( @@ -254,6 +274,16 @@ export const processDojoBuildMaterialsGathered = (guild: TGuildDatabaseDocument, } }; +// guild.save(); is expected some time after this function is called +export const setDojoRoomLogFunded = (guild: TGuildDatabaseDocument, component: IDojoComponentDatabase): void => { + const entry = guild.RoomChanges?.find(x => x.componentId.equals(component._id)); + if (entry && entry.entryType == 2) { + entry.entryType = 0; + entry.dateTime = component.CompletionTime!; + component.CompletionLogPending = true; + } +}; + export const fillInInventoryDataForGuildMember = async (member: IGuildMemberClient): Promise => { const inventory = await getInventory(member._id.$oid, "PlayerLevel ActiveAvatarImageType"); member.PlayerLevel = config.spoofMasteryRank == -1 ? inventory.PlayerLevel : config.spoofMasteryRank; diff --git a/src/types/guildTypes.ts b/src/types/guildTypes.ts index e62cacc5d..811f18136 100644 --- a/src/types/guildTypes.ts +++ b/src/types/guildTypes.ts @@ -48,6 +48,7 @@ export interface IGuildDatabase { CeremonyContributors?: Types.ObjectId[]; CeremonyResetDate?: Date; + RoomChanges?: IGuildLogRoomChange[]; TechChanges?: IGuildLogEntryString[]; RosterActivity?: IGuildLogEntryString[]; ClassChanges?: IGuildLogEntryNumber[]; @@ -146,6 +147,7 @@ export interface IDojoComponentDatabase _id: Types.ObjectId; pi?: Types.ObjectId; CompletionTime?: Date; + CompletionLogPending?: boolean; DestructionTime?: Date; Decos?: IDojoDecoDatabase[]; } @@ -193,6 +195,10 @@ export interface IGuildLogEntryString { details: string; } +export interface IGuildLogRoomChange extends IGuildLogEntryString { + componentId: Types.ObjectId; +} + export interface IGuildLogEntryNumber { dateTime: Date; entryType: number;