using SPTarkov.DI.Annotations; using SPTarkov.Server.Core.DI; using SPTarkov.Server.Core.Extensions; using SPTarkov.Server.Core.Models.Common; using SPTarkov.Server.Core.Models.Eft.Profile; using SPTarkov.Server.Core.Models.Spt.Config; using SPTarkov.Server.Core.Models.Utils; using SPTarkov.Server.Core.Servers; using SPTarkov.Server.Core.Services; using SPTarkov.Server.Core.Utils; using LogLevel = SPTarkov.Server.Core.Models.Spt.Logging.LogLevel; namespace SPTarkov.Server.Core.Callbacks; [Injectable(TypePriority = OnUpdateOrder.BtrDeliveryCallbacks)] public class BtrDeliveryCallbacks( ISptLogger _logger, BtrDeliveryService _btrDeliveryService, TimeUtil _timeUtil, ConfigServer _configServer, SaveServer _saveServer ) : IOnUpdate { private readonly BtrDeliveryConfig _btrDeliveryConfig = _configServer.GetConfig(); public Task OnUpdate(long secondsSinceLastRun) { if (secondsSinceLastRun < _btrDeliveryConfig.RunIntervalSeconds) { return Task.FromResult(false); } ProcessDeliveries(); return Task.FromResult(true); } /// /// Process BTR delivery items of all profiles prior to being given back to the player through the mail service /// protected void ProcessDeliveries() { // Process each installed profile. foreach (var sessionId in _saveServer.GetProfiles()) { ProcessDeliveryByProfile(sessionId.Key); } } /// /// Process delivery items of a single profile prior to being given back to the player through the mail service /// /// Player id public void ProcessDeliveryByProfile(string sessionId) { // Filter out items that don't need to be processed yet. var toBeProcessed = FilterDeliveryItems(sessionId); // Do nothing if no items to process if (toBeProcessed.Count == 0) { return; } ProcessDeliveryItems(toBeProcessed, sessionId); } /// /// Get all delivery items that are ready to be processed in a specific profile /// /// Session/Player id /// All delivery items that are ready to be processed protected List FilterDeliveryItems(string sessionId) { var currentTime = _timeUtil.GetTimeStamp(); var deliveryList = _saveServer.GetProfile(sessionId).BtrDeliveryList; if (deliveryList != null && deliveryList!.Count > 0) { if (_logger.IsLogEnabled(LogLevel.Debug)) { _logger.Debug( $"Found {deliveryList.Count} BTR delivery package(s) in profile {sessionId}" ); } return deliveryList .Where(toBeDelivered => currentTime >= toBeDelivered.ScheduledTime) .ToList(); } return []; } /// /// This method orchestrates the processing of delivery items in a profile /// /// The delivery items to process /// session ID that should receive the processed items protected void ProcessDeliveryItems(List packagesToBeDelivered, string sessionId) { if (_logger.IsLogEnabled(LogLevel.Debug)) { _logger.Debug( $"Processing {packagesToBeDelivered.Count} BTR delivery package(s), which include a total of: {packagesToBeDelivered.Select(items => items.Items).Count()} items, in profile: {sessionId}" ); } // Iterate over each of the insurance packages. foreach (var package in packagesToBeDelivered) { // Create a new root parent ID for the message we'll be sending the player var rootItemParentId = new MongoId(); // Update the delivery items to have the new root parent ID for root/orphaned items package.Items = package.Items.AdoptOrphanedItems(rootItemParentId); _btrDeliveryService.SendBTRDelivery(sessionId, package.Items); // Remove the fully processed BTR delivery package from the profile. _btrDeliveryService.RemoveBTRDeliveryPackageFromProfile(sessionId, package); } } }