using System.Collections.Concurrent; using SPTarkov.DI.Annotations; using SPTarkov.Server.Core.Models.Spt.Services; using SPTarkov.Server.Core.Utils; namespace SPTarkov.Server.Core.Services; [Injectable(InjectionType.Singleton)] public class ProfileActivityService( TimeUtil timeUtil ) { private readonly ConcurrentDictionary _activeProfiles = []; public void AddActiveProfile(string sessionId, long clientStartedTimestamp) { _activeProfiles.AddOrUpdate( sessionId, // On add value key => new ProfileActivityData { ClientStartedTimestamp = clientStartedTimestamp, LastActive = timeUtil.GetTimeStamp() }, // On Update value, client was started before but crashed or user restarted (key, existingValue) => { existingValue.ClientStartedTimestamp = clientStartedTimestamp; existingValue.LastActive = timeUtil.GetTimeStamp(); existingValue.RaidData = null; return existingValue; }); } // Yes this is terrible, the other alternative is re-doing half of bot-gen which is currently doing guess-work anyway public ProfileActivityRaidData? GetFirstProfileActivityRaidData() { if (!_activeProfiles.IsEmpty) { return _activeProfiles.First().Value.RaidData; } return null; } public ProfileActivityRaidData? GetProfileActivityRaidData(string sessionId) { if (_activeProfiles.TryGetValue(sessionId, out var currentActiveProfile)) { currentActiveProfile.RaidData ??= new(); return currentActiveProfile.RaidData; } return null; } /// /// Was the requested profile active within the last x minutes /// /// Profile to check /// Minutes to check for activity in /// True when profile was active within past x minutes public bool ActiveWithinLastMinutes(string sessionId, int minutes) { if (!_activeProfiles.TryGetValue(sessionId, out var profileActivity)) { // No record, exit early return false; } return timeUtil.GetTimeStamp() - profileActivity.LastActive < minutes * 60; } /// /// Get a list of profile ids that were active in the last x minutes /// /// How many minutes from now to search for profiles /// List of active profile ids public List GetActiveProfileIdsWithinMinutes(int minutes) { var currentTimestamp = timeUtil.GetTimeStamp(); var result = new List(); foreach (var (sessionId, activeProfile) in _activeProfiles) { // Profile was active in last x minutes, add to return list if (currentTimestamp - activeProfile.LastActive < minutes * 60) { result.Add(sessionId); } } return result; } /// /// Update the timestamp a profile was last observed active /// /// Profile to update public void SetActivityTimestamp(string sessionId) { if(_activeProfiles.TryGetValue(sessionId, out var currentActiveProfile)) { currentActiveProfile.LastActive = timeUtil.GetTimeStamp(); } } }