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 <drakiaxyz@noreply.dev.sp-tarkov.com>
Co-committed-by: DrakiaXYZ <drakiaxyz@noreply.dev.sp-tarkov.com>
This commit is contained in:
DrakiaXYZ 2024-01-08 08:51:46 +00:00 committed by chomp
parent 0b3ad5bbd8
commit 3401b4094e
14 changed files with 143 additions and 1 deletions

View File

@ -241,7 +241,8 @@
} }
}, },
"allowBossItems": false "allowBossItems": false
} },
"btrDeliveryExpireHours": 240
} }
} }

View File

@ -0,0 +1,7 @@
{
"itemsDelivered": [
"657399489b19e826a721d75c 0",
"657399489b19e826a721d75c 1",
"657399489b19e826a721d75c 2"
]
}

View File

@ -0,0 +1,8 @@
[
{
"serviceType": "BtrItemsDelivery"
},
{
"serviceType": "PlayerTaxi"
}
]

View File

@ -5,6 +5,8 @@ import { INullResponseData } from "@spt-aki/models/eft/httpResponse/INullRespons
import { IRegisterPlayerRequestData } from "@spt-aki/models/eft/inRaid/IRegisterPlayerRequestData"; import { IRegisterPlayerRequestData } from "@spt-aki/models/eft/inRaid/IRegisterPlayerRequestData";
import { ISaveProgressRequestData } from "@spt-aki/models/eft/inRaid/ISaveProgressRequestData"; import { ISaveProgressRequestData } from "@spt-aki/models/eft/inRaid/ISaveProgressRequestData";
import { HttpResponseUtil } from "@spt-aki/utils/HttpResponseUtil"; 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 * Handle client requests
@ -80,4 +82,23 @@ export class InraidCallbacks
{ {
return this.httpResponse.noBody(this.inraidController.getAirdropConfig()); 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();
}
} }

View File

@ -29,6 +29,13 @@ import { MatchBotDetailsCacheService } from "@spt-aki/services/MatchBotDetailsCa
import { PmcChatResponseService } from "@spt-aki/services/PmcChatResponseService"; import { PmcChatResponseService } from "@spt-aki/services/PmcChatResponseService";
import { JsonUtil } from "@spt-aki/utils/JsonUtil"; import { JsonUtil } from "@spt-aki/utils/JsonUtil";
import { TimeUtil } from "@spt-aki/utils/TimeUtil"; 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 * Logic for handling In Raid callbacks
@ -38,6 +45,7 @@ export class InraidController
{ {
protected airdropConfig: IAirdropConfig; protected airdropConfig: IAirdropConfig;
protected inraidConfig: IInRaidConfig; protected inraidConfig: IInRaidConfig;
protected traderConfig: ITraderConfig;
constructor( constructor(
@inject("WinstonLogger") protected logger: ILogger, @inject("WinstonLogger") protected logger: ILogger,
@ -53,14 +61,18 @@ export class InraidController
@inject("PlayerScavGenerator") protected playerScavGenerator: PlayerScavGenerator, @inject("PlayerScavGenerator") protected playerScavGenerator: PlayerScavGenerator,
@inject("HealthHelper") protected healthHelper: HealthHelper, @inject("HealthHelper") protected healthHelper: HealthHelper,
@inject("TraderHelper") protected traderHelper: TraderHelper, @inject("TraderHelper") protected traderHelper: TraderHelper,
@inject("TraderServicesService") protected traderServicesService: TraderServicesService,
@inject("InsuranceService") protected insuranceService: InsuranceService, @inject("InsuranceService") protected insuranceService: InsuranceService,
@inject("InRaidHelper") protected inRaidHelper: InRaidHelper, @inject("InRaidHelper") protected inRaidHelper: InRaidHelper,
@inject("ApplicationContext") protected applicationContext: ApplicationContext, @inject("ApplicationContext") protected applicationContext: ApplicationContext,
@inject("ConfigServer") protected configServer: ConfigServer, @inject("ConfigServer") protected configServer: ConfigServer,
@inject("MailSendService") protected mailSendService: MailSendService,
@inject("RandomUtil") protected randomUtil: RandomUtil,
) )
{ {
this.airdropConfig = this.configServer.getConfig(ConfigTypes.AIRDROP); this.airdropConfig = this.configServer.getConfig(ConfigTypes.AIRDROP);
this.inraidConfig = this.configServer.getConfig(ConfigTypes.IN_RAID); 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; 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,
);
}
} }

