From 7f995de5d11ab8bb02bb9915470a28cd3d7c17e0 Mon Sep 17 00:00:00 2001 From: Dev Date: Tue, 5 Dec 2023 20:41:43 +0000 Subject: [PATCH] Reworked how the flea market categories are calculated, instead of trying to be smart and add/remove in a cache as offers are created, calculate the categories when needed Categories: Are now much more accurate take into account when player is below flea unlock level Any with a (1) and no offers have been fixed Take into account when offers are barters + barters are filtered out Skip items with a type of `node` during flea assort generation --- project/src/controllers/RagfairController.ts | 31 +++-- .../src/generators/RagfairAssortGenerator.ts | 5 + .../src/generators/RagfairOfferGenerator.ts | 6 - project/src/helpers/HandbookHelper.ts | 6 + project/src/helpers/RagfairHelper.ts | 25 ---- project/src/helpers/TraderAssortHelper.ts | 1 + .../src/models/spt/config/IRagfairConfig.ts | 2 +- project/src/servers/RagfairServer.ts | 10 +- .../src/services/RagfairCategoriesService.ts | 119 +++++++----------- project/src/services/RagfairOfferService.ts | 5 - 10 files changed, 83 insertions(+), 127 deletions(-) diff --git a/project/src/controllers/RagfairController.ts b/project/src/controllers/RagfairController.ts index 67be1ed4..9da2f1f8 100644 --- a/project/src/controllers/RagfairController.ts +++ b/project/src/controllers/RagfairController.ts @@ -95,7 +95,11 @@ export class RagfairController const pmcProfile = this.profileHelper.getPmcProfile(sessionID); result.offers = this.getOffersForSearchType(searchRequest, itemsToAdd, traderAssorts, pmcProfile); - result.categories = this.getSpecificCategories(searchRequest, result.offers); + + if (searchRequest.updateOfferCount) + { + result.categories = this.getSpecificCategories(pmcProfile, searchRequest, result.offers); + } // Client requested "required search" if (searchRequest.neededSearchId) @@ -131,10 +135,8 @@ export class RagfairController } } - // Set categories count (needed for categories to show when choosing 'Linked search') - this.ragfairHelper.countCategories(result); - result.offersCount = result.offers.length; + // Handle paging before returning results only if searching for general items, not preset items if (searchRequest.buildCount === 0) { @@ -176,21 +178,28 @@ export class RagfairController * @param offers ragfair offers to get categories for * @returns record with tpls + counts */ - protected getSpecificCategories(searchRequest: ISearchRequestData, offers: IRagfairOffer[]): Record + protected getSpecificCategories(pmcProfile: IPmcData, searchRequest: ISearchRequestData, offers: IRagfairOffer[]): Record { // Linked/required search categories + const playerHasFleaUnlocked = pmcProfile.Info.Level > this.databaseServer.getTables().globals.config.RagFair.minUserLevel; + let offerPool = []; if (this.isLinkedSearch(searchRequest) || this.isRequiredSearch(searchRequest)) { - return this.ragfairServer.getBespokeCategories(offers); + offerPool = offers; } - - // Get all categories - if ((searchRequest.linkedSearchId === "" && searchRequest.neededSearchId === "")) + else if ((searchRequest.linkedSearchId === "" && searchRequest.neededSearchId === "")) { - return this.ragfairServer.getAllCategories(); + // Get all categories + offerPool = this.ragfairOfferService.getOffers(); + } + else + { + this.logger.error("Unable to get categories from search criteria, see log for request data"); + this.logger.debug(JSON.stringify(searchRequest)); + return {}; } - return {}; + return this.ragfairServer.getAllActiveCategories(playerHasFleaUnlocked, searchRequest, offerPool); } /** diff --git a/project/src/generators/RagfairAssortGenerator.ts b/project/src/generators/RagfairAssortGenerator.ts index da525e8f..b326f2c4 100644 --- a/project/src/generators/RagfairAssortGenerator.ts +++ b/project/src/generators/RagfairAssortGenerator.ts @@ -79,6 +79,11 @@ export class RagfairAssortGenerator const seasonalItemTplBlacklist = this.seasonalEventService.getAllSeasonalEventItems(); for (const item of items) { + if (item._type === "Node") + { + continue; + } + if (!this.itemHelper.isValidItem(item._id, ragfairItemInvalidBaseTypes)) { continue; diff --git a/project/src/generators/RagfairOfferGenerator.ts b/project/src/generators/RagfairOfferGenerator.ts index 3fba7154..91d16327 100644 --- a/project/src/generators/RagfairOfferGenerator.ts +++ b/project/src/generators/RagfairOfferGenerator.ts @@ -21,7 +21,6 @@ import { DatabaseServer } from "@spt-aki/servers/DatabaseServer"; import { SaveServer } from "@spt-aki/servers/SaveServer"; import { FenceService } from "@spt-aki/services/FenceService"; import { LocalisationService } from "@spt-aki/services/LocalisationService"; -import { RagfairCategoriesService } from "@spt-aki/services/RagfairCategoriesService"; import { RagfairOfferService } from "@spt-aki/services/RagfairOfferService"; import { RagfairPriceService } from "@spt-aki/services/RagfairPriceService"; import { HashUtil } from "@spt-aki/utils/HashUtil"; @@ -51,7 +50,6 @@ export class RagfairOfferGenerator @inject("RagfairPriceService") protected ragfairPriceService: RagfairPriceService, @inject("LocalisationService") protected localisationService: LocalisationService, @inject("PaymentHelper") protected paymentHelper: PaymentHelper, - @inject("RagfairCategoriesService") protected ragfairCategoriesService: RagfairCategoriesService, @inject("FenceService") protected fenceService: FenceService, @inject("ItemHelper") protected itemHelper: ItemHelper, @inject("ConfigServer") protected configServer: ConfigServer, @@ -431,8 +429,6 @@ export class RagfairOfferGenerator 1, isPreset || isPackOffer, ); // sellAsOnePiece - - this.ragfairCategoriesService.incrementCategory(offer); } /** @@ -510,8 +506,6 @@ export class RagfairOfferGenerator const offer = this.createFleaOffer(traderID, time, items, barterSchemeItems, loyalLevel, false); - this.ragfairCategoriesService.incrementCategory(offer); - // Refresh complete, reset flag to false trader.base.refreshTraderRagfairOffers = false; } diff --git a/project/src/helpers/HandbookHelper.ts b/project/src/helpers/HandbookHelper.ts index 7ce431fd..652a7cb7 100644 --- a/project/src/helpers/HandbookHelper.ts +++ b/project/src/helpers/HandbookHelper.ts @@ -1,5 +1,6 @@ import { inject, injectable } from "tsyringe"; +import { Category } from "@spt-aki/models/eft/common/tables/IHandbookBase"; import { Money } from "@spt-aki/models/enums/Money"; import { DatabaseServer } from "@spt-aki/servers/DatabaseServer"; import { JsonUtil } from "@spt-aki/utils/JsonUtil"; @@ -164,4 +165,9 @@ export class HandbookHelper const price = this.getTemplatePrice(currencyTypeTo); return price ? Math.round(roubleCurrencyCount / price) : 0; } + + public getCategoryById(handbookId: string): Category + { + return this.databaseServer.getTables().templates.handbook.Categories.find(x => x.Id === handbookId); + } } diff --git a/project/src/helpers/RagfairHelper.ts b/project/src/helpers/RagfairHelper.ts index 85dc82d7..c3aedc56 100644 --- a/project/src/helpers/RagfairHelper.ts +++ b/project/src/helpers/RagfairHelper.ts @@ -147,31 +147,6 @@ export class RagfairHelper return result; } - /* Because of presets, categories are not always 1 */ - public countCategories(result: IGetOffersResult): void - { - const categories = {}; - - for (const offer of result.offers) - { - // only the first item can have presets - const item = offer.items[0]; - categories[item._tpl] = categories[item._tpl] || 0; - categories[item._tpl]++; - } - - // not in search mode, add back non-weapon items - for (const category in result.categories) - { - if (!categories[category]) - { - categories[category] = 1; - } - } - - result.categories = categories; - } - /** * Merges Root Items * Ragfair allows abnormally large stacks. diff --git a/project/src/helpers/TraderAssortHelper.ts b/project/src/helpers/TraderAssortHelper.ts index fb6a4431..84975bd4 100644 --- a/project/src/helpers/TraderAssortHelper.ts +++ b/project/src/helpers/TraderAssortHelper.ts @@ -58,6 +58,7 @@ export class TraderAssortHelper * Filter out assorts not unlocked due to level OR quest completion * @param sessionId session id * @param traderId traders id + * @param flea Should assorts player hasn't unlocked be returned - default false * @returns a traders' assorts */ public getAssort(sessionId: string, traderId: string, flea = false): ITraderAssort diff --git a/project/src/models/spt/config/IRagfairConfig.ts b/project/src/models/spt/config/IRagfairConfig.ts index dcffc7c8..6413fe05 100644 --- a/project/src/models/spt/config/IRagfairConfig.ts +++ b/project/src/models/spt/config/IRagfairConfig.ts @@ -154,7 +154,7 @@ export interface Blacklist enableBsgList: boolean; /** Should quest items be blacklisted from flea */ enableQuestList: boolean; - /** Should trader items that are blacklisted by bsg */ + /** Should trader items that are blacklisted by bsg be listed on flea */ traderItems: boolean; } diff --git a/project/src/servers/RagfairServer.ts b/project/src/servers/RagfairServer.ts index 87ad6de3..b8121b88 100644 --- a/project/src/servers/RagfairServer.ts +++ b/project/src/servers/RagfairServer.ts @@ -4,6 +4,7 @@ import { RagfairOfferGenerator } from "@spt-aki/generators/RagfairOfferGenerator import { TraderAssortHelper } from "@spt-aki/helpers/TraderAssortHelper"; import { TraderHelper } from "@spt-aki/helpers/TraderHelper"; import { IRagfairOffer } from "@spt-aki/models/eft/ragfair/IRagfairOffer"; +import { ISearchRequestData } from "@spt-aki/models/eft/ragfair/ISearchRequestData"; import { ConfigTypes } from "@spt-aki/models/enums/ConfigTypes"; import { Traders } from "@spt-aki/models/enums/Traders"; import { IRagfairConfig } from "@spt-aki/models/spt/config/IRagfairConfig"; @@ -82,14 +83,9 @@ export class RagfairServer return Object.keys(this.ragfairConfig.traders).filter((x) => this.ragfairConfig.traders[x]); } - public getAllCategories(): Record + public getAllActiveCategories(fleaUnlocked: boolean, searchRequestData: ISearchRequestData, offers: IRagfairOffer[]): Record { - return this.ragfairCategoriesService.getAllCategories(); - } - - public getBespokeCategories(offers: IRagfairOffer[]): Record - { - return this.ragfairCategoriesService.getBespokeCategories(offers); + return this.ragfairCategoriesService.getCategoriesFromOffers(offers, searchRequestData, fleaUnlocked); } /** diff --git a/project/src/services/RagfairCategoriesService.ts b/project/src/services/RagfairCategoriesService.ts index 5a1196bd..c4982663 100644 --- a/project/src/services/RagfairCategoriesService.ts +++ b/project/src/services/RagfairCategoriesService.ts @@ -1,96 +1,71 @@ import { inject, injectable } from "tsyringe"; +import { PaymentHelper } from "@spt-aki/helpers/PaymentHelper"; import { IRagfairOffer } from "@spt-aki/models/eft/ragfair/IRagfairOffer"; +import { ISearchRequestData, OfferOwnerType } from "@spt-aki/models/eft/ragfair/ISearchRequestData"; +import { MemberCategory } from "@spt-aki/models/enums/MemberCategory"; import { ILogger } from "@spt-aki/models/spt/utils/ILogger"; @injectable() export class RagfairCategoriesService { - protected categories: Record = {}; - - constructor(@inject("WinstonLogger") protected logger: ILogger) + constructor( + @inject("WinstonLogger") protected logger: ILogger, + @inject("PaymentHelper") protected paymentHelper: PaymentHelper, + ) {} /** - * Get all flea categories and their count of offers - * @returns item categories and count + * Get a dictionary of each item the play can see in their flea menu, filtered by what is available for them to buy + * @param offers All offers in flea + * @param searchRequestData Search criteria requested + * @param fleaUnlocked Can player see full flea yet (level 15 by default) + * @returns KVP of item tpls + count of offers */ - public getAllCategories(): Record + public getCategoriesFromOffers(offers: IRagfairOffer[], searchRequestData: ISearchRequestData, fleaUnlocked: boolean): Record { - return this.categories; - } - - /** - * With the supplied items, get custom categories - * @returns a custom list of categories - */ - public getBespokeCategories(offers: IRagfairOffer[]): Record - { - return this.processOffersIntoCategories(offers); - } - - /** - * Take an array of ragfair offers and create a dictionary of items with thier corrisponding offer count - * @param offers ragfair offers - * @returns categories and count - */ - protected processOffersIntoCategories(offers: IRagfairOffer[]): Record - { - const result = {}; + const validOffersForPlayerToSee = {}; for (const offer of offers) { - this.addOrIncrementCategory(offer, result); - } + const isTraderOffer = offer.user.memberType === MemberCategory.TRADER; - return result; - } - - /** - * Increment or decrement a category array - * @param offer Offer to process - * @param categories Categories to update - * @param increment (Optional) Should item be incremented or decremented - */ - protected addOrIncrementCategory(offer: IRagfairOffer, categories: Record, increment = true): void - { - const itemId = offer.items[0]._tpl; - if (increment) - { - categories[itemId] = categories[itemId] ? categories[itemId] + 1 : 1; - } - else - { - // No category, no work to do - if (categories[itemId]) + // Not level 15 and offer is from player, skip + if (!fleaUnlocked && !isTraderOffer) { - categories[itemId]--; + continue; + } - // Remove category entirely as its 0 or less - if (categories[itemId] < 1) - { - delete categories[itemId]; - } + // Remove items not for money when `removeBartering` is enabled + if (searchRequestData.removeBartering && (offer.requirements.length > 1 || !this.paymentHelper.isMoneyTpl(offer.requirements[0]._tpl))) + { + continue; + } + + // Remove when filter set to players only + offer is from trader + if (searchRequestData.offerOwnerType === OfferOwnerType.PLAYEROWNERTYPE && isTraderOffer) + { + continue; + } + + // Remove when filter set to traders only + offer is not from trader + if (searchRequestData.offerOwnerType === OfferOwnerType.TRADEROWNERTYPE && !isTraderOffer) + { + continue; + } + + const itemTpl = offer.items[0]._tpl + + if (!validOffersForPlayerToSee[itemTpl]) + { + validOffersForPlayerToSee[itemTpl] = 1; + } + else + { + validOffersForPlayerToSee[itemTpl]++; } } - } - /** - * Increase category count by 1 - * @param offer - */ - public incrementCategory(offer: IRagfairOffer): void - { - this.addOrIncrementCategory(offer, this.categories); - this.categories[offer.items[0]._tpl]++; - } + return validOffersForPlayerToSee; - /** - * Reduce category count by 1 - * @param offer - */ - public decrementCategory(offer: IRagfairOffer): void - { - this.addOrIncrementCategory(offer, this.categories, false); - this.categories[offer.items[0]._tpl]--; } } diff --git a/project/src/services/RagfairOfferService.ts b/project/src/services/RagfairOfferService.ts index c011cde0..1dfea886 100644 --- a/project/src/services/RagfairOfferService.ts +++ b/project/src/services/RagfairOfferService.ts @@ -13,7 +13,6 @@ import { ConfigServer } from "@spt-aki/servers/ConfigServer"; import { DatabaseServer } from "@spt-aki/servers/DatabaseServer"; import { SaveServer } from "@spt-aki/servers/SaveServer"; import { LocalisationService } from "@spt-aki/services/LocalisationService"; -import { RagfairCategoriesService } from "@spt-aki/services/RagfairCategoriesService"; import { HttpResponseUtil } from "@spt-aki/utils/HttpResponseUtil"; import { RagfairOfferHolder } from "@spt-aki/utils/RagfairOfferHolder"; import { TimeUtil } from "@spt-aki/utils/TimeUtil"; @@ -33,7 +32,6 @@ export class RagfairOfferService @inject("DatabaseServer") protected databaseServer: DatabaseServer, @inject("SaveServer") protected saveServer: SaveServer, @inject("RagfairServerHelper") protected ragfairServerHelper: RagfairServerHelper, - @inject("RagfairCategoriesService") protected ragfairCategoriesService: RagfairCategoriesService, @inject("ProfileHelper") protected profileHelper: ProfileHelper, @inject("EventOutputHolder") protected eventOutputHolder: EventOutputHolder, @inject("HttpResponseUtil") protected httpResponse: HttpResponseUtil, @@ -222,9 +220,6 @@ export class RagfairOfferService this.returnPlayerOffer(staleOffer); } - // Reduce category count by 1 as offer is now stale and about to be removed - this.ragfairCategoriesService.decrementCategory(staleOffer); - // Remove expired existing offer from global offers this.removeOfferById(staleOffer._id); }