From 3401b4094e9c0172000298071d51958cde4dc59a Mon Sep 17 00:00:00 2001 From: DrakiaXYZ Date: Mon, 8 Jan 2024 08:51:46 +0000 Subject: [PATCH] Initial commit of trader services structure (!187) - Only contains BTR taxi and delivery services - Super basic implementation, client doesn't seem to need anything except the service type for these - Includes handling of the BTR Item Delivery service Basic implementation can be merged and expanded on later, doesn't break anything by existing (Hopefully) I've opted to add the routes to the `InRaid` controller/Callbacks, because it is a route only accessed within the raid. Seemed like it would be the best place for it Co-authored-by: DrakiaXYZ <565558+TheDgtl@users.noreply.github.com> Reviewed-on: https://dev.sp-tarkov.com/SPT-AKI/Server/pulls/187 Co-authored-by: DrakiaXYZ Co-committed-by: DrakiaXYZ --- project/assets/configs/trader.json | 3 +- .../656f0f98d80a697f855d34b1/dialogue.json | 7 ++++ .../656f0f98d80a697f855d34b1/services.json | 8 ++++ project/src/callbacks/InraidCallbacks.ts | 21 ++++++++++ project/src/controllers/InraidController.ts | 40 +++++++++++++++++++ project/src/di/Container.ts | 4 ++ .../src/models/eft/common/tables/ITrader.ts | 2 + .../eft/inRaid/IItemDeliveryRequestData.ts | 7 ++++ project/src/models/enums/TraderServiceType.ts | 9 +++++ .../src/models/spt/config/ITraderConfig.ts | 1 + .../spt/services/ITraderServiceModel.ts | 8 ++++ .../routers/dynamic/InraidDynamicRouter.ts | 7 ++++ .../src/routers/static/InraidStaticRouter.ts | 7 ++++ project/src/services/TraderServicesService.ts | 20 ++++++++++ 14 files changed, 143 insertions(+), 1 deletion(-) create mode 100644 project/assets/database/traders/656f0f98d80a697f855d34b1/dialogue.json create mode 100644 project/assets/database/traders/656f0f98d80a697f855d34b1/services.json create mode 100644 project/src/models/eft/inRaid/IItemDeliveryRequestData.ts create mode 100644 project/src/models/enums/TraderServiceType.ts create mode 100644 project/src/models/spt/services/ITraderServiceModel.ts create mode 100644 project/src/services/TraderServicesService.ts diff --git a/project/assets/configs/trader.json b/project/assets/configs/trader.json index 4e900f2d..4833c800 100644 --- a/project/assets/configs/trader.json +++ b/project/assets/configs/trader.json @@ -241,7 +241,8 @@ } }, "allowBossItems": false - } + }, + "btrDeliveryExpireHours": 240 } } diff --git a/project/assets/database/traders/656f0f98d80a697f855d34b1/dialogue.json b/project/assets/database/traders/656f0f98d80a697f855d34b1/dialogue.json new file mode 100644 index 00000000..02e2e378 --- /dev/null +++ b/project/assets/database/traders/656f0f98d80a697f855d34b1/dialogue.json @@ -0,0 +1,7 @@ +{ + "itemsDelivered": [ + "657399489b19e826a721d75c 0", + "657399489b19e826a721d75c 1", + "657399489b19e826a721d75c 2" + ] +} \ No newline at end of file diff --git a/project/assets/database/traders/656f0f98d80a697f855d34b1/services.json b/project/assets/database/traders/656f0f98d80a697f855d34b1/services.json new file mode 100644 index 00000000..501ec691 --- /dev/null +++ b/project/assets/database/traders/656f0f98d80a697f855d34b1/services.json @@ -0,0 +1,8 @@ +[ + { + "serviceType": "BtrItemsDelivery" + }, + { + "serviceType": "PlayerTaxi" + } +] \ No newline at end of file diff --git a/project/src/callbacks/InraidCallbacks.ts b/project/src/callbacks/InraidCallbacks.ts index 71d2dc22..945d60be 100644 --- a/project/src/callbacks/InraidCallbacks.ts +++ b/project/src/callbacks/InraidCallbacks.ts @@ -5,6 +5,8 @@ import { INullResponseData } from "@spt-aki/models/eft/httpResponse/INullRespons import { IRegisterPlayerRequestData } from "@spt-aki/models/eft/inRaid/IRegisterPlayerRequestData"; import { ISaveProgressRequestData } from "@spt-aki/models/eft/inRaid/ISaveProgressRequestData"; import { HttpResponseUtil } from "@spt-aki/utils/HttpResponseUtil"; +import { IEmptyRequestData } from "@spt-aki/models/eft/common/IEmptyRequestData"; +import { IItemDeliveryRequestData } from "@spt-aki/models/eft/inRaid/IItemDeliveryRequestData"; /** * Handle client requests @@ -80,4 +82,23 @@ export class InraidCallbacks { return this.httpResponse.noBody(this.inraidController.getAirdropConfig()); } + + /** + * Handle singleplayer/traderServices/getTraderServices + */ + public getTraderServices(url: string, info: IEmptyRequestData, sessionId: string): string + { + const lastSlashPos = url.lastIndexOf('/'); + const traderId = url.substring(lastSlashPos + 1); + return this.httpResponse.noBody(this.inraidController.getTraderServices(sessionId, traderId)); + } + + /** + * Handle singleplayer/traderServices/itemDelivery + */ + public itemDelivery(url: string, request: IItemDeliveryRequestData, sessionId: string): INullResponseData + { + this.inraidController.itemDelivery(sessionId, request.traderId, request.items); + return this.httpResponse.nullResponse(); + } } diff --git a/project/src/controllers/InraidController.ts b/project/src/controllers/InraidController.ts index 69bba792..454f6c87 100644 --- a/project/src/controllers/InraidController.ts +++ b/project/src/controllers/InraidController.ts @@ -29,6 +29,13 @@ import { MatchBotDetailsCacheService } from "@spt-aki/services/MatchBotDetailsCa import { PmcChatResponseService } from "@spt-aki/services/PmcChatResponseService"; import { JsonUtil } from "@spt-aki/utils/JsonUtil"; import { TimeUtil } from "@spt-aki/utils/TimeUtil"; +import { TraderServicesService } from "@spt-aki/services/TraderServicesService"; +import { ITraderServiceModel } from "@spt-aki/models/spt/services/ITraderServiceModel"; +import { Item } from "@spt-aki/models/eft/common/tables/IItem"; +import { MailSendService } from "@spt-aki/services/MailSendService"; +import { RandomUtil } from "@spt-aki/utils/RandomUtil"; +import { ITraderConfig } from "@spt-aki/models/spt/config/ITraderConfig"; +import { MessageType } from "@spt-aki/models/enums/MessageType"; /** * Logic for handling In Raid callbacks @@ -38,6 +45,7 @@ export class InraidController { protected airdropConfig: IAirdropConfig; protected inraidConfig: IInRaidConfig; + protected traderConfig: ITraderConfig; constructor( @inject("WinstonLogger") protected logger: ILogger, @@ -53,14 +61,18 @@ export class InraidController @inject("PlayerScavGenerator") protected playerScavGenerator: PlayerScavGenerator, @inject("HealthHelper") protected healthHelper: HealthHelper, @inject("TraderHelper") protected traderHelper: TraderHelper, + @inject("TraderServicesService") protected traderServicesService: TraderServicesService, @inject("InsuranceService") protected insuranceService: InsuranceService, @inject("InRaidHelper") protected inRaidHelper: InRaidHelper, @inject("ApplicationContext") protected applicationContext: ApplicationContext, @inject("ConfigServer") protected configServer: ConfigServer, + @inject("MailSendService") protected mailSendService: MailSendService, + @inject("RandomUtil") protected randomUtil: RandomUtil, ) { this.airdropConfig = this.configServer.getConfig(ConfigTypes.AIRDROP); this.inraidConfig = this.configServer.getConfig(ConfigTypes.IN_RAID); + this.traderConfig = this.configServer.getConfig(ConfigTypes.TRADER); } /** @@ -498,4 +510,32 @@ export class InraidController { return this.airdropConfig; } + + /** + * Handle singleplayer/traderServices/getTraderServices + * @returns Trader services data + */ + public getTraderServices(sessionId: string, traderId: string): ITraderServiceModel[] + { + return this.traderServicesService.getTraderServices(traderId); + } + + /** + * Handle singleplayer/traderServices/itemDelivery + */ + public itemDelivery(sessionId: string, traderId: string, items: Item[]): void + { + const dialogueTemplates = this.databaseServer.getTables().traders[traderId].dialogue; + const messageId = this.randomUtil.getArrayValue(dialogueTemplates.itemsDelivered); + const messageStoreTime = this.timeUtil.getHoursAsSeconds(this.traderConfig.fence.btrDeliveryExpireHours); + + this.mailSendService.sendLocalisedNpcMessageToPlayer( + sessionId, + this.traderHelper.getTraderById(traderId), + MessageType.BTR_ITEMS_DELIVERY, + messageId, + items, + messageStoreTime, + ); + } } diff --git a/project/src/di/Container.ts b/project/src/di/Container.ts index d634aee0..797d9c32 100644 --- a/project/src/di/Container.ts +++ b/project/src/di/Container.ts @@ -250,6 +250,7 @@ import { VFS } from "@spt-aki/utils/VFS"; import { Watermark, WatermarkLocale } from "@spt-aki/utils/Watermark"; import { WinstonMainLogger } from "@spt-aki/utils/logging/WinstonMainLogger"; import { WinstonRequestLogger } from "@spt-aki/utils/logging/WinstonRequestLogger"; +import { TraderServicesService } from "@spt-aki/services/TraderServicesService"; /** * Handle the registration of classes to be used by the Dependency Injection code @@ -656,6 +657,9 @@ export class Container depContainer.register("TraderAssortService", TraderAssortService, { lifecycle: Lifecycle.Singleton, }); + depContainer.register("TraderServicesService", TraderServicesService, { + lifecycle: Lifecycle.Singleton, + }); depContainer.register("RagfairPriceService", RagfairPriceService, { lifecycle: Lifecycle.Singleton, diff --git a/project/src/models/eft/common/tables/ITrader.ts b/project/src/models/eft/common/tables/ITrader.ts index ffe8ada7..25dd97fd 100644 --- a/project/src/models/eft/common/tables/ITrader.ts +++ b/project/src/models/eft/common/tables/ITrader.ts @@ -1,5 +1,6 @@ /* eslint-disable @typescript-eslint/naming-convention */ import { Item } from "@spt-aki/models/eft/common/tables/IItem"; +import { ITraderServiceModel } from "@spt-aki/models/spt/services/ITraderServiceModel"; export interface ITrader { @@ -8,6 +9,7 @@ export interface ITrader dialogue?: Record; questassort: Record>; suits?: ISuit[]; + services?: ITraderServiceModel[]; } export interface ITraderBase diff --git a/project/src/models/eft/inRaid/IItemDeliveryRequestData.ts b/project/src/models/eft/inRaid/IItemDeliveryRequestData.ts new file mode 100644 index 00000000..4c2337df --- /dev/null +++ b/project/src/models/eft/inRaid/IItemDeliveryRequestData.ts @@ -0,0 +1,7 @@ +import { Item } from "../common/tables/IItem"; + +export interface IItemDeliveryRequestData +{ + items: Item[], + traderId: string +} \ No newline at end of file diff --git a/project/src/models/enums/TraderServiceType.ts b/project/src/models/enums/TraderServiceType.ts new file mode 100644 index 00000000..406ec0bc --- /dev/null +++ b/project/src/models/enums/TraderServiceType.ts @@ -0,0 +1,9 @@ +export enum TraderServiceType +{ + EXUSEC_LOYALTY = "ExUsecLoyalty", + ZRYACHIY_AID = "ZryachiyAid", + CULTISTS_AID = "CultistsAid", + BTR_ITEMS_DELIVERY = "BtrItemsDelivery", + PLAYER_TAXI = "PlayerTaxi", + BTR_BOT_COVER = "BtrBotCover" +} \ No newline at end of file diff --git a/project/src/models/spt/config/ITraderConfig.ts b/project/src/models/spt/config/ITraderConfig.ts index 9f320ed3..b9a1dd37 100644 --- a/project/src/models/spt/config/ITraderConfig.ts +++ b/project/src/models/spt/config/ITraderConfig.ts @@ -44,6 +44,7 @@ export interface FenceConfig blacklistSeasonalItems: boolean; blacklist: string[]; coopExtractGift: CoopExtractReward; + btrDeliveryExpireHours: number; } export interface CoopExtractReward extends LootRequest diff --git a/project/src/models/spt/services/ITraderServiceModel.ts b/project/src/models/spt/services/ITraderServiceModel.ts new file mode 100644 index 00000000..50fcfa8e --- /dev/null +++ b/project/src/models/spt/services/ITraderServiceModel.ts @@ -0,0 +1,8 @@ +import { TraderServiceType } from "@spt-aki/models/enums/TraderServiceType"; + +export interface ITraderServiceModel +{ + serviceType: TraderServiceType, + itemsToPay?: Record[], + subServices?: Record[], +} \ No newline at end of file diff --git a/project/src/routers/dynamic/InraidDynamicRouter.ts b/project/src/routers/dynamic/InraidDynamicRouter.ts index 72ccea1d..26e89f72 100644 --- a/project/src/routers/dynamic/InraidDynamicRouter.ts +++ b/project/src/routers/dynamic/InraidDynamicRouter.ts @@ -16,6 +16,13 @@ export class InraidDynamicRouter extends DynamicRouter return this.inraidCallbacks.registerPlayer(url, info, sessionID); }, ), + new RouteAction( + "/singleplayer/traderServices/getTraderServices/", + (url: string, info: any, sessionID: string, output: string): any => + { + return this.inraidCallbacks.getTraderServices(url, info, sessionID); + }, + ), ]); } diff --git a/project/src/routers/static/InraidStaticRouter.ts b/project/src/routers/static/InraidStaticRouter.ts index 3d3eda8d..ac8c040c 100644 --- a/project/src/routers/static/InraidStaticRouter.ts +++ b/project/src/routers/static/InraidStaticRouter.ts @@ -41,6 +41,13 @@ export class InraidStaticRouter extends StaticRouter return this.inraidCallbacks.getAirdropConfig(); }, ), + new RouteAction( + "/singleplayer/traderServices/itemDelivery", + (url: string, info: any, sessionID: string, output: string): any => + { + return this.inraidCallbacks.itemDelivery(url, info, sessionID); + } + ), ]); } } diff --git a/project/src/services/TraderServicesService.ts b/project/src/services/TraderServicesService.ts new file mode 100644 index 00000000..8f6216ef --- /dev/null +++ b/project/src/services/TraderServicesService.ts @@ -0,0 +1,20 @@ +import { ITraderServiceModel } from "@spt-aki/models/spt/services/ITraderServiceModel"; +import { ILogger } from "@spt-aki/models/spt/utils/ILogger"; +import { DatabaseServer } from "@spt-aki/servers/DatabaseServer"; +import { inject, injectable } from "tsyringe"; + +@injectable() +export class TraderServicesService +{ + constructor( + @inject("WinstonLogger") protected logger: ILogger, + @inject("DatabaseServer") protected databaseServer: DatabaseServer, + ) + {} + + public getTraderServices(traderId: string): ITraderServiceModel[] + { + const traderServices = this.databaseServer.getTables().traders[traderId]?.services; + return traderServices ?? []; + } +} \ No newline at end of file