View File

@ -250,6 +250,7 @@ import { VFS } from "@spt-aki/utils/VFS";
import { Watermark, WatermarkLocale } from "@spt-aki/utils/Watermark"; import { Watermark, WatermarkLocale } from "@spt-aki/utils/Watermark";
import { WinstonMainLogger } from "@spt-aki/utils/logging/WinstonMainLogger"; import { WinstonMainLogger } from "@spt-aki/utils/logging/WinstonMainLogger";
import { WinstonRequestLogger } from "@spt-aki/utils/logging/WinstonRequestLogger"; 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 * Handle the registration of classes to be used by the Dependency Injection code
@ -656,6 +657,9 @@ export class Container
depContainer.register<TraderAssortService>("TraderAssortService", TraderAssortService, { depContainer.register<TraderAssortService>("TraderAssortService", TraderAssortService, {
lifecycle: Lifecycle.Singleton, lifecycle: Lifecycle.Singleton,
}); });
depContainer.register<TraderServicesService>("TraderServicesService", TraderServicesService, {
lifecycle: Lifecycle.Singleton,
});
depContainer.register<RagfairPriceService>("RagfairPriceService", RagfairPriceService, { depContainer.register<RagfairPriceService>("RagfairPriceService", RagfairPriceService, {
lifecycle: Lifecycle.Singleton, lifecycle: Lifecycle.Singleton,

View File

@ -1,5 +1,6 @@
/* eslint-disable @typescript-eslint/naming-convention */ /* eslint-disable @typescript-eslint/naming-convention */
import { Item } from "@spt-aki/models/eft/common/tables/IItem"; import { Item } from "@spt-aki/models/eft/common/tables/IItem";
import { ITraderServiceModel } from "@spt-aki/models/spt/services/ITraderServiceModel";
export interface ITrader export interface ITrader
{ {
@ -8,6 +9,7 @@ export interface ITrader
dialogue?: Record<string, string[]>; dialogue?: Record<string, string[]>;
questassort: Record<string, Record<string, string>>; questassort: Record<string, Record<string, string>>;
suits?: ISuit[]; suits?: ISuit[];
services?: ITraderServiceModel[];
} }
export interface ITraderBase export interface ITraderBase

View File

@ -0,0 +1,7 @@
import { Item } from "../common/tables/IItem";
export interface IItemDeliveryRequestData
{
items: Item[],
traderId: string
}

View File

@ -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"
}

View File

@ -44,6 +44,7 @@ export interface FenceConfig
blacklistSeasonalItems: boolean; blacklistSeasonalItems: boolean;
blacklist: string[]; blacklist: string[];
coopExtractGift: CoopExtractReward; coopExtractGift: CoopExtractReward;
btrDeliveryExpireHours: number;
} }
export interface CoopExtractReward extends LootRequest export interface CoopExtractReward extends LootRequest

View File

@ -0,0 +1,8 @@
import { TraderServiceType } from "@spt-aki/models/enums/TraderServiceType";
export interface ITraderServiceModel
{
serviceType: TraderServiceType,
itemsToPay?: Record<string, number>[],
subServices?: Record<string, number>[],
}

View File

@ -16,6 +16,13 @@ export class InraidDynamicRouter extends DynamicRouter
return this.inraidCallbacks.registerPlayer(url, info, sessionID); 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);
},
),
]); ]);
} }

View File

@ -41,6 +41,13 @@ export class InraidStaticRouter extends StaticRouter
return this.inraidCallbacks.getAirdropConfig(); 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);
}
),
]); ]);
} }
} }

View File

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