using System.Collections.Concurrent; using CS2_SimpleAdminApi; namespace CS2_SimpleAdmin.Managers; public static class PlayerPenaltyManager { private static readonly ConcurrentDictionary>> Penalties = new(); /// /// Adds a penalty for a specific player slot and penalty type. /// /// The player slot where the penalty should be applied. /// The type of penalty to apply (e.g. gag, mute, silence). /// The validity expiration date/time of the penalty. /// The duration of the penalty in minutes (0 for permanent). public static void AddPenalty(int slot, PenaltyType penaltyType, DateTime endDateTime, int durationInMinutes) { Penalties.AddOrUpdate(slot, (_) => { Dictionary> dict = new Dictionary> { [penaltyType] = [(endDateTime, durationInMinutes, false)] }; return dict; }, (_, existingDict) => { if (!existingDict.TryGetValue(penaltyType, out List<(DateTime EndDateTime, int Duration, bool Passed)>? value)) { value = new List<(DateTime, int, bool)>(); existingDict[penaltyType] = value; } value.Add((endDateTime, durationInMinutes, false)); return existingDict; }); } /// /// Determines whether a player is currently penalized with the given penalty type. /// /// The player slot to check. /// The penalty type to check. /// The out-parameter returning the end datetime of the penalty if active. /// True if the player has an active penalty, false otherwise. public static bool IsPenalized(int slot, PenaltyType penaltyType, out DateTime? endDateTime) { endDateTime = null; if (!Penalties.TryGetValue(slot, out Dictionary>? penaltyDict) || !penaltyDict.TryGetValue(penaltyType, out List<(DateTime EndDateTime, int Duration, bool Passed)>? penaltiesList)) { return false; } if (CS2_SimpleAdmin.Instance.Config.OtherSettings.TimeMode == 0) { if (penaltiesList.Count == 0) { return false; } endDateTime = penaltiesList.First().EndDateTime; return true; } DateTime now = Time.ActualDateTime(); // Check if any active penalties exist foreach ((DateTime EndDateTime, int Duration, bool Passed) penalty in penaltiesList.ToList()) { // Check if the penalty is still active if (penalty.Duration > 0 && now >= penalty.EndDateTime) { penaltiesList.Remove(penalty); // Remove expired penalty if (penaltiesList.Count == 0) { penaltyDict.Remove(penaltyType); // Remove penalty type if no more penalties exist } } else if (penalty.Duration == 0 || now < penalty.EndDateTime) { // Set endDateTime to the end time of this active penalty endDateTime = penalty.EndDateTime; return true; } } // Return false if no active penalties are found return false; } /// /// Retrieves all penalties for a player of a specific penalty type. /// /// The player slot. /// The penalty type to retrieve. /// A list of penalties if found, otherwise an empty list. public static List<(DateTime EndDateTime, int Duration, bool Passed)> GetPlayerPenalties(int slot, PenaltyType penaltyType) { if (Penalties.TryGetValue(slot, out Dictionary>? penaltyDict) && penaltyDict.TryGetValue(penaltyType, out List<(DateTime EndDateTime, int Duration, bool Passed)>? penaltiesList)) { return penaltiesList; } return []; } /// /// Retrieves all penalties for a player across multiple penalty types. /// /// The player slot. /// A list of penalty types to retrieve. /// A combined list of penalties of all requested types. public static List<(DateTime EndDateTime, int Duration, bool Passed)> GetPlayerPenalties(int slot, List penaltyType) { List<(DateTime EndDateTime, int Duration, bool Passed)> result = []; if (Penalties.TryGetValue(slot, out Dictionary>? penaltyDict)) { foreach (PenaltyType type in penaltyType) { if (penaltyDict.TryGetValue(type, out List<(DateTime EndDateTime, int Duration, bool Passed)>? penaltiesList)) { result.AddRange(penaltiesList); } } } return result; } /// /// Retrieves all penalties for a player across all penalty types. /// /// The player slot. /// A dictionary with penalty types as keys and lists of penalties as values. public static Dictionary> GetAllPlayerPenalties(int slot) { if (Penalties.TryGetValue(slot, out Dictionary>? penaltyDict)) { return penaltyDict; } return new Dictionary>(); } /// /// Checks if a given slot has any penalties assigned. /// /// The player slot. /// True if the player has any penalties, false otherwise. public static bool IsSlotInPenalties(int slot) { return Penalties.ContainsKey(slot); } /// /// Removes all penalties assigned to a specific player slot. /// /// The player slot. public static void RemoveAllPenalties(int slot) { if (Penalties.ContainsKey(slot)) { Penalties.TryRemove(slot, out _); } } /// /// Removes all penalties for all players. /// public static void RemoveAllPenalties() { Penalties.Clear(); } /// /// Removes all penalties of a specific type from a player. /// /// The player slot. /// The penalty type to remove. public static void RemovePenaltiesByType(int slot, PenaltyType penaltyType) { if (Penalties.TryGetValue(slot, out Dictionary>? penaltyDict) && penaltyDict.ContainsKey(penaltyType)) { penaltyDict.Remove(penaltyType); } } /// /// Marks penalties with a specific end datetime as "passed" for a player. /// /// The player slot. /// The end datetime of penalties to mark as passed. public static void RemovePenaltiesByDateTime(int slot, DateTime dateTime) { if (!Penalties.TryGetValue(slot, out Dictionary>? penaltyDict)) { return; } foreach (List<(DateTime EndDateTime, int Duration, bool Passed)> penaltiesList in penaltyDict.Values) { for (int i = 0; i < penaltiesList.Count; i++) { if (penaltiesList[i].EndDateTime != dateTime) { continue; } // Create a copy of the penalty (DateTime EndDateTime, int Duration, bool Passed) penalty = penaltiesList[i]; // Update the end datetime of the copied penalty to the current datetime penalty.Passed = true; // Replace the original penalty with the modified one penaltiesList[i] = penalty; } } } /// /// Removes or expires penalties automatically across all players based on their duration or "passed" flag. /// /// /// If TimeMode == 0, penalties are considered passed manually and are removed if flagged as such. /// Otherwise, expired penalties are removed based on the current datetime compared with their end time. /// public static void RemoveExpiredPenalties() { if (CS2_SimpleAdmin.Instance.Config.OtherSettings.TimeMode == 0) { foreach ((int playerSlot, Dictionary>? penaltyDict) in Penalties.ToList()) // Use ToList to avoid modification while iterating { // Remove expired penalties for the player foreach (List<(DateTime EndDateTime, int Duration, bool Passed)>? penaltiesList in penaltyDict.Values) { penaltiesList.RemoveAll(p => p is { Duration: > 0, Passed: true }); } // Remove player slot if no penalties left if (penaltyDict.Count == 0) { Penalties.TryRemove(playerSlot, out _); } } return; } DateTime now = Time.ActualDateTime(); foreach ((int playerSlot, Dictionary>? penaltyDict) in Penalties.ToList()) // Use ToList to avoid modification while iterating { foreach (List<(DateTime EndDateTime, int Duration, bool Passed)>? penaltiesList in penaltyDict.Values) { penaltiesList.RemoveAll(p => p.Duration > 0 && now >= p.EndDateTime); } if (penaltyDict.Count == 0) { Penalties.TryRemove(playerSlot, out _); } } } }