├── CS2Retake ├── Managers │ ├── Interfaces │ │ ├── IMapManager.cs │ │ ├── IWeaponManager.cs │ │ ├── IPlantManager.cs │ │ ├── IRetakeManager.cs │ │ ├── IRoundTypeManager.cs │ │ ├── IGameRuleManager.cs │ │ └── ITeamManager.cs │ ├── Base │ │ └── BaseManager.cs │ ├── RetakeManager.cs │ ├── RoundTypeManager.cs │ ├── MapManager.cs │ ├── GameRuleManager.cs │ ├── PlantManager.cs │ ├── WeaponManager.cs │ └── TeamManager.cs ├── Utils │ ├── PlantTypeEnum.cs │ ├── PlayerStateEnum.cs │ ├── RoundTypeModeEnum.cs │ ├── AllocatorEnum.cs │ ├── PlayerUtils.cs │ └── MessageUtils.cs ├── Allocators │ ├── Implementations │ │ └── CommandAllocator │ │ │ ├── Utils │ │ │ └── DBType.cs │ │ │ ├── Entities │ │ │ ├── WeaponEntity.cs │ │ │ └── ChanceEntity.cs │ │ │ ├── Configs │ │ │ ├── PistolConfig.cs │ │ │ ├── CommandAllocatorConfig.cs │ │ │ ├── MidConfig.cs │ │ │ └── FullBuyConfig.cs │ │ │ ├── Interfaces │ │ │ └── IRetakeRepository.cs │ │ │ ├── Menus │ │ │ ├── ChooserMenu.cs │ │ │ ├── PistolMenu.cs │ │ │ ├── MidMenu.cs │ │ │ └── FullBuyMenu.cs │ │ │ ├── Manager │ │ │ ├── DBManager.cs │ │ │ └── CacheManager.cs │ │ │ ├── CommandAllocator.cs │ │ │ └── Repository │ │ │ ├── SQLiteRepository.cs │ │ │ └── PostgreSqlRepository.cs │ ├── Exceptions │ │ └── AllocatorException.cs │ └── Factory │ │ └── AllocatorFactory.cs ├── Entities │ ├── RoundTypeSequenceEntity.cs │ ├── GrenadeKitEntity.cs │ ├── WeaponKitEntity.cs │ ├── SpawnPointEntity.cs │ └── MapEntity.cs ├── CS2Retake.csproj └── Configs │ ├── FeatureConfig.cs │ ├── CS2RetakeConfig.cs │ └── RuntimeConfig.cs ├── CS2Retake.sln ├── README.md └── .gitignore /CS2Retake/Managers/Interfaces/IMapManager.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | 7 | namespace CS2Retake.Managers.Interfaces 8 | { 9 | public interface IMapManager 10 | { 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /CS2Retake/Managers/Interfaces/IWeaponManager.cs: -------------------------------------------------------------------------------- 1 | using CounterStrikeSharp.API.Core; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | using System.Text; 6 | using System.Threading.Tasks; 7 | 8 | namespace CS2Retake.Managers.Interfaces 9 | { 10 | public interface IWeaponManager 11 | { 12 | public void OnGunsCommand(CCSPlayerController? player); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /CS2Retake/Utils/PlantTypeEnum.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Text.Json.Serialization; 6 | using System.Threading.Tasks; 7 | 8 | namespace CS2Retake.Utils 9 | { 10 | [JsonConverter(typeof(JsonStringEnumConverter))] 11 | public enum PlantTypeEnum 12 | { 13 | AutoPlant = 0, 14 | FastPlant = 1, 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /CS2Retake/Utils/PlayerStateEnum.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | 7 | namespace CS2Retake.Utils 8 | { 9 | public enum PlayerStateEnum 10 | { 11 | None = 0, 12 | Connecting = 1, 13 | Connected = 2, 14 | Spectating = 3, 15 | Queue = 4, 16 | Playing = 5, 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /CS2Retake/Managers/Interfaces/IPlantManager.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | 7 | namespace CS2Retake.Managers.Interfaces 8 | { 9 | public interface IPlantManager 10 | { 11 | public void HandlePlant(); 12 | 13 | public void HasBombBeenPlanted(); 14 | public void HasBombBeenPlantedCallback(); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /CS2Retake/Utils/RoundTypeModeEnum.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Text.Json.Serialization; 6 | using System.Threading.Tasks; 7 | 8 | namespace CS2Retake.Utils 9 | { 10 | [JsonConverter(typeof(JsonStringEnumConverter))] 11 | public enum RoundTypeModeEnum 12 | { 13 | Random = 0, 14 | Sequence = 1, 15 | Specific = 2, 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /CS2Retake/Utils/AllocatorEnum.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Security.AccessControl; 5 | using System.Text; 6 | using System.Text.Json.Serialization; 7 | using System.Threading.Tasks; 8 | 9 | namespace CS2Retake.Utils 10 | { 11 | [JsonConverter(typeof(JsonStringEnumConverter))] 12 | public enum AllocatorEnum 13 | { 14 | Custom = 0, 15 | Command = 1, 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /CS2Retake/Managers/Base/BaseManager.cs: -------------------------------------------------------------------------------- 1 | using CS2Retake.Utils; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | using System.Text; 6 | using System.Threading.Tasks; 7 | 8 | namespace CS2Retake.Managers.Base 9 | { 10 | public abstract class BaseManager 11 | { 12 | public abstract void ResetForNextRound(bool completeReset = true); 13 | 14 | public abstract void ResetForNextMap(bool completeReset = true); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /CS2Retake/Allocators/Implementations/CommandAllocator/Utils/DBType.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Text.Json.Serialization; 6 | using System.Threading.Tasks; 7 | 8 | namespace CS2Retake.Allocators.Implementations.CommandAllocator.Utils 9 | { 10 | [JsonConverter(typeof(JsonStringEnumConverter))] 11 | public enum DBType 12 | { 13 | Cache = 0, 14 | SQLite = 1, 15 | PostgreSql = 2, 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /CS2Retake/Managers/Interfaces/IRetakeManager.cs: -------------------------------------------------------------------------------- 1 | using CounterStrikeSharp.API.Core; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | using System.Text; 6 | using System.Threading.Tasks; 7 | 8 | namespace CS2Retake.Managers.Interfaces 9 | { 10 | public interface IRetakeManager 11 | { 12 | public void PlaySpotAnnouncer(); 13 | public void AssignRandomPlayerInBombZoneAsPlanter(); 14 | 15 | public void ConfigureForRetake(bool complete = true); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /CS2Retake/Managers/Interfaces/IRoundTypeManager.cs: -------------------------------------------------------------------------------- 1 | using CounterStrikeSharp.API.Modules.Entities.Constants; 2 | using CS2Retake.Utils; 3 | using System; 4 | using System.Collections.Generic; 5 | using System.Linq; 6 | using System.Text; 7 | using System.Threading.Tasks; 8 | using CSZoneNet.Plugin.Utils.Enums; 9 | 10 | namespace CS2Retake.Managers.Interfaces 11 | { 12 | public interface IRoundTypeManager 13 | { 14 | public RoundTypeEnum RoundType { get; } 15 | public void HandleRoundType(); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /CS2Retake/Allocators/Exceptions/AllocatorException.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | 7 | namespace CS2Retake.Allocators.Exceptions 8 | { 9 | public class AllocatorException : Exception 10 | { 11 | public AllocatorException() { } 12 | public AllocatorException(string message) : base(message) { } 13 | public AllocatorException(string message, Exception inner) : base(message, inner) { } 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /CS2Retake/Entities/RoundTypeSequenceEntity.cs: -------------------------------------------------------------------------------- 1 | using CS2Retake.Utils; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | using System.Text; 6 | using System.Threading.Tasks; 7 | using CSZoneNet.Plugin.Utils.Enums; 8 | 9 | namespace CS2Retake.Entities 10 | { 11 | public class RoundTypeSequenceEntity 12 | { 13 | public RoundTypeEnum RoundType { get; set; } = RoundTypeEnum.Undefined; 14 | public int AmountOfRounds { get; set; } = 5; 15 | 16 | public RoundTypeSequenceEntity(RoundTypeEnum roundType, int amountOfRounds) 17 | { 18 | this.RoundType = roundType; 19 | this.AmountOfRounds = amountOfRounds; 20 | } 21 | 22 | public RoundTypeSequenceEntity(){} 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /CS2Retake/Managers/Interfaces/IGameRuleManager.cs: -------------------------------------------------------------------------------- 1 | using CounterStrikeSharp.API.Core; 2 | using CounterStrikeSharp.API.Modules.Entities.Constants; 3 | using System; 4 | using System.Collections.Generic; 5 | using System.Linq; 6 | using System.Text; 7 | using System.Threading.Tasks; 8 | 9 | namespace CS2Retake.Managers.Interfaces 10 | { 11 | public interface IGameRuleManager 12 | { 13 | public CCSGameRules? GameRules { get; set; } 14 | public bool IsWarmup { get; } 15 | public float WarmupEnd { get; } 16 | public bool BombPlanted { get; set; } 17 | public bool BombDefused { get; set; } 18 | public int TotalRoundsPlayed { get; } 19 | 20 | public void TerminateRound(RoundEndReason reason = RoundEndReason.RoundDraw); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /CS2Retake/CS2Retake.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net8.0 5 | enable 6 | enable 7 | true 8 | 2.2.0 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /CS2Retake/Allocators/Implementations/CommandAllocator/Entities/WeaponEntity.cs: -------------------------------------------------------------------------------- 1 | using CounterStrikeSharp.API.Modules.Utils; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | using System.Text; 6 | using System.Threading.Tasks; 7 | 8 | namespace CS2Retake.Allocators.Implementations.CommandAllocator.Entities 9 | { 10 | public class WeaponEntity 11 | { 12 | public string WeaponName { get; set; } 13 | public string WeaponString { get; set; } 14 | public CsTeam Team { get; set; } = CsTeam.None; 15 | 16 | public WeaponEntity() { } 17 | 18 | public WeaponEntity(string weaponName, string weaponString, CsTeam team = CsTeam.None) 19 | { 20 | this.WeaponName = weaponName; 21 | this.WeaponString = weaponString; 22 | this.Team = team; 23 | } 24 | 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /CS2Retake/Managers/Interfaces/ITeamManager.cs: -------------------------------------------------------------------------------- 1 | using CounterStrikeSharp.API.Core; 2 | using CounterStrikeSharp.API.Modules.Utils; 3 | using CS2Retake.Utils; 4 | using System; 5 | using System.Collections.Generic; 6 | using System.Linq; 7 | using System.Text; 8 | using System.Threading.Tasks; 9 | 10 | namespace CS2Retake.Managers.Interfaces 11 | { 12 | public interface ITeamManager 13 | { 14 | public void ScrambleTeams(); 15 | public void AddQueuePlayers(); 16 | public void SwitchTeams(); 17 | 18 | public void FixTeams(); 19 | 20 | public void OnTick(); 21 | 22 | 23 | public void PlayerConnected(CCSPlayerController player); 24 | public void PlayerConnectedFull(CCSPlayerController player); 25 | public void PlayerDisconnected(CCSPlayerController player); 26 | 27 | public void PlayerSwitchTeam(CCSPlayerController player, CsTeam previousTeam, CsTeam newTeam); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /CS2Retake/Allocators/Implementations/CommandAllocator/Entities/ChanceEntity.cs: -------------------------------------------------------------------------------- 1 | using CounterStrikeSharp.API.Modules.Utils; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | using System.Text; 6 | using System.Threading.Tasks; 7 | 8 | namespace CS2Retake.Allocators.Implementations.CommandAllocator.Entities 9 | { 10 | public class ChanceEntity 11 | { 12 | public string WeaponName { get; set; } 13 | public string WeaponString { get; set; } 14 | public CsTeam Team { get; set; } = CsTeam.None; 15 | 16 | public List Chances { get; set; } 17 | 18 | public int Limit { get; set; } = -1; 19 | 20 | public ChanceEntity() { } 21 | 22 | public ChanceEntity(string weaponName, string weaponString, List chances, CsTeam team = CsTeam.None) 23 | { 24 | this.WeaponName = weaponName; 25 | this.WeaponString = weaponString; 26 | this.Chances = chances; 27 | 28 | this.Team = team; 29 | } 30 | 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /CS2Retake/Entities/GrenadeKitEntity.cs: -------------------------------------------------------------------------------- 1 | using CounterStrikeSharp.API.Modules.Utils; 2 | using CS2Retake.Utils; 3 | using CSZoneNet.Plugin.Utils.Enums; 4 | using System; 5 | using System.Collections.Generic; 6 | using System.Linq; 7 | using System.Text; 8 | using System.Text.Json.Serialization; 9 | using System.Threading.Tasks; 10 | 11 | namespace CS2Retake.Entities 12 | { 13 | public class GrenadeKitEntity 14 | { 15 | public string KitName { get; set; } 16 | 17 | public List GrenadeList { get; set; } = new List(); 18 | 19 | //CsTeam.None = Both Teams 20 | public CsTeam Team { get; set; } = CsTeam.None; 21 | 22 | public RoundTypeEnum RoundType { get; set; } = RoundTypeEnum.Undefined; 23 | public int KitLimit { get; set; } = -1; 24 | 25 | 26 | [JsonIgnore] 27 | public int KitUsedAmount { get; set; } = 0; 28 | 29 | [JsonIgnore] 30 | public bool KitLimitReached => this.KitLimit == 0 || this.KitLimit == this.KitUsedAmount; 31 | 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /CS2Retake.sln: -------------------------------------------------------------------------------- 1 | 2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio Version 17 4 | VisualStudioVersion = 17.4.33213.308 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CS2Retake", "CS2Retake\CS2Retake.csproj", "{3B202901-0697-40CE-AD55-FC18A891A1F3}" 7 | EndProject 8 | Global 9 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 10 | Debug|Any CPU = Debug|Any CPU 11 | Release|Any CPU = Release|Any CPU 12 | EndGlobalSection 13 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 14 | {3B202901-0697-40CE-AD55-FC18A891A1F3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 15 | {3B202901-0697-40CE-AD55-FC18A891A1F3}.Debug|Any CPU.Build.0 = Debug|Any CPU 16 | {3B202901-0697-40CE-AD55-FC18A891A1F3}.Release|Any CPU.ActiveCfg = Release|Any CPU 17 | {3B202901-0697-40CE-AD55-FC18A891A1F3}.Release|Any CPU.Build.0 = Release|Any CPU 18 | EndGlobalSection 19 | GlobalSection(SolutionProperties) = preSolution 20 | HideSolutionNode = FALSE 21 | EndGlobalSection 22 | GlobalSection(ExtensibilityGlobals) = postSolution 23 | SolutionGuid = {CE98A4EE-5C9E-4FB8-99F9-4E5DF57D3DF7} 24 | EndGlobalSection 25 | EndGlobal 26 | -------------------------------------------------------------------------------- /CS2Retake/Configs/FeatureConfig.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | 7 | namespace CS2Retake.Configs 8 | { 9 | public static class FeatureConfig 10 | { 11 | public static bool EnableSpotAnnouncer { get; set; } = true; 12 | public static bool EnableQueue { get; set; } = true; 13 | public static bool EnableScramble { get; set; } = true; 14 | public static bool EnableSwitchOnRoundWin { get; set; } = true; 15 | 16 | public static bool EnableThankYouMessage { get; set; } = false; 17 | 18 | public static bool EnableDebug { get; set; } = false; 19 | 20 | public static void SetBaseConfig(CS2RetakeConfig baseConfig) 21 | { 22 | EnableSpotAnnouncer = baseConfig.SpotAnnouncerEnabled; 23 | EnableQueue = baseConfig.EnableQueue; 24 | EnableScramble = baseConfig.EnableScramble; 25 | EnableSwitchOnRoundWin = baseConfig.EnableSwitchOnRoundWin; 26 | 27 | EnableThankYouMessage = baseConfig.EnableThankYouMessage; 28 | 29 | EnableDebug = baseConfig.EnableDebug; 30 | } 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /CS2Retake/Entities/WeaponKitEntity.cs: -------------------------------------------------------------------------------- 1 | using CounterStrikeSharp.API.Modules.Utils; 2 | using CS2Retake.Utils; 3 | using System; 4 | using System.Collections.Generic; 5 | using System.Linq; 6 | using System.Text; 7 | using System.Text.Json.Serialization; 8 | using System.Threading.Tasks; 9 | using CSZoneNet.Plugin.Utils.Enums; 10 | 11 | namespace CS2Retake.Entities 12 | { 13 | public class WeaponKitEntity 14 | { 15 | public string KitName { get; set; } 16 | public string PrimaryWeapon { get; set; } 17 | public string SecondaryWeapon { get; set; } 18 | public bool DefuseKit { get; set; } = true; 19 | public KevlarEnum Kevlar { get; set; } = KevlarEnum.KevlarHelmet; 20 | 21 | //CsTeam.None = Both Teams 22 | public CsTeam Team { get; set; } = CsTeam.None; 23 | 24 | 25 | public RoundTypeEnum RoundType { get; set; } = RoundTypeEnum.Undefined; 26 | 27 | 28 | public int KitLimit { get; set; } = -1; 29 | 30 | [JsonIgnore] 31 | public int KitUsedAmount { get; set; } = 0; 32 | 33 | [JsonIgnore] 34 | public bool KitLimitReached => this.KitLimit == 0 || this.KitLimit == this.KitUsedAmount; 35 | 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /CS2Retake/Allocators/Implementations/CommandAllocator/Configs/PistolConfig.cs: -------------------------------------------------------------------------------- 1 | using CounterStrikeSharp.API.Modules.Utils; 2 | using CS2Retake.Allocators.Implementations.CommandAllocator.Entities; 3 | using CSZoneNet.Plugin.CS2BaseAllocator.Configs.Base; 4 | using System; 5 | using System.Collections.Generic; 6 | using System.Linq; 7 | using System.Text; 8 | using System.Threading.Tasks; 9 | 10 | namespace CS2Retake.Allocators.Implementations.CommandAllocator.Configs 11 | { 12 | public class PistolConfig : BaseAllocatorConfig 13 | { 14 | public List AvailableSecondaries { get; set; } = new List() 15 | { 16 | new WeaponEntity("Deagle", "weapon_deagle"), 17 | new WeaponEntity("P250", "weapon_p250"), 18 | new WeaponEntity("CZ75", "weapon_cz75a"), 19 | new WeaponEntity("Dual Berettas", "weapon_elite"), 20 | 21 | new WeaponEntity("USP-s", "weapon_usp_silencer", CsTeam.CounterTerrorist), 22 | new WeaponEntity("P2000", "weapon_hkp2000", CsTeam.CounterTerrorist), 23 | new WeaponEntity("FiveSeven", "weapon_fiveseven", CsTeam.CounterTerrorist), 24 | 25 | new WeaponEntity("Glock", "weapon_glock", CsTeam.Terrorist), 26 | new WeaponEntity("Tec-9", "weapon_tec9", CsTeam.Terrorist), 27 | }; 28 | 29 | public PistolConfig() 30 | { 31 | this.Version = 1; 32 | } 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /CS2Retake/Allocators/Implementations/CommandAllocator/Interfaces/IRetakeRepository.cs: -------------------------------------------------------------------------------- 1 | using CounterStrikeSharp.API.Core; 2 | using CounterStrikeSharp.API.Modules.Utils; 3 | using System; 4 | using System.Collections.Generic; 5 | using System.Linq; 6 | using System.Text; 7 | using System.Threading.Tasks; 8 | using System.Web; 9 | 10 | namespace CS2Retake.Allocators.Implementations.CommandAllocator.Interfaces 11 | { 12 | public interface IRetakeRepository 13 | { 14 | void Init(); 15 | 16 | bool InsertOrUpdateFullBuyPrimaryWeaponString(ulong userId, string weaponString, int team); 17 | bool InsertOrUpdateFullBuySecondaryWeaponString(ulong userId, string weaponString, int team); 18 | bool InsertOrUpdateFullBuyAWPChance(ulong userId, int chance, int team); 19 | 20 | bool InsertOrUpdateMidPrimaryWeaponString(ulong userId, string weaponString, int team); 21 | bool InsertOrUpdateMidSecondaryWeaponString(ulong userId, string weaponString, int team); 22 | 23 | bool InsertOrUpdatePistolWeaponString(ulong userId, string weaponString, int team); 24 | 25 | 26 | (string? primaryWeapon, string? secondaryWeapon, int? awpChance) GetFullBuyWeapons(ulong userId, int team); 27 | 28 | (string? primaryWeapon, string? secondaryWeapon, int? awpChance) GetMidWeapons(ulong userId, int team); 29 | 30 | (string? primaryWeapon, string? secondaryWeapon, int? awpChance) GetPistolWeapons(ulong userId, int team); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /CS2Retake/Allocators/Implementations/CommandAllocator/Configs/CommandAllocatorConfig.cs: -------------------------------------------------------------------------------- 1 | using CounterStrikeSharp.API.Modules.Entities.Constants; 2 | using CounterStrikeSharp.API.Modules.Utils; 3 | using CS2Retake.Allocators.Implementations.CommandAllocator.Utils; 4 | using CSZoneNet.Plugin.CS2BaseAllocator.Configs.Base; 5 | using System; 6 | using System.Collections.Generic; 7 | using System.Linq; 8 | using System.Text; 9 | using System.Threading.Tasks; 10 | 11 | namespace CS2Retake.Allocators.Implementations.CommandAllocator.Configs 12 | { 13 | public class CommandAllocatorConfig : BaseAllocatorConfig 14 | { 15 | public bool EnableRoundTypePistolMenu { get; set; } = true; 16 | public bool EnableRoundTypeMidMenu { get; set; } = true; 17 | public bool EnableRoundTypeFullBuyMenu { get; set; } = true; 18 | 19 | public int DefuseKitChance { get; set; } = 100; 20 | 21 | public bool EnableZeus { get; set; } = false; 22 | public int ZeusChance { get; set; } = 20; 23 | 24 | public DBType DatabaseType { get; set; } = DBType.SQLite; 25 | 26 | public string ConnectionString { get; set; } = "Server=;Port=;Database=;Userid=;Password="; 27 | 28 | public float HowToMessageDelayInMinutes { get; set; } = 3.5f; 29 | public string HowToMessage { get; set; } = $"Customize your weapons by using !guns"; 30 | 31 | public CommandAllocatorConfig() 32 | { 33 | this.Version = 4; 34 | } 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /CS2Retake/Allocators/Factory/AllocatorFactory.cs: -------------------------------------------------------------------------------- 1 | using CounterStrikeSharp.API.Core; 2 | using CS2Retake.Allocators.Implementations.CommandAllocator; 3 | using CS2Retake.Utils; 4 | using CSZoneNet.Plugin.CS2BaseAllocator.Interfaces; 5 | using Microsoft.Extensions.Logging; 6 | using System; 7 | using System.Collections.Generic; 8 | using System.Linq; 9 | using System.Text; 10 | using System.Threading.Tasks; 11 | 12 | namespace CS2Retake.Allocators.Factory 13 | { 14 | public class AllocatorFactory 15 | { 16 | public IBaseAllocator GetAllocator(AllocatorEnum allocator, IPlugin? pluginInstance = null) 17 | { 18 | IBaseAllocator chosenAllocator; 19 | 20 | switch (allocator) 21 | { 22 | case AllocatorEnum.Command: 23 | chosenAllocator = new CommandAllocator(); 24 | break; 25 | case AllocatorEnum.Custom: 26 | //TODO: Custom Allocator implementation way. 27 | chosenAllocator = new CommandAllocator(); 28 | break; 29 | default: 30 | chosenAllocator = new CommandAllocator(); 31 | break; 32 | } 33 | 34 | chosenAllocator.InitializeConfig(chosenAllocator, chosenAllocator.GetType()); 35 | 36 | if(pluginInstance != null) 37 | { 38 | chosenAllocator.InjectBasePluginInstance(pluginInstance); 39 | } 40 | 41 | return chosenAllocator; 42 | } 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /CS2Retake/Utils/PlayerUtils.cs: -------------------------------------------------------------------------------- 1 | using CounterStrikeSharp.API; 2 | using CounterStrikeSharp.API.Core; 3 | using CounterStrikeSharp.API.Modules.Utils; 4 | using System; 5 | using System.Collections.Generic; 6 | using System.Linq; 7 | using System.Text; 8 | using System.Threading.Tasks; 9 | 10 | namespace CS2Retake.Utils 11 | { 12 | public static class PlayerUtils 13 | { 14 | public static List GetPlayerControllersOfTeam(CsTeam team) 15 | { 16 | var playerList = Utilities.GetPlayers(); 17 | 18 | //Valid players 19 | playerList = playerList.FindAll(x => x != null && x.IsValid && x.PlayerPawn != null && x.PlayerPawn.IsValid && x.PlayerPawn.Value != null && x.PlayerPawn.Value.IsValid); 20 | 21 | //Team specific players 22 | playerList = playerList.FindAll(x => x.TeamNum == (int)team); 23 | 24 | return playerList ?? new List(); 25 | } 26 | 27 | public static List GetCounterTerroristPlayers() => GetPlayerControllersOfTeam(CsTeam.CounterTerrorist); 28 | public static List GetTerroristPlayers() => GetPlayerControllersOfTeam(CsTeam.Terrorist); 29 | 30 | public static List GetValidPlayerControllers() => Utilities.GetPlayers().Where(player => player.PlayerPawn != null && player.PlayerPawn.IsValid && player.PlayerPawn.Value != null && player.PlayerPawn.Value.IsValid).ToList(); 31 | 32 | public static bool AreMoreThenPlayersConnected(int playerCount) => GetValidPlayerControllers().Count() >= playerCount; 33 | 34 | public static bool AreMoreThenOrEqualPlayersConnected(int playerCount) => GetValidPlayerControllers().Count() >= playerCount; 35 | 36 | public static void SuicideAll() => GetValidPlayerControllers().ForEach(x => x.CommitSuicide(true, true)); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /CS2Retake/Configs/CS2RetakeConfig.cs: -------------------------------------------------------------------------------- 1 | using CounterStrikeSharp.API.Core; 2 | using CS2Retake.Entities; 3 | using CS2Retake.Utils; 4 | using System; 5 | using System.Collections.Generic; 6 | using System.Linq; 7 | using System.Text; 8 | using System.Text.Json.Serialization; 9 | using System.Threading.Tasks; 10 | using CSZoneNet.Plugin.Utils.Enums; 11 | 12 | namespace CS2Retake.Configs 13 | { 14 | public class CS2RetakeConfig : BasePluginConfig 15 | { 16 | public PlantTypeEnum PlantType { get; set; } = PlantTypeEnum.AutoPlant; 17 | public RoundTypeModeEnum RoundTypeMode { get; set; } = RoundTypeModeEnum.Sequence; 18 | 19 | public List RoundTypeSequence { get; set; } = new List() 20 | { 21 | new RoundTypeSequenceEntity(RoundTypeEnum.Pistol, 5), 22 | new RoundTypeSequenceEntity(RoundTypeEnum.Mid, 3), 23 | new RoundTypeSequenceEntity(RoundTypeEnum.FullBuy, -1), 24 | }; 25 | 26 | public RoundTypeEnum RoundTypeSpecific { get; set; } = RoundTypeEnum.FullBuy; 27 | 28 | public AllocatorEnum Allocator { get; set; } = AllocatorEnum.Command; 29 | 30 | public float SecondsUntilBombPlantedCheck { get; set; } = 5.0f; 31 | 32 | public bool SpotAnnouncerEnabled { get; set; } = true; 33 | 34 | public bool EnableQueue { get; set; } = true; 35 | public bool EnableScramble { get; set; } = true; 36 | public bool EnableSwitchOnRoundWin { get; set; } = true; 37 | 38 | public int ScrambleAfterSubsequentTerroristRoundWins { get; set; } = 5; 39 | 40 | public int MaxPlayers { get; set; } = 10; 41 | public float TeamBalanceRatio { get; set; } = 0.499f; 42 | 43 | public bool EnableThankYouMessage { get; set; } = false; 44 | 45 | 46 | 47 | 48 | public bool EnableDebug { get; set; } = false; 49 | public CS2RetakeConfig() { 50 | this.Version = 5; 51 | } 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /CS2Retake/Allocators/Implementations/CommandAllocator/Configs/MidConfig.cs: -------------------------------------------------------------------------------- 1 | using CounterStrikeSharp.API.Modules.Utils; 2 | using CS2Retake.Allocators.Implementations.CommandAllocator.Entities; 3 | using CSZoneNet.Plugin.CS2BaseAllocator.Configs.Base; 4 | using System; 5 | using System.Collections.Generic; 6 | using System.Linq; 7 | using System.Text; 8 | using System.Threading.Tasks; 9 | 10 | namespace CS2Retake.Allocators.Implementations.CommandAllocator.Configs 11 | { 12 | public class MidConfig : BaseAllocatorConfig 13 | { 14 | public List AvailablePrimaries { get; set; } = new List() 15 | { 16 | new WeaponEntity("P90", "weapon_p90"), 17 | new WeaponEntity("MP-5", "weapon_mp5sd"), 18 | new WeaponEntity("UMP-45", "weapon_ump45"), 19 | new WeaponEntity("PP-Bizon", "weapon_bizon"), 20 | new WeaponEntity("MP7", "weapon_mp7"), 21 | 22 | new WeaponEntity("MP-9", "weapon_mp9", CsTeam.CounterTerrorist), 23 | 24 | new WeaponEntity("Mac-10", "weapon_mac10", CsTeam.Terrorist), 25 | }; 26 | 27 | public List AvailableSecondaries { get; set; } = new List() 28 | { 29 | new WeaponEntity("Deagle", "weapon_deagle"), 30 | new WeaponEntity("P250", "weapon_p250"), 31 | new WeaponEntity("CZ75", "weapon_cz75a"), 32 | new WeaponEntity("Dual Berettas", "weapon_elite"), 33 | 34 | new WeaponEntity("USP-s", "weapon_usp_silencer", CsTeam.CounterTerrorist), 35 | new WeaponEntity("P2000", "weapon_hkp2000", CsTeam.CounterTerrorist), 36 | new WeaponEntity("FiveSeven", "weapon_fiveseven", CsTeam.CounterTerrorist), 37 | 38 | new WeaponEntity("Glock", "weapon_glock", CsTeam.Terrorist), 39 | new WeaponEntity("Tec-9", "weapon_tec9", CsTeam.Terrorist), 40 | }; 41 | 42 | public MidConfig() 43 | { 44 | this.Version = 1; 45 | } 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /CS2Retake/Utils/MessageUtils.cs: -------------------------------------------------------------------------------- 1 | using CounterStrikeSharp.API; 2 | using CounterStrikeSharp.API.Core; 3 | using CounterStrikeSharp.API.Modules.Utils; 4 | using CS2Retake.Configs; 5 | using Microsoft.Extensions.Logging; 6 | using System.Runtime.CompilerServices; 7 | 8 | namespace CS2Retake.Utils 9 | { 10 | public static class MessageUtils 11 | { 12 | public static ILogger? Logger { get; set; } 13 | 14 | public static string PluginPrefix => $"[{ChatColors.Gold}{RuntimeConfig.ModuleName}{ChatColors.White}]"; 15 | 16 | private static List _thankYouMessages = new List() 17 | { 18 | $"Thank you for using {ChatColors.Gold}CS2Retake{ChatColors.White} :)", 19 | $"If you notice any bugs please report them here:", 20 | $"https://github.com/LordFetznschaedl/CS2Retake/issues", 21 | $"If you want to support the development of this Retake Plugin", 22 | $"https://www.buymeacoffee.com/lordfetznschaedl", 23 | }; 24 | 25 | public static void ThankYouMessage() 26 | { 27 | foreach(var message in _thankYouMessages) 28 | { 29 | Server.PrintToChatAll($"{MessageUtils.PluginPrefix} {message}"); 30 | } 31 | } 32 | 33 | public static void PrintToPlayerOrServer(string message, CCSPlayerController? player = null) 34 | { 35 | if (player != null) 36 | { 37 | message = $"{MessageUtils.PluginPrefix} {message}"; 38 | 39 | player.PrintToConsole(message); 40 | player.PrintToChat(message); 41 | } 42 | else 43 | { 44 | MessageUtils.Log(LogLevel.Information, message); 45 | } 46 | } 47 | 48 | public static void PrintToChatAll(string message) 49 | { 50 | Server.PrintToChatAll($"{MessageUtils.PluginPrefix} {message}"); 51 | } 52 | 53 | public static void Log(LogLevel level, string? message, params object?[] args) 54 | { 55 | Logger?.Log(level, message, args); 56 | } 57 | 58 | public static void LogDebug(string? message, params object?[] args) 59 | { 60 | if(!FeatureConfig.EnableDebug) 61 | { 62 | return; 63 | } 64 | 65 | Logger?.LogInformation(message, args); 66 | } 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /CS2Retake/Configs/RuntimeConfig.cs: -------------------------------------------------------------------------------- 1 | using CS2Retake.Entities; 2 | using CS2Retake.Utils; 3 | using System; 4 | using System.Collections.Generic; 5 | using System.Linq; 6 | using System.Runtime.CompilerServices; 7 | using System.Text; 8 | using System.Threading.Tasks; 9 | using CSZoneNet.Plugin.Utils.Enums; 10 | 11 | namespace CS2Retake.Configs 12 | { 13 | public static class RuntimeConfig 14 | { 15 | public static string ModuleName { get; set; } = string.Empty; 16 | public static string ModuleDirectory { get; set; } = string.Empty; 17 | 18 | public static float SecondsUntilBombPlantedCheck { get; set; } = 5; 19 | 20 | public static int ScrambleAfterSubsequentTerroristRoundWins { get; set; } = 5; 21 | 22 | public static int MaxPlayers { get; set; } = 10; 23 | public static float TeamBalanceRatio { get; set; } = 0.499f; 24 | 25 | public static PlantTypeEnum PlantType { get; set; } = PlantTypeEnum.AutoPlant; 26 | public static RoundTypeModeEnum RoundTypeMode { get; set; } = RoundTypeModeEnum.Sequence; 27 | 28 | public static List RoundTypeSequence { get; set; } = new List() 29 | { 30 | new RoundTypeSequenceEntity(RoundTypeEnum.Pistol, 5), 31 | new RoundTypeSequenceEntity(RoundTypeEnum.Mid, 3), 32 | new RoundTypeSequenceEntity(RoundTypeEnum.FullBuy, -1), 33 | }; 34 | 35 | public static RoundTypeEnum RoundTypeSpecific { get; set; } = RoundTypeEnum.FullBuy; 36 | 37 | public static AllocatorEnum Allocator { get; set; } = AllocatorEnum.Command; 38 | 39 | public static void SetModuleInfo(string moduleName, string moduleDirectory) 40 | { 41 | ModuleName = moduleName; 42 | ModuleDirectory = moduleDirectory; 43 | } 44 | 45 | public static void SetBaseConfig(CS2RetakeConfig baseConfig) 46 | { 47 | SecondsUntilBombPlantedCheck = baseConfig.SecondsUntilBombPlantedCheck; 48 | 49 | ScrambleAfterSubsequentTerroristRoundWins = baseConfig.ScrambleAfterSubsequentTerroristRoundWins; 50 | 51 | MaxPlayers = baseConfig.MaxPlayers; 52 | TeamBalanceRatio = baseConfig.TeamBalanceRatio; 53 | 54 | PlantType = baseConfig.PlantType; 55 | 56 | RoundTypeMode = baseConfig.RoundTypeMode; 57 | RoundTypeSequence = baseConfig.RoundTypeSequence; 58 | RoundTypeSpecific = baseConfig.RoundTypeSpecific; 59 | 60 | Allocator = baseConfig.Allocator; 61 | } 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /CS2Retake/Entities/SpawnPointEntity.cs: -------------------------------------------------------------------------------- 1 | using CounterStrikeSharp.API; 2 | using CounterStrikeSharp.API.Core; 3 | using CounterStrikeSharp.API.Modules.Entities; 4 | using CounterStrikeSharp.API.Modules.Memory; 5 | using CounterStrikeSharp.API.Modules.Utils; 6 | using CS2Retake.Utils; 7 | using System; 8 | using System.Collections.Generic; 9 | using System.Linq; 10 | using System.Text; 11 | using System.Text.Json.Serialization; 12 | using System.Threading.Tasks; 13 | using CSZoneNet.Plugin.Utils.Enums; 14 | 15 | namespace CS2Retake.Entities 16 | { 17 | public class SpawnPointEntity 18 | { 19 | public Guid SpawnId { get; set; } = Guid.Empty; 20 | public CsTeam Team { get; set; } = CsTeam.None; 21 | public BombSiteEnum BombSite { get; set; } = BombSiteEnum.Undefined; 22 | public bool IsInBombZone { get; set; } = false; 23 | 24 | public float PositionX { get; set; } 25 | public float PositionY { get; set; } 26 | public float PositionZ { get; set; } 27 | 28 | public float QAngleX { get; set; } 29 | public float QAngleY { get; set; } 30 | public float QAngleZ { get; set; } 31 | 32 | [JsonIgnore] 33 | public Vector Position => new Vector(this.PositionX, this.PositionY, this.PositionZ); 34 | [JsonIgnore] 35 | public QAngle QAngle => new QAngle(this.QAngleX, this.QAngleY, this.QAngleZ); 36 | 37 | [JsonIgnore] 38 | public CCSPlayerController? SpawnUsedBy { get; set; } = null; 39 | 40 | public SpawnPointEntity(Vector position, QAngle qAngle, BombSiteEnum bombSite, CsTeam team, bool isInBombZone = false) 41 | { 42 | this.PositionX = position.X; 43 | this.PositionY = position.Y; 44 | this.PositionZ = position.Z; 45 | 46 | this.QAngleX = qAngle.X; 47 | this.QAngleY = qAngle.Y; 48 | this.QAngleZ = qAngle.Z; 49 | 50 | this.Team = team; 51 | this.BombSite = bombSite; 52 | this.IsInBombZone = isInBombZone; 53 | 54 | this.SpawnId= Guid.NewGuid(); 55 | } 56 | 57 | public SpawnPointEntity(float positionX, float positionY, float positionZ, float qAngleX, float qAngleY, float qAngleZ, CsTeam team, BombSiteEnum bombSite) 58 | { 59 | this.PositionX = positionX; 60 | this.PositionY = positionY; 61 | this.PositionZ = positionZ; 62 | 63 | this.QAngleX = qAngleX; 64 | this.QAngleY = qAngleY; 65 | this.QAngleZ = qAngleZ; 66 | 67 | this.Team = team; 68 | this.BombSite = bombSite; 69 | } 70 | 71 | public SpawnPointEntity() 72 | { 73 | } 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /CS2Retake/Allocators/Implementations/CommandAllocator/Configs/FullBuyConfig.cs: -------------------------------------------------------------------------------- 1 | using CounterStrikeSharp.API.Modules.Utils; 2 | using CS2Retake.Allocators.Implementations.CommandAllocator.Entities; 3 | using CSZoneNet.Plugin.CS2BaseAllocator.Configs.Base; 4 | 5 | 6 | namespace CS2Retake.Allocators.Implementations.CommandAllocator.Configs 7 | { 8 | public class FullBuyConfig : BaseAllocatorConfig 9 | { 10 | public List AvailablePrimaries { get; set; } = new List() 11 | { 12 | new WeaponEntity("M4A4", "weapon_m4a1", CsTeam.CounterTerrorist), 13 | new WeaponEntity("M4A1-s", "weapon_m4a1_silencer", CsTeam.CounterTerrorist), 14 | new WeaponEntity("Famas", "weapon_famas", CsTeam.CounterTerrorist), 15 | new WeaponEntity("AUG", "weapon_aug", CsTeam.CounterTerrorist), 16 | 17 | new WeaponEntity("AK-47", "weapon_ak47", CsTeam.Terrorist), 18 | new WeaponEntity("Galil", "weapon_galilar", CsTeam.Terrorist), 19 | new WeaponEntity("SG-553", "weapon_sg556", CsTeam.Terrorist), 20 | 21 | }; 22 | 23 | public List AvailableSecondaries { get; set; } = new List() 24 | { 25 | new WeaponEntity("Deagle", "weapon_deagle"), 26 | new WeaponEntity("P250", "weapon_p250"), 27 | new WeaponEntity("CZ75", "weapon_cz75a"), 28 | new WeaponEntity("Dual Berettas", "weapon_elite"), 29 | 30 | new WeaponEntity("USP-s", "weapon_usp_silencer", CsTeam.CounterTerrorist), 31 | new WeaponEntity("P2000", "weapon_hkp2000", CsTeam.CounterTerrorist), 32 | new WeaponEntity("FiveSeven", "weapon_fiveseven", CsTeam.CounterTerrorist), 33 | 34 | new WeaponEntity("Glock", "weapon_glock", CsTeam.Terrorist), 35 | new WeaponEntity("Tec-9", "weapon_tec9", CsTeam.Terrorist), 36 | }; 37 | 38 | public ChanceEntity AWPChanceCT { get; set; } = new ChanceEntity() 39 | { 40 | Team = CsTeam.CounterTerrorist, 41 | WeaponName = "AWP", 42 | WeaponString = "weapon_awp", 43 | Limit = 1, 44 | Chances = new List() { 10, 20, 30, 40, 50 }, 45 | }; 46 | 47 | public ChanceEntity AWPChanceT { get; set; } = new ChanceEntity() 48 | { 49 | Team = CsTeam.Terrorist, 50 | WeaponName = "AWP", 51 | WeaponString = "weapon_awp", 52 | Limit = 1, 53 | Chances = new List() { 10, 20, 30, 40, 50 }, 54 | }; 55 | 56 | public bool EnableAWPChance { get; set; } = true; 57 | 58 | public FullBuyConfig() 59 | { 60 | this.Version = 1; 61 | } 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /CS2Retake/Allocators/Implementations/CommandAllocator/Menus/ChooserMenu.cs: -------------------------------------------------------------------------------- 1 | using CounterStrikeSharp.API.Core; 2 | using CounterStrikeSharp.API.Modules.Menu; 3 | using CounterStrikeSharp.API.Modules.Utils; 4 | using System; 5 | using System.Collections.Generic; 6 | using System.Linq; 7 | using System.Runtime.CompilerServices; 8 | using System.Text; 9 | using System.Threading.Tasks; 10 | 11 | namespace CS2Retake.Allocators.Implementations.CommandAllocator.Menus 12 | { 13 | public static class ChooserMenu 14 | { 15 | public static void OpenMenu(CCSPlayerController? player, IPlugin pluginInstance, bool enableRoundTypePistolMenu = true, bool enableRoundTypeMidMenu = true, bool enableRoundTypeFullBuyMenu = true) 16 | { 17 | var plugin = pluginInstance as BasePlugin; 18 | 19 | if(plugin == null) 20 | { 21 | return; 22 | } 23 | 24 | if(player == null || !player.IsValid || player.PlayerPawn == null || !player.PlayerPawn.IsValid || player.PlayerPawn.Value == null || !player.PlayerPawn.Value.IsValid) 25 | { 26 | return; 27 | } 28 | 29 | ChatMenu menu = new ChatMenu("Gun Menu"); 30 | 31 | if(enableRoundTypePistolMenu) 32 | { 33 | menu.AddMenuOption("T_Pistol", OnSelect); 34 | menu.AddMenuOption("CT_Pistol", OnSelect); 35 | } 36 | 37 | if(enableRoundTypeMidMenu) 38 | { 39 | menu.AddMenuOption("T_Mid", OnSelect); 40 | menu.AddMenuOption("CT_Mid", OnSelect); 41 | } 42 | 43 | if(enableRoundTypeFullBuyMenu) 44 | { 45 | menu.AddMenuOption("T_FullBuy", OnSelect); 46 | menu.AddMenuOption("CT_FullBuy", OnSelect); 47 | } 48 | 49 | MenuManager.OpenChatMenu(player, menu); 50 | } 51 | 52 | private static void OnSelect(CCSPlayerController player, ChatMenuOption chatMenuOption) 53 | { 54 | var siteString = chatMenuOption?.Text?.Split('_')?.FirstOrDefault()?.ToUpper() ?? string.Empty; 55 | var roundTypeString = chatMenuOption?.Text?.Split('_')?.LastOrDefault()?.ToUpper() ?? string.Empty; 56 | 57 | CsTeam team = CsTeam.None; 58 | 59 | switch(siteString) 60 | { 61 | case "CT": 62 | team = CsTeam.CounterTerrorist; 63 | break; 64 | case "T": 65 | team = CsTeam.Terrorist; 66 | break; 67 | default: 68 | return; 69 | } 70 | 71 | if(team != CsTeam.Terrorist && team != CsTeam.CounterTerrorist) 72 | { 73 | return; 74 | } 75 | 76 | 77 | MenuManager.CloseActiveMenu(player); 78 | switch (roundTypeString) 79 | { 80 | case "PISTOL": 81 | PistolMenu.Instance.OpenSecondaryMenu(player, team); 82 | break; 83 | case "MID": 84 | MidMenu.Instance.OpenPrimaryMenu(player, team); 85 | break; 86 | case "FULLBUY": 87 | FullBuyMenu.Instance.OpenPrimaryMenu(player, team); 88 | break; 89 | default: 90 | return; 91 | } 92 | } 93 | 94 | } 95 | } 96 | -------------------------------------------------------------------------------- /CS2Retake/Entities/MapEntity.cs: -------------------------------------------------------------------------------- 1 | using CounterStrikeSharp.API.Modules.Utils; 2 | using CS2Retake.Utils; 3 | using System; 4 | using System.Collections.Generic; 5 | using System.Linq; 6 | using System.Runtime.Serialization; 7 | using System.Text; 8 | using System.Threading.Tasks; 9 | using System.Text.Json; 10 | using System.Text.Json.Serialization; 11 | using CounterStrikeSharp.API.Core; 12 | using CS2Retake.Configs; 13 | using Microsoft.Extensions.Logging; 14 | using CSZoneNet.Plugin.Utils.Enums; 15 | 16 | namespace CS2Retake.Entities 17 | { 18 | public class MapEntity 19 | { 20 | public string MapName { get; set; } 21 | 22 | public List SpawnPoints { get; set; } = new List(); 23 | public Queue PlayerQueue { get; set; } = new Queue(); 24 | 25 | private string _moduleDirectory { get; set; } 26 | 27 | 28 | public MapEntity(string mapName, string moduleDirectoy) 29 | { 30 | this.MapName = mapName; 31 | this._moduleDirectory = moduleDirectoy; 32 | } 33 | 34 | public SpawnPointEntity? GetRandomSpawn(CsTeam team, BombSiteEnum bombSite, bool hasToBeInBombZone) 35 | { 36 | if(!this.SpawnPoints.Any()) 37 | { 38 | this.LoadSpawns(); 39 | } 40 | 41 | var spawnChoices = this.SpawnPoints.Where(x => x.Team == team && x.SpawnUsedBy == null && x.BombSite == bombSite).ToList(); 42 | 43 | if(!spawnChoices.Any()) 44 | { 45 | MessageUtils.Log(LogLevel.Warning,$"No spawn choices found."); 46 | return null; 47 | } 48 | 49 | if(team == CsTeam.Terrorist && hasToBeInBombZone) 50 | { 51 | spawnChoices = spawnChoices.Where(x => x.IsInBombZone).ToList(); 52 | 53 | if (!spawnChoices.Any()) 54 | { 55 | MessageUtils.Log(LogLevel.Warning, $"hasToBeInBombZone has not found any spawns."); 56 | return null; 57 | } 58 | } 59 | 60 | var random = new Random(); 61 | return spawnChoices.OrderBy(x => random.Next()).FirstOrDefault(); 62 | } 63 | 64 | 65 | 66 | public void LoadSpawns() 67 | { 68 | var path = this.GetPath(); 69 | 70 | if(!File.Exists(path)) 71 | { 72 | return; 73 | } 74 | 75 | var jsonSpawnPoints = File.ReadAllText(path); 76 | 77 | if(string.IsNullOrEmpty(jsonSpawnPoints)) 78 | { 79 | return; 80 | } 81 | 82 | this.SpawnPoints = JsonSerializer.Deserialize>(jsonSpawnPoints) ?? new List(); 83 | } 84 | 85 | public void SaveSpawns() 86 | { 87 | this.SpawnPoints.Where(spawnPoint => spawnPoint.SpawnId == Guid.Empty).ToList().ForEach(spawnPoint => spawnPoint.SpawnId = Guid.NewGuid()); 88 | 89 | File.WriteAllText(this.GetPath(), JsonSerializer.Serialize(this.SpawnPoints)); 90 | } 91 | 92 | private string GetPath() 93 | { 94 | var path = Path.Join(this._moduleDirectory, $"spawns"); 95 | 96 | if(!Path.Exists(path)) 97 | { 98 | Directory.CreateDirectory(path); 99 | } 100 | 101 | path = Path.Join(path, $"{this.MapName}.json"); 102 | 103 | return path; 104 | } 105 | 106 | public void ResetSpawnInUse() 107 | { 108 | this.SpawnPoints.ForEach(spawn => spawn.SpawnUsedBy = null); 109 | } 110 | } 111 | } 112 | -------------------------------------------------------------------------------- /CS2Retake/Managers/RetakeManager.cs: -------------------------------------------------------------------------------- 1 | using CounterStrikeSharp.API.Core; 2 | using CounterStrikeSharp.API; 3 | using CounterStrikeSharp.API.Modules.Utils; 4 | using CS2Retake.Utils; 5 | using CS2Retake.Managers.Base; 6 | using Microsoft.Extensions.Logging; 7 | using CS2Retake.Managers.Interfaces; 8 | using CounterStrikeSharp.API.Modules.Entities; 9 | using CS2Retake.Configs; 10 | 11 | namespace CS2Retake.Managers 12 | { 13 | public class RetakeManager : BaseManager, IRetakeManager 14 | { 15 | private static RetakeManager? _instance = null; 16 | public bool BombHasBeenPlanted { get; set; } = false; 17 | 18 | private CCSPlayerController? _planterPlayerController = null; 19 | 20 | public CCSPlayerController? PlanterPlayerController 21 | { 22 | get 23 | { 24 | 25 | return this._planterPlayerController; 26 | } 27 | } 28 | 29 | 30 | 31 | public static RetakeManager Instance 32 | { 33 | get 34 | { 35 | if (_instance == null) 36 | { 37 | _instance = new RetakeManager(); 38 | } 39 | return _instance; 40 | } 41 | } 42 | 43 | private RetakeManager() { } 44 | 45 | public void AssignRandomPlayerInBombZoneAsPlanter() 46 | { 47 | 48 | var random = new Random(); 49 | var plantSpawn = MapManager.Instance.CurrentMap?.SpawnPoints.Where(spawn => spawn.SpawnUsedBy != null && spawn.IsInBombZone).OrderBy(x => random.Next()).FirstOrDefault(); 50 | 51 | 52 | if(plantSpawn == null) 53 | { 54 | MessageUtils.Log(LogLevel.Warning,$"No valid plant spawn found! This might be because no player is on terrorist team."); 55 | return; 56 | } 57 | 58 | if(plantSpawn.SpawnUsedBy == null) 59 | { 60 | MessageUtils.Log(LogLevel.Error, $"Spawn is not used by any player"); 61 | return; 62 | } 63 | 64 | this._planterPlayerController = plantSpawn.SpawnUsedBy; 65 | 66 | if(this._planterPlayerController == null) 67 | { 68 | MessageUtils.Log(LogLevel.Error, $"Player that uses the valid plant spawn is null"); 69 | return; 70 | } 71 | 72 | if (RuntimeConfig.PlantType == PlantTypeEnum.AutoPlant) 73 | { 74 | return; 75 | } 76 | 77 | this._planterPlayerController.GiveNamedItem("weapon_c4"); 78 | this._planterPlayerController.ExecuteClientCommand("slot5"); 79 | 80 | 81 | 82 | if (RuntimeConfig.SecondsUntilBombPlantedCheck > 0) 83 | { 84 | this._planterPlayerController.PrintToCenter($"YOU HAVE {ChatColors.DarkRed}{RuntimeConfig.SecondsUntilBombPlantedCheck}{ChatColors.White} SECONDS TO PLANT THE BOMB!"); 85 | } 86 | } 87 | 88 | 89 | public void PlaySpotAnnouncer() 90 | { 91 | var bombsite = MapManager.Instance.BombSite; 92 | 93 | foreach(var player in Utilities.GetPlayers().FindAll(x => x.TeamNum == (int)CsTeam.CounterTerrorist)) 94 | { 95 | player.ExecuteClientCommand($"play sounds/vo/agents/seal_epic/loc_{bombsite.ToString().ToLower()}_01"); 96 | } 97 | } 98 | 99 | public void ConfigureForRetake(bool complete = true) 100 | { 101 | Server.ExecuteCommand($"execifexists cs2retake/retake.cfg"); 102 | } 103 | 104 | public override void ResetForNextRound(bool completeReset = true) 105 | { 106 | if (completeReset) 107 | { 108 | 109 | } 110 | 111 | this.BombHasBeenPlanted = false; 112 | } 113 | 114 | public override void ResetForNextMap(bool completeReset = true) 115 | { 116 | 117 | } 118 | } 119 | } 120 | -------------------------------------------------------------------------------- /CS2Retake/Allocators/Implementations/CommandAllocator/Menus/PistolMenu.cs: -------------------------------------------------------------------------------- 1 | using CounterStrikeSharp.API.Core; 2 | using CounterStrikeSharp.API.Modules.Entities; 3 | using CounterStrikeSharp.API.Modules.Menu; 4 | using CounterStrikeSharp.API.Modules.Utils; 5 | using CS2Retake.Allocators.Implementations.CommandAllocator.Configs; 6 | using CS2Retake.Allocators.Implementations.CommandAllocator.Manager; 7 | using CS2Retake.Managers; 8 | using CS2Retake.Utils; 9 | using CSZoneNet.Plugin.CS2BaseAllocator.Configs; 10 | using Microsoft.VisualBasic; 11 | using System; 12 | using System.Collections.Generic; 13 | using System.Linq; 14 | using System.Text; 15 | using System.Threading.Tasks; 16 | 17 | namespace CS2Retake.Allocators.Implementations.CommandAllocator.Menus 18 | { 19 | public class PistolMenu 20 | { 21 | private static PistolConfig _config { get; set; } = new PistolConfig(); 22 | 23 | 24 | private static PistolMenu? _instance = null; 25 | public static PistolMenu Instance 26 | { 27 | get 28 | { 29 | if (_instance == null) 30 | { 31 | _instance = new PistolMenu(); 32 | } 33 | return _instance; 34 | } 35 | } 36 | 37 | private PistolMenu() 38 | { 39 | var config = AllocatorConfigManager.Load("CommandAllocator", "Pistol"); 40 | 41 | if (config == null) 42 | { 43 | MessageUtils.Log(Microsoft.Extensions.Logging.LogLevel.Error, $"The Pistol configuration could not be parsed!"); 44 | return; 45 | } 46 | 47 | if (_config.Version > config.Version) 48 | { 49 | MessageUtils.Log(Microsoft.Extensions.Logging.LogLevel.Warning, $"The Pistol configuration is out of date. Consider updating the config. [Current Version: {config.Version} - Pistol Version: {_config.Version}]"); 50 | } 51 | 52 | _config = config; 53 | } 54 | 55 | public void OpenSecondaryMenu(CCSPlayerController player, CsTeam team) 56 | { 57 | ChatMenu menu = new ChatMenu("Pistol Menu"); 58 | 59 | var teamString = team == CsTeam.CounterTerrorist ? "CT" : "T"; 60 | 61 | foreach (var secondary in _config.AvailableSecondaries.FindAll(x => x.Team == team || x.Team == CsTeam.None)) 62 | { 63 | menu.AddMenuOption($"{teamString}_{secondary.WeaponName}", OnSelectSecondary); 64 | } 65 | 66 | MenuManager.OpenChatMenu(player, menu); 67 | } 68 | 69 | private static void OnSelectSecondary(CCSPlayerController player, ChatMenuOption chatMenuOption) 70 | { 71 | var menuText = chatMenuOption.Text?.Split('_')?.LastOrDefault() ?? string.Empty; 72 | var weaponString = _config.AvailableSecondaries.FirstOrDefault(x => x.WeaponName.Equals(menuText))?.WeaponString; 73 | 74 | if (string.IsNullOrWhiteSpace(weaponString)) 75 | { 76 | return; 77 | } 78 | 79 | var team = GetTeam(chatMenuOption.Text ?? string.Empty); 80 | 81 | MessageUtils.PrintToPlayerOrServer($"You have now selected {ChatColors.Green}{menuText}{ChatColors.White} as your pistol for {ChatColors.Green}Pistol{ChatColors.White} rounds!", player); 82 | 83 | MenuManager.CloseActiveMenu(player); 84 | CacheManager.Instance.AddOrUpdatePistolCache(player, weaponString, team); 85 | DBManager.Instance.InsertOrUpdatePistolWeaponString(player.SteamID, weaponString, (int)team); 86 | } 87 | 88 | private static CsTeam GetTeam(string weaponString) 89 | { 90 | var teamString = weaponString?.Split('_')?.FirstOrDefault()?.ToUpper() ?? string.Empty; 91 | 92 | CsTeam team = CsTeam.None; 93 | 94 | switch (teamString) 95 | { 96 | case "CT": 97 | return CsTeam.CounterTerrorist; 98 | case "T": 99 | return CsTeam.Terrorist; 100 | default: 101 | return CsTeam.None; 102 | } 103 | 104 | } 105 | } 106 | } 107 | -------------------------------------------------------------------------------- /CS2Retake/Managers/RoundTypeManager.cs: -------------------------------------------------------------------------------- 1 | using CounterStrikeSharp.API.Modules.Cvars; 2 | using CS2Retake.Configs; 3 | using CS2Retake.Managers.Base; 4 | using CS2Retake.Managers.Interfaces; 5 | using CS2Retake.Utils; 6 | using System; 7 | using System.Collections.Generic; 8 | using System.Linq; 9 | using System.Text; 10 | using System.Threading.Tasks; 11 | using CSZoneNet.Plugin.Utils.Enums; 12 | 13 | namespace CS2Retake.Managers 14 | { 15 | public class RoundTypeManager : BaseManager, IRoundTypeManager 16 | { 17 | private static RoundTypeManager? _instance = null; 18 | 19 | public RoundTypeEnum RoundType { get; private set; } = RoundTypeEnum.Undefined; 20 | private List _roundTypeList = new List(); 21 | 22 | public static RoundTypeManager Instance 23 | { 24 | get 25 | { 26 | if (_instance == null) 27 | { 28 | _instance = new RoundTypeManager(); 29 | } 30 | return _instance; 31 | } 32 | } 33 | private RoundTypeManager() { } 34 | 35 | 36 | public void HandleRoundType() 37 | { 38 | switch (RuntimeConfig.RoundTypeMode) 39 | { 40 | case RoundTypeModeEnum.Random: 41 | this.HandleRandomRoundType(); 42 | break; 43 | case RoundTypeModeEnum.Specific: 44 | this.HandleSpecificRoundType(); 45 | break; 46 | case RoundTypeModeEnum.Sequence: 47 | this.HandleSequenceRoundType(); 48 | break; 49 | default: 50 | this.HandleRandomRoundType(); 51 | break; 52 | } 53 | } 54 | 55 | public void ResetSequenceRoundType() 56 | { 57 | this._roundTypeList.Clear(); 58 | this.CreateRoundTypeList(); 59 | } 60 | 61 | private void HandleRandomRoundType() 62 | { 63 | this.RoundType = (RoundTypeEnum)new Random().Next(0, Enum.GetNames(typeof(RoundTypeEnum)).Length - 1); 64 | } 65 | 66 | private void HandleSpecificRoundType() 67 | { 68 | this.RoundType = RuntimeConfig.RoundTypeSpecific; 69 | } 70 | 71 | private void HandleSequenceRoundType() 72 | { 73 | if (!this._roundTypeList.Any()) 74 | { 75 | this.CreateRoundTypeList(); 76 | 77 | } 78 | 79 | var totalRoundsPlayed = GameRuleManager.Instance.TotalRoundsPlayed; 80 | var maxRounds = ConVar.Find("mp_maxrounds")!.GetPrimitiveValue(); 81 | 82 | MessageUtils.LogDebug($"TotalRoundsPlayed: {totalRoundsPlayed} - MaxRounds: {maxRounds}"); 83 | 84 | if(totalRoundsPlayed > maxRounds || totalRoundsPlayed > this._roundTypeList.Count) 85 | { 86 | return; 87 | } 88 | 89 | this.RoundType = this._roundTypeList.ElementAt(totalRoundsPlayed); 90 | } 91 | 92 | private void CreateRoundTypeList() 93 | { 94 | var maxRounds = ConVar.Find("mp_maxrounds")!.GetPrimitiveValue(); 95 | 96 | MessageUtils.LogDebug($"MaxRounds: {maxRounds}"); 97 | 98 | var roundTypeSequence = RuntimeConfig.RoundTypeSequence; 99 | 100 | foreach (var roundType in roundTypeSequence) 101 | { 102 | var roundsToAddToQueue = roundType.AmountOfRounds; 103 | 104 | if (roundType.AmountOfRounds < 0) 105 | { 106 | roundsToAddToQueue = maxRounds - this._roundTypeList.Count + 1; 107 | } 108 | 109 | for (int i = 0; i < roundsToAddToQueue; i++) 110 | { 111 | this._roundTypeList.Add(roundType.RoundType); 112 | } 113 | } 114 | } 115 | 116 | public override void ResetForNextRound(bool completeReset = true) 117 | { 118 | if (completeReset) 119 | { 120 | 121 | } 122 | 123 | } 124 | 125 | public override void ResetForNextMap(bool completeReset = true) 126 | { 127 | if (completeReset) 128 | { 129 | this.RoundType = RuntimeConfig.RoundTypeSpecific; 130 | this._roundTypeList.Clear(); 131 | } 132 | } 133 | } 134 | } 135 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # CS2Retake 2.2.0 2 | 3 | Implementation of a Retake plugin for CS2 using CounterStrikeSharp 4 | 5 | 6 | --- 7 | # reuirements: 8 | - min. CounterStrikeSharp API Version: 228 9 | - GameType: Casual or Competitive 10 | 11 | --- 12 | # usage: 13 | | Command | Parameter | Description | Permissions | 14 | |-----------------|--------------------------------------------|-----------------------------------------------------------------------|------------------| 15 | | !guns | | opens up the gun menu for the allocator system | | 16 | | !retakeinfo | | prints the plugin information | | 17 | | !retakespawn | | teleports the player to the given spawn index | @cs2retake/admin | 18 | | !retakewrite | | writes the spawns of the current map to the corresponding map config | @cs2retake/admin | 19 | | !retakeread | | reads the spawns of the current map from the corresponding map config | @cs2retake/admin | 20 | | !retakescramble | | scrambles the teams after the round is finished | @cs2retake/admin | 21 | | !retaketeleport | | teleports player to the given coordinates | @cs2retake/admin | 22 | | !retakeaddspawn | <2/3 - 2 = T; 3 = CT> <0/1 - 0 = A; 1 = B> | creates a new spawn | @cs2retake/admin | 23 | 24 | --- 25 | # installation: 26 | Extract the `addons` folder to the `/csgo/` directory of the dedicated server. 27 | 28 | --- 29 | # release 2.2.0: 30 | - [x] modular weapon allocator system 31 | - [x] creating, saving and reading spawns 32 | - [x] player spawn in spawnpoints 33 | - [x] scramble teams 34 | - [x] basic autoplant (fast plant, player needs to plant himself) 35 | - [x] assigning permissions for the commands 36 | - [x] spawn loading system 37 | - [x] weapon allocator system (kit based) 38 | - [x] nade allocator system (kit based) 39 | - [x] on ct win -> switch cts to t and the ts to ct 40 | - [x] auto assign teams -> deny choosing team -> switch team automatically 41 | - [x] KevlarHelmet being only given as Kevlar without Helmet 42 | - [x] config system 43 | - [x] auto plant -> changable to fast plant if prefered in plugin base config 44 | 45 | --- 46 | # future releases: 47 | - [ ] editor system for spawns 48 | - [ ] change scramble command to do the scramble after round ends and before round starts 49 | - [ ] multi language support 50 | 51 | --- 52 | # plugin base config 53 | location: addons\counterstrikesharp\configs\plugins\CS2Retake.json 54 | ``` 55 | { 56 | //PlantType Options: AutoPlant, FastPlant 57 | "PlantType": "AutoPlant", 58 | 59 | //RoundTypeMode Options: Sequence, Specific, Random 60 | "RoundTypeMode": "Sequence", 61 | 62 | //Configuration for RoundTypeMode Sequence 63 | //Played from top to bottom 64 | //AmountOfRounds -1 is for all remaining rounds in the map 65 | //Available RoundType Options: FullBuy, Pistol, Mid, Undefined 66 | "RoundTypeSequence": [ 67 | { 68 | "RoundType": "Pistol", 69 | "AmountOfRounds": 5 70 | }, 71 | { 72 | "RoundType": "Mid", 73 | "AmountOfRounds": 3 74 | }, 75 | { 76 | "RoundType": "FullBuy", 77 | "AmountOfRounds": -1 78 | } 79 | ], 80 | 81 | //RoundTypeSpecific is for a non changing roundtype 82 | //This will only work if RoundTypeMode is Specific 83 | //RoundTypeSpecific Options: FullBuy, Pistol, Mid, Undefined 84 | "RoundTypeSpecific": "FullBuy", 85 | 86 | //Change this to change the way how the allocator system works 87 | //More allocators coming in the future 88 | //Allocator Options: Command 89 | "Allocator": "Command", 90 | 91 | "SecondsUntilBombPlantedCheck": 5, 92 | "SpotAnnouncerEnabled": true, 93 | "EnableQueue": true, 94 | "EnableScramble": true, 95 | "EnableSwitchOnRoundWin": true, 96 | "ScrambleAfterSubsequentTerroristRoundWins": 5, 97 | "MaxPlayers": 10, 98 | "TeamBalanceRatio": 0.499, 99 | "EnableThankYouMessage": false, 100 | "EnableDebug": false, 101 | "ConfigVersion": 5 102 | } 103 | ``` 104 | 105 | --- 106 | # special thanks: 107 | [splewis](https://github.com/splewis): This plugin is inspired by [his retake plugin for CSGO](https://github.com/splewis/csgo-retakes) 108 | 109 | Discord: 110 | - @gorwok for creating spawns on de_mirage 111 | - @cowhitface for creating spawns on de_overpass, de_inferno, de_vertigo, de_nuke, de_anubis & de_ancient 112 | 113 | Twitter: 114 | - @t1ckrate for helping moderate the issues on this repo 115 | 116 | 117 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ## Ignore Visual Studio temporary files, build results, and 2 | ## files generated by popular Visual Studio add-ons. 3 | 4 | # User-specific files 5 | *.suo 6 | *.user 7 | *.userosscache 8 | *.sln.docstates 9 | 10 | # User-specific files (MonoDevelop/Xamarin Studio) 11 | *.userprefs 12 | 13 | # Build results 14 | [Dd]ebug/ 15 | [Dd]ebugPublic/ 16 | [Rr]elease/ 17 | [Rr]eleases/ 18 | [Xx]64/ 19 | [Xx]86/ 20 | [Bb]uild/ 21 | bld/ 22 | [Bb]in/ 23 | [Oo]bj/ 24 | 25 | # Visual Studio 2015 cache/options directory 26 | .vs/ 27 | # Uncomment if you have tasks that create the project's static files in wwwroot 28 | #wwwroot/ 29 | 30 | # MSTest test Results 31 | [Tt]est[Rr]esult*/ 32 | [Bb]uild[Ll]og.* 33 | 34 | # NUNIT 35 | *.VisualState.xml 36 | TestResult.xml 37 | 38 | # Build Results of an ATL Project 39 | [Dd]ebugPS/ 40 | [Rr]eleasePS/ 41 | dlldata.c 42 | 43 | # DNX 44 | project.lock.json 45 | artifacts/ 46 | 47 | *_i.c 48 | *_p.c 49 | *_i.h 50 | *.ilk 51 | *.meta 52 | *.obj 53 | *.pch 54 | *.pdb 55 | *.pgc 56 | *.pgd 57 | *.rsp 58 | *.sbr 59 | *.tlb 60 | *.tli 61 | *.tlh 62 | *.tmp 63 | *.tmp_proj 64 | *.log 65 | *.vspscc 66 | *.vssscc 67 | .builds 68 | *.pidb 69 | *.svclog 70 | *.scc 71 | 72 | # Chutzpah Test files 73 | _Chutzpah* 74 | 75 | # Visual C++ cache files 76 | ipch/ 77 | *.aps 78 | *.ncb 79 | *.opendb 80 | *.opensdf 81 | *.sdf 82 | *.cachefile 83 | *.VC.db 84 | 85 | # Visual Studio profiler 86 | *.psess 87 | *.vsp 88 | *.vspx 89 | *.sap 90 | 91 | # TFS 2012 Local Workspace 92 | $tf/ 93 | 94 | # Guidance Automation Toolkit 95 | *.gpState 96 | 97 | # ReSharper is a .NET coding add-in 98 | _ReSharper*/ 99 | *.[Rr]e[Ss]harper 100 | *.DotSettings.user 101 | 102 | # JustCode is a .NET coding add-in 103 | .JustCode 104 | 105 | # TeamCity is a build add-in 106 | _TeamCity* 107 | 108 | # DotCover is a Code Coverage Tool 109 | *.dotCover 110 | 111 | # NCrunch 112 | _NCrunch_* 113 | .*crunch*.local.xml 114 | nCrunchTemp_* 115 | 116 | # MightyMoose 117 | *.mm.* 118 | AutoTest.Net/ 119 | 120 | # Web workbench (sass) 121 | .sass-cache/ 122 | 123 | # Installshield output folder 124 | [Ee]xpress/ 125 | 126 | # DocProject is a documentation generator add-in 127 | DocProject/buildhelp/ 128 | DocProject/Help/*.HxT 129 | DocProject/Help/*.HxC 130 | DocProject/Help/*.hhc 131 | DocProject/Help/*.hhk 132 | DocProject/Help/*.hhp 133 | DocProject/Help/Html2 134 | DocProject/Help/html 135 | 136 | # Click-Once directory 137 | publish/ 138 | 139 | # Publish Web Output 140 | *.[Pp]ublish.xml 141 | *.azurePubxml 142 | 143 | # TODO: Un-comment the next line if you do not want to checkin 144 | # your web deploy settings because they may include unencrypted 145 | # passwords 146 | #*.pubxml 147 | *.publishproj 148 | 149 | # NuGet Packages 150 | *.nupkg 151 | # The packages folder can be ignored because of Package Restore 152 | **/packages/* 153 | # except build/, which is used as an MSBuild target. 154 | !**/packages/build/ 155 | # Uncomment if necessary however generally it will be regenerated when needed 156 | #!**/packages/repositories.config 157 | # NuGet v3's project.json files produces more ignoreable files 158 | *.nuget.props 159 | *.nuget.targets 160 | 161 | # Microsoft Azure Build Output 162 | csx/ 163 | *.build.csdef 164 | 165 | # Microsoft Azure Emulator 166 | ecf/ 167 | rcf/ 168 | 169 | # Windows Store app package directory 170 | AppPackages/ 171 | BundleArtifacts/ 172 | 173 | # Visual Studio cache files 174 | # files ending in .cache can be ignored 175 | *.[Cc]ache 176 | # but keep track of directories ending in .cache 177 | !*.[Cc]ache/ 178 | 179 | # Others 180 | ClientBin/ 181 | [Ss]tyle[Cc]op.* 182 | ~$* 183 | *~ 184 | *.dbmdl 185 | *.dbproj.schemaview 186 | *.pfx 187 | *.publishsettings 188 | node_modules/ 189 | orleans.codegen.cs 190 | 191 | # RIA/Silverlight projects 192 | Generated_Code/ 193 | 194 | # Backup & report files from converting an old project file 195 | # to a newer Visual Studio version. Backup files are not needed, 196 | # because we have git ;-) 197 | _UpgradeReport_Files/ 198 | Backup*/ 199 | UpgradeLog*.XML 200 | UpgradeLog*.htm 201 | 202 | # SQL Server files 203 | *.mdf 204 | *.ldf 205 | 206 | # Business Intelligence projects 207 | *.rdl.data 208 | *.bim.layout 209 | *.bim_*.settings 210 | 211 | # Microsoft Fakes 212 | FakesAssemblies/ 213 | 214 | # GhostDoc plugin setting file 215 | *.GhostDoc.xml 216 | 217 | # Node.js Tools for Visual Studio 218 | .ntvs_analysis.dat 219 | 220 | # Visual Studio 6 build log 221 | *.plg 222 | 223 | # Visual Studio 6 workspace options file 224 | *.opt 225 | 226 | # Visual Studio LightSwitch build output 227 | **/*.HTMLClient/GeneratedArtifacts 228 | **/*.DesktopClient/GeneratedArtifacts 229 | **/*.DesktopClient/ModelManifest.xml 230 | **/*.Server/GeneratedArtifacts 231 | **/*.Server/ModelManifest.xml 232 | _Pvt_Extensions 233 | 234 | # LightSwitch generated files 235 | GeneratedArtifacts/ 236 | ModelManifest.xml 237 | 238 | # Paket dependency manager 239 | .paket/paket.exe 240 | 241 | # FAKE - F# Make 242 | .fake/ 243 | 244 | # JetBrains 245 | .idea* 246 | .DS_Store 247 | 248 | -------------------------------------------------------------------------------- /CS2Retake/Managers/MapManager.cs: -------------------------------------------------------------------------------- 1 | using CounterStrikeSharp.API; 2 | using CounterStrikeSharp.API.Core; 3 | using CounterStrikeSharp.API.Modules.Timers; 4 | using CounterStrikeSharp.API.Modules.Utils; 5 | using CS2Retake.Entities; 6 | using CS2Retake.Managers.Base; 7 | using CS2Retake.Managers.Interfaces; 8 | using CS2Retake.Utils; 9 | using Microsoft.Extensions.Logging; 10 | using CSZoneNet.Plugin.Utils.Enums; 11 | 12 | namespace CS2Retake.Managers 13 | { 14 | public class MapManager : BaseManager, IMapManager 15 | { 16 | private static MapManager? _instance = null; 17 | public MapEntity? CurrentMap { get; set; } = null; 18 | 19 | public BombSiteEnum BombSite { get; private set; } = BombSiteEnum.Undefined; 20 | public bool HasToBeInBombZone { get; set; } = true; 21 | 22 | public int TerroristRoundWinStreak { get; set; } = 0; 23 | 24 | 25 | 26 | public static MapManager Instance 27 | { 28 | get 29 | { 30 | if (_instance == null) 31 | { 32 | _instance = new MapManager(); 33 | } 34 | return _instance; 35 | } 36 | 37 | } 38 | 39 | private MapManager() { } 40 | 41 | 42 | public void RandomBombSite() 43 | { 44 | this.BombSite = (BombSiteEnum)new Random().Next(0, 2); 45 | } 46 | 47 | public void AddSpawn(CCSPlayerController player, CsTeam team, BombSiteEnum bombSite) 48 | { 49 | if(player == null) 50 | { 51 | MessageUtils.Log(LogLevel.Error, $"player is null"); 52 | return; 53 | } 54 | 55 | var isInBombZone = player?.PlayerPawn?.Value?.InBombZone; 56 | var playerPawn = player?.PlayerPawn.Value; 57 | 58 | if(playerPawn == null) 59 | { 60 | MessageUtils.Log(LogLevel.Error, $"playerPawn is null"); 61 | return; 62 | } 63 | if (playerPawn.AbsOrigin == null) 64 | { 65 | MessageUtils.Log(LogLevel.Error, $"playerPawn position is null"); 66 | return; 67 | } 68 | if (playerPawn.AbsRotation == null) 69 | { 70 | MessageUtils.Log(LogLevel.Error, $"playerPawn rotation is null"); 71 | return; 72 | } 73 | if (isInBombZone == null || !isInBombZone.HasValue) 74 | { 75 | MessageUtils.Log(LogLevel.Error, $"isInBombZone is null"); 76 | return; 77 | } 78 | 79 | var newSpawnPoint = new SpawnPointEntity(playerPawn.AbsOrigin, playerPawn.AbsRotation, bombSite, team, isInBombZone.Value); 80 | 81 | this.CurrentMap?.SpawnPoints.Add(newSpawnPoint); 82 | 83 | player?.PrintToChat($"SpawnPoint added! BombSite: {bombSite} - Team: {team} - isInBombZone: {isInBombZone}"); 84 | } 85 | 86 | public void TeleportPlayerToSpawn(CCSPlayerController player, int? spawnIndex = null) 87 | { 88 | if(this.BombSite == BombSiteEnum.Undefined) 89 | { 90 | this.RandomBombSite(); 91 | } 92 | 93 | var team = (CsTeam)player.TeamNum; 94 | SpawnPointEntity? spawn; 95 | if (!spawnIndex.HasValue) 96 | { 97 | spawn = this.CurrentMap?.GetRandomSpawn(team, this.BombSite, team == CsTeam.CounterTerrorist ? false : this.HasToBeInBombZone); 98 | 99 | if (team == CsTeam.Terrorist && this.HasToBeInBombZone) 100 | { 101 | this.HasToBeInBombZone = false; 102 | } 103 | } 104 | else if (spawnIndex.Value > (this.CurrentMap?.SpawnPoints.Count - 1) || spawnIndex.Value < 0) 105 | { 106 | return; 107 | } 108 | else 109 | { 110 | spawn = this.CurrentMap?.SpawnPoints[spawnIndex.Value]; 111 | } 112 | 113 | if (spawn == null) 114 | { 115 | MessageUtils.Log(LogLevel.Error,$"Spawn is null. Moving player to Spectator"); 116 | player.SwitchTeam(CsTeam.Spectator); 117 | return; 118 | } 119 | 120 | spawn.SpawnUsedBy = player; 121 | player.PrintToConsole($"Spawnpoint: {spawn.SpawnId}"); 122 | player?.PlayerPawn?.Value?.Teleport(spawn.Position, spawn.QAngle, new Vector(0f, 0f, 0f)); 123 | 124 | 125 | 126 | } 127 | 128 | public override void ResetForNextRound(bool completeReset = true) 129 | { 130 | if(completeReset) 131 | { 132 | this.RandomBombSite(); 133 | this.CurrentMap?.ResetSpawnInUse(); 134 | } 135 | 136 | this.HasToBeInBombZone = true; 137 | } 138 | 139 | public override void ResetForNextMap(bool completeReset = true) 140 | { 141 | 142 | } 143 | } 144 | } 145 | -------------------------------------------------------------------------------- /CS2Retake/Managers/GameRuleManager.cs: -------------------------------------------------------------------------------- 1 | using CounterStrikeSharp.API; 2 | using CounterStrikeSharp.API.Core; 3 | using CounterStrikeSharp.API.Modules.Entities.Constants; 4 | using CS2Retake.Managers.Base; 5 | using CS2Retake.Managers.Interfaces; 6 | using CS2Retake.Utils; 7 | using Microsoft.Extensions.Logging; 8 | using System; 9 | using System.Collections.Generic; 10 | using System.Linq; 11 | using System.Text; 12 | using System.Threading.Tasks; 13 | 14 | namespace CS2Retake.Managers 15 | { 16 | public class GameRuleManager : BaseManager, IGameRuleManager 17 | { 18 | private static GameRuleManager? _instance = null; 19 | 20 | public CCSGameRules? GameRules { get; set; } = null; 21 | 22 | public static GameRuleManager Instance 23 | { 24 | get 25 | { 26 | if (_instance == null) 27 | { 28 | _instance = new GameRuleManager(); 29 | } 30 | return _instance; 31 | } 32 | } 33 | 34 | private GameRuleManager() { } 35 | 36 | public bool IsWarmup 37 | { 38 | get 39 | { 40 | if (this.GameRules == null) 41 | { 42 | this.GetGameRules(); 43 | } 44 | 45 | return this.GameRules?.WarmupPeriod ?? false; 46 | } 47 | } 48 | 49 | public float WarmupEnd 50 | { 51 | get 52 | { 53 | if (this.GameRules == null) 54 | { 55 | this.GetGameRules(); 56 | } 57 | 58 | return this.GameRules?.WarmupPeriodEnd ?? 0f; 59 | } 60 | } 61 | 62 | public bool BombPlanted 63 | { 64 | get 65 | { 66 | if (this.GameRules == null) 67 | { 68 | this.GetGameRules(); 69 | } 70 | 71 | return this.GameRules!.BombPlanted; 72 | } 73 | 74 | set 75 | { 76 | if (this.GameRules == null) 77 | { 78 | this.GetGameRules(); 79 | } 80 | 81 | this.GameRules!.BombPlanted = value; 82 | } 83 | } 84 | 85 | public bool BombDefused 86 | { 87 | get 88 | { 89 | if (this.GameRules == null) 90 | { 91 | this.GetGameRules(); 92 | } 93 | 94 | return this.GameRules!.BombDefused; 95 | } 96 | 97 | set 98 | { 99 | if (this.GameRules == null) 100 | { 101 | this.GetGameRules(); 102 | } 103 | 104 | this.GameRules!.BombDefused = value; 105 | } 106 | } 107 | 108 | public int TotalRoundsPlayed 109 | { 110 | get 111 | { 112 | if (this.GameRules == null) 113 | { 114 | this.GetGameRules(); 115 | } 116 | 117 | return this.GameRules!.TotalRoundsPlayed; 118 | } 119 | } 120 | 121 | public void TerminateRound(RoundEndReason reason = RoundEndReason.RoundDraw) 122 | { 123 | this.GameRules?.TerminateRound(0, reason); 124 | } 125 | 126 | private void GetGameRules() 127 | { 128 | MessageUtils.Log(LogLevel.Information, $"GameRules is null. Fetching gamerule..."); 129 | 130 | RetakeManager.Instance.ConfigureForRetake(); 131 | 132 | var gameRuleProxyList = this.GetGameRulesProxies(); 133 | 134 | if (gameRuleProxyList.Count > 1) 135 | { 136 | MessageUtils.Log(LogLevel.Error, $"Multiple GameRuleProxies found. Using firstOrDefault"); 137 | } 138 | 139 | var gameRuleProxy = gameRuleProxyList.FirstOrDefault(); 140 | 141 | if (gameRuleProxy == null) 142 | { 143 | MessageUtils.Log(LogLevel.Error, $"GameRuleProxy is null"); 144 | return; 145 | } 146 | 147 | if (gameRuleProxy.GameRules == null) 148 | { 149 | MessageUtils.Log(LogLevel.Error, $"GameRules is null"); 150 | return; 151 | } 152 | 153 | this.GameRules = gameRuleProxy.GameRules; 154 | } 155 | 156 | private List GetGameRulesProxies() 157 | { 158 | var gameRulesProxyList = Utilities.FindAllEntitiesByDesignerName("cs_gamerules").ToList(); 159 | 160 | if (!gameRulesProxyList.Any()) 161 | { 162 | MessageUtils.Log(LogLevel.Error, $"No gameRuleProxy found!"); 163 | } 164 | 165 | return gameRulesProxyList; 166 | } 167 | 168 | public override void ResetForNextRound(bool completeReset = true) 169 | { 170 | 171 | } 172 | 173 | public override void ResetForNextMap(bool completeReset = true) 174 | { 175 | 176 | } 177 | } 178 | } 179 | -------------------------------------------------------------------------------- /CS2Retake/Allocators/Implementations/CommandAllocator/Manager/DBManager.cs: -------------------------------------------------------------------------------- 1 | using CS2Retake.Allocators.Implementations.CommandAllocator.Interfaces; 2 | using CS2Retake.Allocators.Implementations.CommandAllocator.Repository; 3 | using CS2Retake.Allocators.Implementations.CommandAllocator.Utils; 4 | using CS2Retake.Utils; 5 | using CSZoneNet.Plugin.CS2BaseAllocator.Interfaces; 6 | using System; 7 | using System.Collections.Generic; 8 | using System.Linq; 9 | using System.Text; 10 | using System.Threading.Tasks; 11 | 12 | namespace CS2Retake.Allocators.Implementations.CommandAllocator.Manager 13 | { 14 | public class DBManager : IRetakeRepository 15 | { 16 | private static DBManager? _instance = null; 17 | 18 | public DBType DBType { get; set; } = DBType.Cache; 19 | public string AllocatorConfigDirectoryPath { get; set; } = string.Empty; 20 | public string ConnectionString { get; set; } = string.Empty; 21 | 22 | private IRetakeRepository? _retakeDB = null; 23 | 24 | public static DBManager Instance 25 | { 26 | get 27 | { 28 | if (_instance == null) 29 | { 30 | _instance = new DBManager(); 31 | } 32 | return _instance; 33 | } 34 | } 35 | 36 | public DBManager() { } 37 | 38 | public void Init() 39 | { 40 | MessageUtils.Log(Microsoft.Extensions.Logging.LogLevel.Information, $"Init DBManager for Type {this.DBType} - Path: {this.AllocatorConfigDirectoryPath}"); 41 | 42 | switch (this.DBType) 43 | { 44 | case DBType.Cache: 45 | this._retakeDB = null; 46 | break; 47 | case DBType.SQLite: 48 | this._retakeDB = new SQLiteRepository(this.AllocatorConfigDirectoryPath); 49 | break; 50 | case DBType.PostgreSql: 51 | this._retakeDB = new PostgreSqlRepository(this.ConnectionString); 52 | break; 53 | default: 54 | this._retakeDB = null; 55 | break; 56 | } 57 | } 58 | 59 | public bool InsertOrUpdateFullBuyPrimaryWeaponString(ulong userId, string weaponString, int team) 60 | { 61 | 62 | if(this.DBType == DBType.Cache || this._retakeDB == null) 63 | { 64 | return false; 65 | } 66 | 67 | return this._retakeDB.InsertOrUpdateFullBuyPrimaryWeaponString(userId, weaponString, team); 68 | } 69 | 70 | public bool InsertOrUpdateFullBuySecondaryWeaponString(ulong userId, string weaponString, int team) 71 | { 72 | if (this.DBType == DBType.Cache || this._retakeDB == null) 73 | { 74 | return false; 75 | } 76 | 77 | return this._retakeDB.InsertOrUpdateFullBuySecondaryWeaponString(userId, weaponString, team); 78 | } 79 | 80 | public bool InsertOrUpdateFullBuyAWPChance(ulong userId, int chance, int team) 81 | { 82 | if (this.DBType == DBType.Cache || this._retakeDB == null) 83 | { 84 | return false; 85 | } 86 | 87 | return this._retakeDB.InsertOrUpdateFullBuyAWPChance(userId, chance, team); 88 | } 89 | 90 | public bool InsertOrUpdateMidPrimaryWeaponString(ulong userId, string weaponString, int team) 91 | { 92 | if (this.DBType == DBType.Cache || this._retakeDB == null) 93 | { 94 | return false; 95 | } 96 | 97 | return this._retakeDB.InsertOrUpdateMidPrimaryWeaponString(userId, weaponString, team); 98 | } 99 | 100 | public bool InsertOrUpdateMidSecondaryWeaponString(ulong userId, string weaponString, int team) 101 | { 102 | if (this.DBType == DBType.Cache || this._retakeDB == null) 103 | { 104 | return false; 105 | } 106 | 107 | return this._retakeDB.InsertOrUpdateMidSecondaryWeaponString(userId, weaponString, team); 108 | } 109 | 110 | public bool InsertOrUpdatePistolWeaponString(ulong userId, string weaponString, int team) 111 | { 112 | if (this.DBType == DBType.Cache || this._retakeDB == null) 113 | { 114 | return false; 115 | } 116 | 117 | return this._retakeDB.InsertOrUpdatePistolWeaponString(userId, weaponString, team); 118 | } 119 | 120 | public (string? primaryWeapon, string? secondaryWeapon, int? awpChance) GetFullBuyWeapons(ulong userId, int team) 121 | { 122 | if (this.DBType == DBType.Cache || this._retakeDB == null) 123 | { 124 | return (null,null,null); 125 | } 126 | 127 | return this._retakeDB.GetFullBuyWeapons(userId, team); 128 | } 129 | 130 | public (string? primaryWeapon, string? secondaryWeapon, int? awpChance) GetMidWeapons(ulong userId, int team) 131 | { 132 | if (this.DBType == DBType.Cache || this._retakeDB == null) 133 | { 134 | return (null, null, null); 135 | } 136 | 137 | return this._retakeDB.GetMidWeapons(userId, team); 138 | } 139 | 140 | public (string? primaryWeapon, string? secondaryWeapon, int? awpChance) GetPistolWeapons(ulong userId, int team) 141 | { 142 | if (this.DBType == DBType.Cache || this._retakeDB == null) 143 | { 144 | return (null, null, null); 145 | } 146 | 147 | return this._retakeDB.GetPistolWeapons(userId, team); 148 | } 149 | } 150 | } 151 | -------------------------------------------------------------------------------- /CS2Retake/Allocators/Implementations/CommandAllocator/Menus/MidMenu.cs: -------------------------------------------------------------------------------- 1 | using CounterStrikeSharp.API.Core; 2 | using CounterStrikeSharp.API.Modules.Entities; 3 | using CounterStrikeSharp.API.Modules.Menu; 4 | using CounterStrikeSharp.API.Modules.Utils; 5 | using CS2Retake.Allocators.Implementations.CommandAllocator.Configs; 6 | using CS2Retake.Allocators.Implementations.CommandAllocator.Manager; 7 | using CS2Retake.Utils; 8 | using CSZoneNet.Plugin.CS2BaseAllocator.Configs; 9 | using System; 10 | using System.Collections.Generic; 11 | using System.Linq; 12 | using System.Numerics; 13 | using System.Runtime.CompilerServices; 14 | using System.Text; 15 | using System.Threading.Tasks; 16 | 17 | namespace CS2Retake.Allocators.Implementations.CommandAllocator.Menus 18 | { 19 | public class MidMenu 20 | { 21 | private static MidConfig _config { get; set; } = new MidConfig(); 22 | 23 | private static MidMenu? _instance = null; 24 | public static MidMenu Instance 25 | { 26 | get 27 | { 28 | if (_instance == null) 29 | { 30 | _instance = new MidMenu(); 31 | } 32 | return _instance; 33 | } 34 | } 35 | 36 | private MidMenu() 37 | { 38 | var config = AllocatorConfigManager.Load("CommandAllocator", "Mid"); 39 | 40 | if(config == null) 41 | { 42 | MessageUtils.Log(Microsoft.Extensions.Logging.LogLevel.Error, $"The Mid configuration could not be parsed!"); 43 | return; 44 | } 45 | 46 | if (_config.Version > config.Version) 47 | { 48 | MessageUtils.Log(Microsoft.Extensions.Logging.LogLevel.Warning, $"The Mid configuration is out of date. Consider updating the config. [Current Version: {config.Version} - Mid Version: {_config.Version}]"); 49 | } 50 | 51 | _config = config; 52 | } 53 | 54 | public void OpenPrimaryMenu(CCSPlayerController player, CsTeam team) 55 | { 56 | ChatMenu menu = new ChatMenu("Mid Primary Menu"); 57 | 58 | var teamString = team == CsTeam.CounterTerrorist ? "CT" : "T"; 59 | 60 | foreach (var primary in _config.AvailablePrimaries.FindAll(x => x.Team == team || x.Team == CsTeam.None)) 61 | { 62 | menu.AddMenuOption($"{teamString}_{primary.WeaponName}", OnSelectPrimary); 63 | } 64 | 65 | MenuManager.CloseActiveMenu(player); 66 | MenuManager.OpenChatMenu(player, menu); 67 | } 68 | 69 | public void OpenSecondaryMenu(CCSPlayerController player, CsTeam team) 70 | { 71 | ChatMenu menu = new ChatMenu("Mid Secondary Menu"); 72 | 73 | var teamString = team == CsTeam.CounterTerrorist ? "CT" : "T"; 74 | 75 | foreach (var secondary in _config.AvailableSecondaries.FindAll(x => x.Team == team || x.Team == CsTeam.None)) 76 | { 77 | menu.AddMenuOption($"{teamString}_{secondary.WeaponName}", OnSelectSecondary); 78 | } 79 | 80 | MenuManager.CloseActiveMenu(player); 81 | MenuManager.OpenChatMenu(player, menu); 82 | } 83 | 84 | private static void OnSelectPrimary(CCSPlayerController player, ChatMenuOption chatMenuOption) 85 | { 86 | var menuText = chatMenuOption.Text?.Split('_')?.LastOrDefault() ?? string.Empty; 87 | var weaponString = _config.AvailablePrimaries.FirstOrDefault(x => x.WeaponName.Equals(menuText))?.WeaponString; 88 | 89 | if (string.IsNullOrWhiteSpace(weaponString)) 90 | { 91 | return; 92 | } 93 | 94 | var team = GetTeam(chatMenuOption.Text ?? string.Empty); 95 | 96 | MessageUtils.PrintToPlayerOrServer($"You have now selected {ChatColors.Green}{menuText}{ChatColors.White} as your primary for {ChatColors.Green}Mid{ChatColors.White} rounds!", player); 97 | 98 | CacheManager.Instance.AddOrUpdateMidPrimaryCache(player, weaponString, team); 99 | DBManager.Instance.InsertOrUpdateMidPrimaryWeaponString(player.SteamID, weaponString, (int)team); 100 | 101 | MidMenu.Instance.OpenSecondaryMenu(player, team); 102 | } 103 | 104 | private static void OnSelectSecondary(CCSPlayerController player, ChatMenuOption chatMenuOption) 105 | { 106 | var menuText = chatMenuOption.Text?.Split('_')?.LastOrDefault() ?? string.Empty; 107 | var weaponString = _config.AvailableSecondaries.FirstOrDefault(x => x.WeaponName.Equals(menuText))?.WeaponString; 108 | 109 | if (string.IsNullOrWhiteSpace(weaponString)) 110 | { 111 | return; 112 | } 113 | 114 | var team = GetTeam(chatMenuOption.Text ?? string.Empty); 115 | 116 | MessageUtils.PrintToPlayerOrServer($"You have now selected {ChatColors.Green}{menuText}{ChatColors.White} as your secondary for {ChatColors.Green}Mid{ChatColors.White} rounds!", player); 117 | 118 | CacheManager.Instance.AddOrUpdateMidSecondaryCache(player, weaponString, team); 119 | DBManager.Instance.InsertOrUpdateMidSecondaryWeaponString(player.SteamID, weaponString, (int)team); 120 | } 121 | 122 | private static CsTeam GetTeam(string weaponString) 123 | { 124 | var teamString = weaponString?.Split('_')?.FirstOrDefault()?.ToUpper() ?? string.Empty; 125 | 126 | CsTeam team = CsTeam.None; 127 | 128 | switch (teamString) 129 | { 130 | case "CT": 131 | return CsTeam.CounterTerrorist; 132 | case "T": 133 | return CsTeam.Terrorist; 134 | default: 135 | return CsTeam.None; 136 | } 137 | 138 | } 139 | } 140 | } 141 | -------------------------------------------------------------------------------- /CS2Retake/Allocators/Implementations/CommandAllocator/Menus/FullBuyMenu.cs: -------------------------------------------------------------------------------- 1 | using CounterStrikeSharp.API.Core; 2 | using CounterStrikeSharp.API.Modules.Entities; 3 | using CounterStrikeSharp.API.Modules.Menu; 4 | using CounterStrikeSharp.API.Modules.Utils; 5 | using CS2Retake.Allocators.Implementations.CommandAllocator.Configs; 6 | using CS2Retake.Allocators.Implementations.CommandAllocator.Manager; 7 | using CS2Retake.Utils; 8 | using CSZoneNet.Plugin.CS2BaseAllocator.Configs; 9 | using System; 10 | using System.Collections.Generic; 11 | using System.Configuration; 12 | using System.Linq; 13 | using System.Numerics; 14 | using System.Runtime.CompilerServices; 15 | using System.Text; 16 | using System.Threading.Channels; 17 | using System.Threading.Tasks; 18 | 19 | namespace CS2Retake.Allocators.Implementations.CommandAllocator.Menus 20 | { 21 | public class FullBuyMenu 22 | { 23 | private static FullBuyConfig _config { get; set; } = new FullBuyConfig(); 24 | 25 | private static FullBuyMenu? _instance = null; 26 | 27 | public static FullBuyMenu Instance 28 | { 29 | get 30 | { 31 | if (_instance == null) 32 | { 33 | _instance = new FullBuyMenu(); 34 | } 35 | return _instance; 36 | } 37 | } 38 | 39 | public FullBuyConfig Config 40 | { 41 | get 42 | { 43 | return _config; 44 | } 45 | } 46 | 47 | 48 | private FullBuyMenu() 49 | { 50 | var config = AllocatorConfigManager.Load("CommandAllocator", "FullBuy"); 51 | 52 | if (config == null) 53 | { 54 | MessageUtils.Log(Microsoft.Extensions.Logging.LogLevel.Error, $"The FullBuy configuration could not be parsed!"); 55 | return; 56 | } 57 | 58 | if (_config.Version > config.Version) 59 | { 60 | MessageUtils.Log(Microsoft.Extensions.Logging.LogLevel.Warning, $"The FullBuy configuration is out of date. Consider updating the config. [Current Version: {config.Version} - FullBuy Version: {_config.Version}]"); 61 | } 62 | 63 | _config = config; 64 | } 65 | 66 | public void OpenPrimaryMenu(CCSPlayerController player, CsTeam team) 67 | { 68 | ChatMenu menu = new ChatMenu("Full Buy Primary Menu"); 69 | 70 | var teamString = team == CsTeam.CounterTerrorist ? "CT" : "T"; 71 | 72 | foreach (var primary in _config.AvailablePrimaries.FindAll(x => x.Team == team || x.Team == CsTeam.None)) 73 | { 74 | menu.AddMenuOption($"{teamString}_{primary.WeaponName}", OnSelectPrimary); 75 | } 76 | 77 | MenuManager.CloseActiveMenu(player); 78 | MenuManager.OpenChatMenu(player, menu); 79 | } 80 | 81 | public void OpenSecondaryMenu(CCSPlayerController player, CsTeam team) 82 | { 83 | ChatMenu menu = new ChatMenu("Full Buy Secondary Menu"); 84 | 85 | var teamString = team == CsTeam.CounterTerrorist ? "CT" : "T"; 86 | 87 | foreach (var secondary in _config.AvailableSecondaries.FindAll(x => x.Team == team || x.Team == CsTeam.None)) 88 | { 89 | menu.AddMenuOption($"{teamString}_{secondary.WeaponName}", OnSelectSecondary); 90 | } 91 | 92 | MenuManager.CloseActiveMenu(player); 93 | MenuManager.OpenChatMenu(player, menu); 94 | } 95 | 96 | public void OpenAWPChanceMenu(CCSPlayerController player, CsTeam team) 97 | { 98 | ChatMenu menu = new ChatMenu("AWP Chance Menu"); 99 | 100 | var teamString = team == CsTeam.CounterTerrorist ? "CT" : "T"; 101 | var awpSettings = team == CsTeam.CounterTerrorist ? _config.AWPChanceCT : _config.AWPChanceT; 102 | 103 | menu.AddMenuOption($"{teamString}_0%", OnSelectAWPChance); 104 | foreach (var chance in awpSettings.Chances) 105 | { 106 | menu.AddMenuOption($"{teamString}_{chance}%", OnSelectAWPChance); 107 | } 108 | 109 | MenuManager.CloseActiveMenu(player); 110 | MenuManager.OpenChatMenu(player, menu); 111 | } 112 | 113 | private static void OnSelectPrimary(CCSPlayerController player, ChatMenuOption chatMenuOption) 114 | { 115 | var menuText = chatMenuOption.Text?.Split('_')?.LastOrDefault() ?? string.Empty; 116 | var weaponString = _config.AvailablePrimaries.FirstOrDefault(x => x.WeaponName.Equals(menuText))?.WeaponString; 117 | 118 | if (string.IsNullOrWhiteSpace(weaponString)) 119 | { 120 | return; 121 | } 122 | 123 | var team = GetTeam(chatMenuOption.Text ?? string.Empty); 124 | 125 | MessageUtils.PrintToPlayerOrServer($"You have now selected {ChatColors.Green}{menuText}{ChatColors.White} as your primary for {ChatColors.Green}FullBuy{ChatColors.White} rounds!", player); 126 | 127 | CacheManager.Instance.AddOrUpdateFullBuyPrimaryCache(player, weaponString, team); 128 | DBManager.Instance.InsertOrUpdateFullBuyPrimaryWeaponString(player.SteamID, weaponString, (int)team); 129 | 130 | FullBuyMenu.Instance.OpenSecondaryMenu(player, team); 131 | } 132 | 133 | private static void OnSelectSecondary(CCSPlayerController player, ChatMenuOption chatMenuOption) 134 | { 135 | var menuText = chatMenuOption.Text?.Split('_')?.LastOrDefault() ?? string.Empty; 136 | var weaponString = _config.AvailableSecondaries.FirstOrDefault(x => x.WeaponName.Equals(menuText))?.WeaponString; 137 | 138 | if (string.IsNullOrWhiteSpace(weaponString)) 139 | { 140 | return; 141 | } 142 | 143 | var team = GetTeam(chatMenuOption.Text ?? string.Empty); 144 | 145 | MessageUtils.PrintToPlayerOrServer($"You have now selected {ChatColors.Green}{menuText}{ChatColors.White} as your secondary for {ChatColors.Green}FullBuy{ChatColors.White} rounds!", player); 146 | 147 | CacheManager.Instance.AddOrUpdateFullBuySecondaryCache(player, weaponString, team); 148 | DBManager.Instance.InsertOrUpdateFullBuySecondaryWeaponString(player.SteamID, weaponString, (int)team); 149 | 150 | if(_config.EnableAWPChance) 151 | { 152 | FullBuyMenu.Instance.OpenAWPChanceMenu(player, team); 153 | } 154 | else 155 | { 156 | CacheManager.Instance.AddOrUpdateFullBuyAWPChanceCache(player, 0, team); 157 | } 158 | 159 | } 160 | 161 | private static void OnSelectAWPChance(CCSPlayerController player, ChatMenuOption chatMenuOption) 162 | { 163 | var chanceString = chatMenuOption.Text?.Split('_')?.LastOrDefault()?.Replace("%","") ?? string.Empty; 164 | 165 | if(string.IsNullOrWhiteSpace(chanceString)) 166 | { 167 | return; 168 | } 169 | 170 | if(!int.TryParse(chanceString, out var chance)) 171 | { 172 | return; 173 | } 174 | 175 | var team = GetTeam(chatMenuOption.Text ?? string.Empty); 176 | 177 | MessageUtils.PrintToPlayerOrServer($"You have now selected {ChatColors.Green}{chanceString}%{ChatColors.White} chance to receive an AWP for {ChatColors.Green}FullBuy{ChatColors.White} rounds!", player); 178 | 179 | CacheManager.Instance.AddOrUpdateFullBuyAWPChanceCache(player, chance, team); 180 | DBManager.Instance.InsertOrUpdateFullBuyAWPChance(player.SteamID, chance, (int)team); 181 | } 182 | 183 | private static CsTeam GetTeam(string weaponString) 184 | { 185 | var teamString = weaponString?.Split(separator: '_')?.FirstOrDefault()?.ToUpper() ?? string.Empty; 186 | 187 | CsTeam team = CsTeam.None; 188 | 189 | switch (teamString) 190 | { 191 | case "CT": 192 | team = CsTeam.CounterTerrorist; 193 | break; 194 | case "T": 195 | team = CsTeam.Terrorist; 196 | break; 197 | } 198 | 199 | return team; 200 | 201 | } 202 | } 203 | } 204 | -------------------------------------------------------------------------------- /CS2Retake/Managers/PlantManager.cs: -------------------------------------------------------------------------------- 1 | using CounterStrikeSharp.API.Core; 2 | using CounterStrikeSharp.API; 3 | using CS2Retake.Configs; 4 | using CS2Retake.Managers.Base; 5 | using CS2Retake.Managers.Interfaces; 6 | using CS2Retake.Utils; 7 | using System; 8 | using System.Collections.Generic; 9 | using System.Linq; 10 | using System.Text; 11 | using System.Threading.Tasks; 12 | using CounterStrikeSharp.API.Modules.Utils; 13 | using Microsoft.Extensions.Logging; 14 | using CSZoneNet.Plugin.Utils.Enums; 15 | 16 | namespace CS2Retake.Managers 17 | { 18 | public class PlantManager : BaseManager, IPlantManager 19 | { 20 | private static PlantManager? _instance = null; 21 | 22 | public CounterStrikeSharp.API.Modules.Timers.Timer? HasBombBeenPlantedTimer = null; 23 | 24 | public static PlantManager Instance 25 | { 26 | get 27 | { 28 | if (_instance == null) 29 | { 30 | _instance = new PlantManager(); 31 | } 32 | return _instance; 33 | } 34 | } 35 | 36 | private PlantManager() { } 37 | 38 | public void HandlePlant() 39 | { 40 | switch(RuntimeConfig.PlantType) 41 | { 42 | case PlantTypeEnum.AutoPlant: 43 | this.AutoPlant(); 44 | break; 45 | case PlantTypeEnum.FastPlant: 46 | this.FastPlant(); 47 | break; 48 | default: 49 | this.AutoPlant(); 50 | break; 51 | } 52 | } 53 | 54 | private void AutoPlant() 55 | { 56 | if(GameRuleManager.Instance.IsWarmup || !PlayerUtils.AreMoreThenOrEqualPlayersConnected(2)) 57 | { 58 | MessageUtils.LogDebug($"Skipping AutoPlant because its still Warmup or less then 2 players are connected."); 59 | return; 60 | } 61 | 62 | var planterPlayerController = RetakeManager.Instance.PlanterPlayerController; 63 | 64 | if (planterPlayerController == null || MapManager.Instance.BombSite == BombSiteEnum.Undefined || planterPlayerController.PlayerPawn == null || planterPlayerController.PlayerPawn.Value == null) 65 | { 66 | MessageUtils.Log(LogLevel.Error, $"planterPlayerController null or bombsite undefined [{MapManager.Instance.BombSite}]"); 67 | return; 68 | } 69 | 70 | var bombPlanterPawn = planterPlayerController.PlayerPawn.Value; 71 | 72 | if (bombPlanterPawn == null || !bombPlanterPawn.IsValid) 73 | { 74 | MessageUtils.Log(LogLevel.Error, "bombPlanterPawn null or isn't valid"); 75 | return; 76 | } 77 | if (bombPlanterPawn.AbsOrigin == null) 78 | { 79 | MessageUtils.Log(LogLevel.Error, "bombPlanterPawn AbsOrigin null"); 80 | return; 81 | } 82 | if (bombPlanterPawn.AbsRotation == null) 83 | { 84 | MessageUtils.Log(LogLevel.Error, "bombPlanterPawn AbsRotation null"); 85 | return; 86 | } 87 | if (bombPlanterPawn.TeamNum != (int)CsTeam.Terrorist) 88 | { 89 | MessageUtils.Log(LogLevel.Error, "bombPlanterPawn team is not terrorist"); 90 | return; 91 | } 92 | 93 | var plantedc4 = Utilities.CreateEntityByName("planted_c4"); 94 | 95 | if (plantedc4 == null) 96 | { 97 | MessageUtils.Log(LogLevel.Error, "plantedc4 null"); 98 | return; 99 | } 100 | if (plantedc4.AbsOrigin == null) 101 | { 102 | MessageUtils.Log(LogLevel.Error, "plantedc4 AbsOrigin is null"); 103 | return; 104 | } 105 | plantedc4.AbsOrigin.X = bombPlanterPawn.AbsOrigin.X; 106 | plantedc4.AbsOrigin.Y = bombPlanterPawn.AbsOrigin.Y; 107 | plantedc4.AbsOrigin.Z = bombPlanterPawn.AbsOrigin.Z; 108 | plantedc4.HasExploded = false; 109 | 110 | plantedc4.BombSite = (int)MapManager.Instance.BombSite; 111 | plantedc4.BombTicking = true; 112 | plantedc4.CannotBeDefused = false; 113 | 114 | plantedc4.DispatchSpawn(); 115 | 116 | GameRuleManager.Instance.BombPlanted = true; 117 | GameRuleManager.Instance.BombDefused = false; 118 | 119 | this.FireBombPlantedEvent(planterPlayerController, MapManager.Instance.BombSite); 120 | } 121 | 122 | private void FastPlant() 123 | { 124 | var c4list = Utilities.FindAllEntitiesByDesignerName("weapon_c4"); 125 | 126 | if (!c4list.Any()) 127 | { 128 | return; 129 | } 130 | 131 | var c4 = c4list.FirstOrDefault(); 132 | if (c4 == null) 133 | { 134 | return; 135 | } 136 | 137 | c4.BombPlacedAnimation = false; 138 | c4.ArmedTime = 0f; 139 | } 140 | 141 | public void HasBombBeenPlanted() 142 | { 143 | if (RuntimeConfig.SecondsUntilBombPlantedCheck <= 0 || GameRuleManager.Instance.IsWarmup || !PlayerUtils.AreMoreThenOrEqualPlayersConnected(2)) 144 | { 145 | return; 146 | } 147 | 148 | //Finding planted_c4 or weapon_c4 149 | var bombList = Utilities.FindAllEntitiesByDesignerName("c4"); 150 | 151 | if (!bombList.Any() && !GameRuleManager.Instance.IsWarmup && PlayerUtils.GetPlayerControllersOfTeam(CsTeam.Terrorist).Any()) 152 | { 153 | MessageUtils.PrintToChatAll($"No bomb was found in any players inventory resetting."); 154 | TeamManager.Instance.ScrambleTeams(); 155 | Utilities.GetPlayers().ForEach(x => x?.PlayerPawn?.Value?.CommitSuicide(false, true)); 156 | return; 157 | } 158 | 159 | this.HasBombBeenPlantedTimer = new CounterStrikeSharp.API.Modules.Timers.Timer(RuntimeConfig.SecondsUntilBombPlantedCheck, this.HasBombBeenPlantedCallback); 160 | } 161 | 162 | public void HasBombBeenPlantedCallback() 163 | { 164 | var plantedBomb = this.FindPlantedBomb(); 165 | 166 | if (plantedBomb != null) 167 | { 168 | return; 169 | } 170 | 171 | var planterPlayerController = RetakeManager.Instance.PlanterPlayerController; 172 | 173 | if (planterPlayerController != null) 174 | { 175 | Server.PrintToChatAll($"{MessageUtils.PluginPrefix} Player {ChatColors.DarkRed}{planterPlayerController.PlayerName}{ChatColors.White} failed to plant the bomb in time. Counter-Terrorists win this round."); 176 | } 177 | 178 | //var terroristPlayerList = Utilities.GetPlayers().Where(x => x != null && x.IsValid && x.PlayerPawn != null && x.PlayerPawn.IsValid && x.PlayerPawn.Value != null && x.PlayerPawn.Value.IsValid && x.TeamNum == (int)CsTeam.Terrorist).ToList(); 179 | //terroristPlayerList.ForEach(x => x?.PlayerPawn?.Value?.CommitSuicide(true, true)); 180 | 181 | GameRuleManager.Instance.TerminateRound(CounterStrikeSharp.API.Modules.Entities.Constants.RoundEndReason.CTsWin); 182 | } 183 | 184 | private void FireBombPlantedEvent(CCSPlayerController planterController, BombSiteEnum bombsite) 185 | { 186 | if (!planterController.IsValid || planterController.PlayerPawn.Value == null) 187 | { 188 | return; 189 | } 190 | 191 | var eventPtr = NativeAPI.CreateEvent("bomb_planted", true); 192 | NativeAPI.SetEventPlayerController(eventPtr, "userid", planterController.Handle); 193 | NativeAPI.SetEventInt(eventPtr, "userid", (int)planterController.PlayerPawn.Value.Index); 194 | NativeAPI.SetEventInt(eventPtr, "site", (int)bombsite); 195 | 196 | NativeAPI.FireEvent(eventPtr, false); 197 | } 198 | 199 | private CPlantedC4? FindPlantedBomb() 200 | { 201 | var plantedBombList = Utilities.FindAllEntitiesByDesignerName("planted_c4"); 202 | 203 | if (!plantedBombList.Any()) 204 | { 205 | MessageUtils.Log(LogLevel.Warning, "No planted bomb entities have been found! This might be because no bomb was planted."); 206 | return null; 207 | } 208 | 209 | return plantedBombList.FirstOrDefault(); 210 | } 211 | 212 | public override void ResetForNextRound(bool completeReset = true) 213 | { 214 | if (this.HasBombBeenPlantedTimer != null) 215 | { 216 | this.HasBombBeenPlantedTimer?.Kill(); 217 | } 218 | } 219 | 220 | public override void ResetForNextMap(bool completeReset = true) 221 | { 222 | 223 | } 224 | } 225 | } 226 | -------------------------------------------------------------------------------- /CS2Retake/Managers/WeaponManager.cs: -------------------------------------------------------------------------------- 1 | using CounterStrikeSharp.API; 2 | using CounterStrikeSharp.API.Core; 3 | using CounterStrikeSharp.API.Modules.Entities.Constants; 4 | using CounterStrikeSharp.API.Modules.Utils; 5 | using CS2Retake.Allocators; 6 | using CS2Retake.Allocators.Exceptions; 7 | using CS2Retake.Configs; 8 | using CS2Retake.Managers.Base; 9 | using CS2Retake.Managers.Interfaces; 10 | using CS2Retake.Utils; 11 | using Microsoft.Extensions.Logging; 12 | using CSZoneNet.Plugin.Utils.Enums; 13 | using CSZoneNet.Plugin.CS2BaseAllocator.Interfaces; 14 | using CS2Retake.Allocators.Factory; 15 | using CounterStrikeSharp.API.Modules.Entities; 16 | 17 | namespace CS2Retake.Managers 18 | { 19 | public class WeaponManager : BaseManager, IWeaponManager 20 | { 21 | private static WeaponManager? _instance = null; 22 | 23 | private IBaseAllocator _allocator; 24 | 25 | public IPlugin PluginInstance { get; set; } = null; 26 | 27 | 28 | public static WeaponManager Instance 29 | { 30 | get 31 | { 32 | if (_instance == null) 33 | { 34 | _instance = new WeaponManager(); 35 | } 36 | return _instance; 37 | } 38 | } 39 | 40 | private WeaponManager() 41 | { 42 | 43 | } 44 | 45 | public void AssignWeapons() 46 | { 47 | Utilities.GetPlayers().FindAll(x => x.TeamNum == (int)CsTeam.Terrorist || x.TeamNum == (int)CsTeam.CounterTerrorist).ForEach(x => { 48 | this.RemoveWeapons(x); 49 | this.AssignWeapon(x); 50 | }); 51 | } 52 | 53 | public void AssignWeapon(CCSPlayerController player) 54 | { 55 | if(player == null || !player.IsValid) 56 | { 57 | MessageUtils.Log(LogLevel.Error, $"Player is null or not valid"); 58 | return; 59 | } 60 | 61 | if (player.PlayerPawn == null || !player.PlayerPawn.IsValid || player.PlayerPawn.Value == null || !player.PlayerPawn.Value.IsValid) 62 | { 63 | MessageUtils.Log(LogLevel.Warning, $"PlayerPawn is null or not valid. This might be because of a disconnected player."); 64 | return; 65 | } 66 | 67 | var team = (CsTeam)player.TeamNum; 68 | if (team != CsTeam.Terrorist && team != CsTeam.CounterTerrorist) 69 | { 70 | MessageUtils.Log(LogLevel.Error, $"Player not in Terrorist or CounterTerrorist Team"); 71 | return; 72 | } 73 | 74 | var roundType = RoundTypeManager.Instance.RoundType; 75 | 76 | 77 | if(!this.HandleAllocatorCreation()) 78 | { 79 | MessageUtils.Log(LogLevel.Error, $"Error while Handling the creation of an allocator"); 80 | return; 81 | } 82 | 83 | 84 | var allocationData = this._allocator.Allocate(player, roundType); 85 | 86 | 87 | 88 | if (player?.PlayerPawn?.Value?.ItemServices == null) 89 | { 90 | MessageUtils.Log(LogLevel.Error,$"Player has no item service"); 91 | return; 92 | } 93 | 94 | var itemService = new CCSPlayer_ItemServices(player.PlayerPawn.Value.ItemServices.Handle); 95 | 96 | foreach (var grenade in allocationData.grenades) 97 | { 98 | var enumMemberValue = EnumUtils.GetEnumMemberAttributeValue(grenade); 99 | 100 | if (!string.IsNullOrWhiteSpace(enumMemberValue)) 101 | { 102 | player.GiveNamedItem(enumMemberValue); 103 | } 104 | 105 | } 106 | 107 | if (!string.IsNullOrWhiteSpace(allocationData.secondaryWeapon)) 108 | { 109 | player.GiveNamedItem(allocationData.secondaryWeapon); 110 | } 111 | if (!string.IsNullOrWhiteSpace(allocationData.primaryWeapon)) 112 | { 113 | player.GiveNamedItem(allocationData.primaryWeapon); 114 | } 115 | 116 | if(allocationData.zeus) 117 | { 118 | player.GiveNamedItem(CsItem.Taser); 119 | } 120 | 121 | if (allocationData.kit && player.Team == CsTeam.CounterTerrorist) 122 | { 123 | itemService.HasDefuser = true; 124 | } 125 | 126 | switch (allocationData.kevlar) 127 | { 128 | case KevlarEnum.Kevlar: 129 | player.GiveNamedItem(CsItem.Kevlar); 130 | break; 131 | case KevlarEnum.KevlarHelmet: 132 | player.GiveNamedItem(CsItem.AssaultSuit); 133 | itemService.HasHelmet = true; 134 | break; 135 | } 136 | 137 | player.ExecuteClientCommand($"slot3; slot2; slot1"); 138 | } 139 | 140 | public void RemoveWeapons(CCSPlayerController player) 141 | { 142 | if (player == null || !player.IsValid) 143 | { 144 | MessageUtils.Log(LogLevel.Error, $"Player is null or not valid"); 145 | return; 146 | } 147 | 148 | if (player.PlayerPawn == null || !player.PlayerPawn.IsValid || player.PlayerPawn.Value == null || !player.PlayerPawn.Value.IsValid) 149 | { 150 | MessageUtils.Log(LogLevel.Warning, $"PlayerPawn is null or not valid. This might be because of a disconnected player."); 151 | return; 152 | } 153 | 154 | var weaponService = player?.PlayerPawn?.Value?.WeaponServices ?? null; 155 | 156 | if (weaponService == null) 157 | { 158 | MessageUtils.Log(LogLevel.Error, $"WeaponService of player is null"); 159 | return; 160 | } 161 | 162 | var playerWeaponService = new CCSPlayer_WeaponServices(weaponService.Handle); 163 | 164 | if (playerWeaponService == null) 165 | { 166 | MessageUtils.Log(LogLevel.Error, $"PlayerWeaponService is null"); 167 | return; 168 | } 169 | 170 | playerWeaponService.MyWeapons.Where(weapon => weapon != null && weapon.IsValid && weapon.Value != null && weapon.Value.IsValid && !weapon.Value.DesignerName.Contains("knife")).ToList().ForEach(weapon => weapon.Value?.Remove()); 171 | 172 | var playerPawn = player?.PlayerPawn?.Value; 173 | 174 | if(playerPawn == null || !playerPawn.IsValid) 175 | { 176 | MessageUtils.Log(LogLevel.Error, $"PlayerPawn is null or not valid"); 177 | return; 178 | } 179 | 180 | playerPawn.ArmorValue = 0; 181 | 182 | var itemService = player?.PlayerPawn?.Value?.ItemServices ?? null; 183 | 184 | if(itemService == null) 185 | { 186 | MessageUtils.Log(LogLevel.Error, $"Player has no item service"); 187 | return; 188 | } 189 | 190 | var playerItemService = new CCSPlayer_ItemServices(itemService.Handle); 191 | 192 | if(playerItemService == null) 193 | { 194 | MessageUtils.Log(LogLevel.Error, $"PlayerItemService is null"); 195 | return; 196 | } 197 | 198 | playerItemService.HasHelmet = false; 199 | } 200 | 201 | public void OnGunsCommand(CCSPlayerController? player) 202 | { 203 | if (player == null || !player.IsValid) 204 | { 205 | MessageUtils.Log(LogLevel.Error, $"Player is null or not valid"); 206 | return; 207 | } 208 | 209 | if (player.PlayerPawn == null || !player.PlayerPawn.IsValid || player.PlayerPawn.Value == null || !player.PlayerPawn.Value.IsValid) 210 | { 211 | MessageUtils.Log(LogLevel.Warning, $"PlayerPawn is null or not valid. This might be because of a disconnected player."); 212 | return; 213 | } 214 | 215 | if (!this.HandleAllocatorCreation()) 216 | { 217 | MessageUtils.Log(LogLevel.Error, $"Error while Handling the creation of an allocator"); 218 | return; 219 | } 220 | 221 | this._allocator.OnGunsCommand(player); 222 | } 223 | 224 | public void OnPlayerConnected(CCSPlayerController? player) 225 | { 226 | if (player == null || !player.IsValid) 227 | { 228 | MessageUtils.Log(LogLevel.Error, $"Player is null or not valid"); 229 | return; 230 | } 231 | 232 | if (player.PlayerPawn == null || !player.PlayerPawn.IsValid || player.PlayerPawn.Value == null || !player.PlayerPawn.Value.IsValid) 233 | { 234 | MessageUtils.Log(LogLevel.Warning, $"PlayerPawn is null or not valid. This might be because of a disconnected player."); 235 | return; 236 | } 237 | 238 | if (!this.HandleAllocatorCreation()) 239 | { 240 | MessageUtils.Log(LogLevel.Error, $"Error while Handling the creation of an allocator"); 241 | return; 242 | } 243 | 244 | this._allocator.OnPlayerConnected(player); 245 | } 246 | 247 | public void OnPlayerDisconnected(CCSPlayerController? player) 248 | { 249 | if (player == null || !player.IsValid) 250 | { 251 | MessageUtils.Log(LogLevel.Error, $"Player is null or not valid"); 252 | return; 253 | } 254 | 255 | if (player.PlayerPawn == null || !player.PlayerPawn.IsValid || player.PlayerPawn.Value == null || !player.PlayerPawn.Value.IsValid) 256 | { 257 | MessageUtils.Log(LogLevel.Warning, $"PlayerPawn is null or not valid. This might be because of a disconnected player."); 258 | return; 259 | } 260 | 261 | if (!this.HandleAllocatorCreation()) 262 | { 263 | MessageUtils.Log(LogLevel.Error, $"Error while Handling the creation of an allocator"); 264 | return; 265 | } 266 | 267 | this._allocator.OnPlayerDisconnected(player); 268 | } 269 | 270 | public override void ResetForNextMap(bool completeReset = true) 271 | { 272 | if (!this.HandleAllocatorCreation()) 273 | { 274 | MessageUtils.Log(LogLevel.Error, $"Error while Handling the creation of an allocator"); 275 | return; 276 | } 277 | 278 | this._allocator.ResetForNextRound(); 279 | } 280 | 281 | private bool HandleAllocatorCreation() 282 | { 283 | if (this._allocator == null) 284 | { 285 | AllocatorFactory factory = new AllocatorFactory(); 286 | this._allocator = factory.GetAllocator(RuntimeConfig.Allocator, this.PluginInstance); 287 | } 288 | 289 | return this._allocator != null; 290 | } 291 | 292 | public override void ResetForNextRound(bool completeReset = true) 293 | { 294 | if(completeReset) 295 | { 296 | 297 | } 298 | 299 | RoundTypeManager.Instance.HandleRoundType(); 300 | this._allocator?.ResetForNextRound(); 301 | 302 | } 303 | 304 | 305 | } 306 | } 307 | -------------------------------------------------------------------------------- /CS2Retake/Allocators/Implementations/CommandAllocator/Manager/CacheManager.cs: -------------------------------------------------------------------------------- 1 | using CounterStrikeSharp.API.Core; 2 | using CounterStrikeSharp.API.Modules.Utils; 3 | using CS2Retake.Managers; 4 | using CS2Retake.Utils; 5 | using System; 6 | using System.Collections.Generic; 7 | using System.Linq; 8 | using System.Text; 9 | using System.Threading.Tasks; 10 | 11 | namespace CS2Retake.Allocators.Implementations.CommandAllocator.Manager 12 | { 13 | public class CacheManager 14 | { 15 | private static CacheManager? _instance = null; 16 | public static CacheManager Instance 17 | { 18 | get 19 | { 20 | if (_instance == null) 21 | { 22 | _instance = new CacheManager(); 23 | } 24 | return _instance; 25 | } 26 | } 27 | 28 | private CacheManager() { } 29 | 30 | private Dictionary _fullBuyPrimaryCacheCT { get; set; } = new Dictionary(); 31 | private Dictionary _fullBuySecondaryCacheCT { get; set; } = new Dictionary(); 32 | private Dictionary _fullBuyAWPChanceCacheCT { get; set; } = new Dictionary(); 33 | private Dictionary _midPrimaryCacheCT { get; set; } = new Dictionary(); 34 | private Dictionary _midSecondaryCacheCT { get; set; } = new Dictionary(); 35 | private Dictionary _pistolCacheCT { get; set; } = new Dictionary(); 36 | 37 | 38 | public void AddOrUpdateFullBuyPrimaryCacheCT(CCSPlayerController player, string weaponString) 39 | { 40 | if (_fullBuyPrimaryCacheCT.ContainsKey(player.SteamID)) 41 | { 42 | _fullBuyPrimaryCacheCT[player.SteamID] = weaponString; 43 | } 44 | else 45 | { 46 | _fullBuyPrimaryCacheCT.Add(player.SteamID, weaponString); 47 | } 48 | } 49 | 50 | public void AddOrUpdateFullBuySecondaryCacheCT(CCSPlayerController player, string weaponString) 51 | { 52 | if (_fullBuySecondaryCacheCT.ContainsKey(player.SteamID)) 53 | { 54 | _fullBuySecondaryCacheCT[player.SteamID] = weaponString; 55 | } 56 | else 57 | { 58 | _fullBuySecondaryCacheCT.Add(player.SteamID, weaponString); 59 | } 60 | } 61 | 62 | public void AddOrUpdateFullBuyAWPChanceCacheCT(CCSPlayerController player, int awpChance) 63 | { 64 | if (_fullBuyAWPChanceCacheCT.ContainsKey(player.SteamID)) 65 | { 66 | _fullBuyAWPChanceCacheCT[player.SteamID] = awpChance; 67 | } 68 | else 69 | { 70 | _fullBuyAWPChanceCacheCT.Add(player.SteamID, awpChance); 71 | } 72 | } 73 | 74 | public void AddOrUpdateMidPrimaryCacheCT(CCSPlayerController player, string weaponString) 75 | { 76 | if (_midPrimaryCacheCT.ContainsKey(player.SteamID)) 77 | { 78 | _midPrimaryCacheCT[player.SteamID] = weaponString; 79 | } 80 | else 81 | { 82 | _midPrimaryCacheCT.Add(player.SteamID, weaponString); 83 | } 84 | } 85 | 86 | public void AddOrUpdateMidSecondaryCacheCT(CCSPlayerController player, string weaponString) 87 | { 88 | if (_midSecondaryCacheCT.ContainsKey(player.SteamID)) 89 | { 90 | _midSecondaryCacheCT[player.SteamID] = weaponString; 91 | } 92 | else 93 | { 94 | _midSecondaryCacheCT.Add(player.SteamID, weaponString); 95 | } 96 | } 97 | 98 | public void AddOrUpdatePistolCacheCT(CCSPlayerController player, string weaponString) 99 | { 100 | if (_pistolCacheCT.ContainsKey(player.SteamID)) 101 | { 102 | _pistolCacheCT[player.SteamID] = weaponString; 103 | } 104 | else 105 | { 106 | _pistolCacheCT.Add(player.SteamID, weaponString); 107 | } 108 | } 109 | 110 | private Dictionary _fullBuyPrimaryCacheT { get; set; } = new Dictionary(); 111 | private Dictionary _fullBuySecondaryCacheT { get; set; } = new Dictionary(); 112 | private Dictionary _fullBuyAWPChanceCacheT { get; set; } = new Dictionary(); 113 | private Dictionary _midPrimaryCacheT { get; set; } = new Dictionary(); 114 | private Dictionary _midSecondaryCacheT { get; set; } = new Dictionary(); 115 | private Dictionary _pistolCacheT { get; set; } = new Dictionary(); 116 | 117 | 118 | public void AddOrUpdateFullBuyPrimaryCacheT(CCSPlayerController player, string weaponString) 119 | { 120 | if (_fullBuyPrimaryCacheT.ContainsKey(player.SteamID)) 121 | { 122 | _fullBuyPrimaryCacheT[player.SteamID] = weaponString; 123 | } 124 | else 125 | { 126 | _fullBuyPrimaryCacheT.Add(player.SteamID, weaponString); 127 | } 128 | } 129 | 130 | public void AddOrUpdateFullBuySecondaryCacheT(CCSPlayerController player, string weaponString) 131 | { 132 | if (_fullBuySecondaryCacheT.ContainsKey(player.SteamID)) 133 | { 134 | _fullBuySecondaryCacheT[player.SteamID] = weaponString; 135 | } 136 | else 137 | { 138 | _fullBuySecondaryCacheT.Add(player.SteamID, weaponString); 139 | } 140 | } 141 | 142 | public void AddOrUpdateFullBuyAWPChanceCacheT(CCSPlayerController player, int awpChance) 143 | { 144 | if (_fullBuyAWPChanceCacheT.ContainsKey(player.SteamID)) 145 | { 146 | _fullBuyAWPChanceCacheT[player.SteamID] = awpChance; 147 | } 148 | else 149 | { 150 | _fullBuyAWPChanceCacheT.Add(player.SteamID, awpChance); 151 | } 152 | } 153 | 154 | public void AddOrUpdateMidPrimaryCacheT(CCSPlayerController player, string weaponString) 155 | { 156 | if (_midPrimaryCacheT.ContainsKey(player.SteamID)) 157 | { 158 | _midPrimaryCacheT[player.SteamID] = weaponString; 159 | } 160 | else 161 | { 162 | _midPrimaryCacheT.Add(player.SteamID, weaponString); 163 | } 164 | } 165 | 166 | public void AddOrUpdateMidSecondaryCacheT(CCSPlayerController player, string weaponString) 167 | { 168 | if (_midSecondaryCacheT.ContainsKey(player.SteamID)) 169 | { 170 | _midSecondaryCacheT[player.SteamID] = weaponString; 171 | } 172 | else 173 | { 174 | _midSecondaryCacheT.Add(player.SteamID, weaponString); 175 | } 176 | } 177 | 178 | public void AddOrUpdatePistolCacheT(CCSPlayerController player, string weaponString) 179 | { 180 | if (_pistolCacheT.ContainsKey(player.SteamID)) 181 | { 182 | _pistolCacheT[player.SteamID] = weaponString; 183 | } 184 | else 185 | { 186 | _pistolCacheT.Add(player.SteamID, weaponString); 187 | } 188 | } 189 | 190 | 191 | public (string? primaryWeapon, string? secondaryWeapon, int? awpChance) GetFullBuyWeapons(CCSPlayerController player) 192 | { 193 | 194 | if (player.Team == CsTeam.CounterTerrorist) 195 | { 196 | _ = this._fullBuyPrimaryCacheCT.TryGetValue(player.SteamID, out string? primary); 197 | _ = this._fullBuySecondaryCacheCT.TryGetValue(player.SteamID, out string? secondary); 198 | _ = this._fullBuyAWPChanceCacheCT.TryGetValue(player.SteamID, out int awpChance); 199 | 200 | return (primary, secondary, awpChance); 201 | } 202 | else 203 | { 204 | _ = this._fullBuyPrimaryCacheT.TryGetValue(player.SteamID, out string? primary); 205 | _ = this._fullBuySecondaryCacheT.TryGetValue(player.SteamID, out string? secondary); 206 | _ = this._fullBuyAWPChanceCacheT.TryGetValue(player.SteamID, out int awpChance); 207 | 208 | return (primary, secondary, awpChance); 209 | } 210 | 211 | 212 | 213 | } 214 | 215 | public (string? primaryWeapon, string? secondaryWeapon, int? awpChance) GetMidWeapons(CCSPlayerController player) 216 | { 217 | if (player.Team == CsTeam.CounterTerrorist) 218 | { 219 | _ = this._midPrimaryCacheCT.TryGetValue(player.SteamID, out string? primary); 220 | _ = this._midSecondaryCacheCT.TryGetValue(player.SteamID, out string? secondary); 221 | 222 | return (primary, secondary, null); 223 | } 224 | else 225 | { 226 | _ = this._midPrimaryCacheT.TryGetValue(player.SteamID, out string? primary); 227 | _ = this._midSecondaryCacheT.TryGetValue(player.SteamID, out string? secondary); 228 | 229 | return (primary, secondary, null); 230 | } 231 | } 232 | 233 | public (string? primaryWeapon, string? secondaryWeapon, int? awpChance) GetPistolWeapons(CCSPlayerController player) 234 | { 235 | if (player.Team == CsTeam.CounterTerrorist) 236 | { 237 | _ = this._pistolCacheCT.TryGetValue(player.SteamID, out string? secondary); 238 | 239 | return (string.Empty, secondary, null); 240 | } 241 | else 242 | { 243 | _ = this._pistolCacheT.TryGetValue(player.SteamID, out string? secondary); 244 | 245 | return (string.Empty, secondary, null); 246 | } 247 | 248 | 249 | } 250 | 251 | public void AddOrUpdateFullBuyPrimaryCache(CCSPlayerController player, string weaponString, CsTeam team) 252 | { 253 | if (team == CsTeam.CounterTerrorist) 254 | { 255 | this.AddOrUpdateFullBuyPrimaryCacheCT(player, weaponString); 256 | } 257 | else 258 | { 259 | this.AddOrUpdateFullBuyPrimaryCacheT(player, weaponString); 260 | } 261 | } 262 | 263 | public void AddOrUpdateFullBuySecondaryCache(CCSPlayerController player, string weaponString, CsTeam team) 264 | { 265 | if (team == CsTeam.CounterTerrorist) 266 | { 267 | this.AddOrUpdateFullBuySecondaryCacheCT(player, weaponString); 268 | } 269 | else 270 | { 271 | this.AddOrUpdateFullBuySecondaryCacheT(player, weaponString); 272 | } 273 | } 274 | 275 | public void AddOrUpdateFullBuyAWPChanceCache(CCSPlayerController player, int awpChance, CsTeam team) 276 | { 277 | if (team == CsTeam.CounterTerrorist) 278 | { 279 | this.AddOrUpdateFullBuyAWPChanceCacheCT(player, awpChance); 280 | } 281 | else 282 | { 283 | this.AddOrUpdateFullBuyAWPChanceCacheT(player, awpChance); 284 | } 285 | } 286 | 287 | public void AddOrUpdateMidPrimaryCache(CCSPlayerController player, string weaponString, CsTeam team) 288 | { 289 | if (team == CsTeam.CounterTerrorist) 290 | { 291 | this.AddOrUpdateMidPrimaryCacheCT(player, weaponString); 292 | } 293 | else 294 | { 295 | this.AddOrUpdateMidPrimaryCacheT(player, weaponString); 296 | } 297 | } 298 | 299 | public void AddOrUpdateMidSecondaryCache(CCSPlayerController player, string weaponString, CsTeam team) 300 | { 301 | if (team == CsTeam.CounterTerrorist) 302 | { 303 | this.AddOrUpdateMidSecondaryCacheCT(player, weaponString); 304 | } 305 | else 306 | { 307 | this.AddOrUpdateMidSecondaryCacheT(player, weaponString); 308 | } 309 | } 310 | 311 | public void AddOrUpdatePistolCache(CCSPlayerController player, string weaponString, CsTeam team) 312 | { 313 | if (team == CsTeam.CounterTerrorist) 314 | { 315 | this.AddOrUpdatePistolCacheCT(player, weaponString); 316 | } 317 | else 318 | { 319 | this.AddOrUpdatePistolCacheT(player, weaponString); 320 | } 321 | } 322 | 323 | public void RemoveUserFromCache(CCSPlayerController player) 324 | { 325 | var userId = player.SteamID; 326 | 327 | this._fullBuyPrimaryCacheCT.Remove(userId); 328 | this._fullBuySecondaryCacheCT.Remove(userId); 329 | this._fullBuyAWPChanceCacheCT.Remove(userId); 330 | this._midPrimaryCacheCT.Remove(userId); 331 | this._midSecondaryCacheCT.Remove(userId); 332 | this._pistolCacheCT.Remove(userId); 333 | 334 | this._fullBuyPrimaryCacheT.Remove(userId); 335 | this._fullBuySecondaryCacheT.Remove(userId); 336 | this._fullBuyAWPChanceCacheT.Remove(userId); 337 | this._midPrimaryCacheT.Remove(userId); 338 | this._midSecondaryCacheT.Remove(userId); 339 | this._pistolCacheT.Remove(userId); 340 | } 341 | } 342 | } 343 | -------------------------------------------------------------------------------- /CS2Retake/Allocators/Implementations/CommandAllocator/CommandAllocator.cs: -------------------------------------------------------------------------------- 1 | using CounterStrikeSharp.API.Core; 2 | using CS2Retake.Allocators.Implementations.CommandAllocator.Configs; 3 | using CSZoneNet.Plugin.CS2BaseAllocator.Configs.Interfaces; 4 | using CSZoneNet.Plugin.CS2BaseAllocator; 5 | using CSZoneNet.Plugin.Utils.Enums; 6 | using System; 7 | using System.Collections.Generic; 8 | using System.Linq; 9 | using System.Text; 10 | using System.Threading.Tasks; 11 | using CS2Retake.Utils; 12 | using Microsoft.Extensions.Logging; 13 | using CS2Retake.Allocators.Implementations.CommandAllocator.Menus; 14 | using CS2Retake.Allocators.Implementations.CommandAllocator.Manager; 15 | using CounterStrikeSharp.API.Modules.Utils; 16 | using CS2Retake.Allocators.Implementations.CommandAllocator.Entities; 17 | using CounterStrikeSharp.API; 18 | using CounterStrikeSharp.API.Modules.Timers; 19 | 20 | namespace CS2Retake.Allocators.Implementations.CommandAllocator 21 | { 22 | public class CommandAllocator : BaseGrenadeAllocator, IAllocatorConfig, IDisposable 23 | { 24 | public CommandAllocatorConfig Config { get; set; } = new CommandAllocatorConfig(); 25 | 26 | private ChanceEntity _awpChanceCT { get; set; } 27 | private ChanceEntity _awpChanceT { get; set; } 28 | 29 | private int _awpInUseCountCT { get; set; } = 0; 30 | private int _awpInUseCountT { get; set; } = 0; 31 | 32 | private CounterStrikeSharp.API.Modules.Timers.Timer? _howToTimer { get; set; } = null; 33 | 34 | public CommandAllocator() 35 | { 36 | 37 | } 38 | 39 | public override (string primaryWeapon, string secondaryWeapon, KevlarEnum kevlar, bool kit, bool zeus, List grenades) Allocate(CCSPlayerController player, RoundTypeEnum roundType = RoundTypeEnum.Undefined) 40 | { 41 | (string primaryWeapon, string secondaryWeapon, KevlarEnum kevlar, bool kit, bool zeus, List grenades) returnValue = ("", "weapon_deagle", KevlarEnum.KevlarHelmet, true, false, new List()); 42 | 43 | if (player == null || !player.IsValid || player.PlayerPawn == null || !player.PlayerPawn.IsValid || player.PlayerPawn.Value == null || !player.PlayerPawn.Value!.IsValid) 44 | { 45 | return returnValue; 46 | } 47 | 48 | var random = new Random(); 49 | 50 | //GRENADES 51 | 52 | var grenades = this.AllocateGrenades(player, roundType); 53 | 54 | if (grenades == null) 55 | { 56 | return returnValue; 57 | } 58 | 59 | returnValue.grenades = grenades; 60 | 61 | //RIFLES AND PISTOLS 62 | 63 | (string? primary, string? secondary, int? awpChance) weapons = ("", "", null); 64 | (string primary, string secondary, int? awpChance) defaultWeapons = ("", "weapon_deagle", 0); 65 | switch (roundType) 66 | { 67 | case RoundTypeEnum.FullBuy: 68 | weapons = CacheManager.Instance.GetFullBuyWeapons(player); 69 | defaultWeapons.primary = player.Team == CsTeam.CounterTerrorist ? "weapon_m4a1" : "weapon_ak47"; 70 | 71 | var chanceEntity = player.Team == CsTeam.CounterTerrorist ? this._awpChanceCT : this._awpChanceT; 72 | 73 | if((player.Team == CsTeam.CounterTerrorist && chanceEntity.Limit <= this._awpInUseCountCT) || (player.Team == CsTeam.Terrorist && chanceEntity.Limit <= this._awpInUseCountT)) 74 | { 75 | break; 76 | } 77 | 78 | var randomValue = random.Next(1, 100); 79 | 80 | if(randomValue <= weapons.awpChance) 81 | { 82 | weapons.primary = "weapon_awp"; 83 | 84 | if(player.Team == CsTeam.CounterTerrorist) 85 | { 86 | this._awpInUseCountCT++; 87 | } 88 | else 89 | { 90 | this._awpInUseCountT++; 91 | } 92 | } 93 | 94 | 95 | break; 96 | case RoundTypeEnum.Mid: 97 | weapons = CacheManager.Instance.GetMidWeapons(player); 98 | defaultWeapons.primary = player.Team == CsTeam.CounterTerrorist ? "weapon_mp9" : "weapon_mac10"; 99 | break; 100 | case RoundTypeEnum.Pistol: 101 | weapons = CacheManager.Instance.GetPistolWeapons(player); 102 | defaultWeapons.secondary = player.Team == CsTeam.CounterTerrorist ? "weapon_usp_silencer" : "weapon_glock"; 103 | break; 104 | 105 | } 106 | 107 | MessageUtils.LogDebug($"SteamID: {player.SteamID} - Default: {defaultWeapons.primary}, {defaultWeapons.secondary}, {defaultWeapons.awpChance} - Selected: {weapons.primary}, {weapons.secondary}, {weapons.awpChance}"); 108 | 109 | if (string.IsNullOrWhiteSpace(weapons.primary)) 110 | { 111 | returnValue.primaryWeapon = defaultWeapons.primary; 112 | } 113 | else 114 | { 115 | returnValue.primaryWeapon = weapons.primary; 116 | } 117 | if (string.IsNullOrWhiteSpace(weapons.secondary)) 118 | { 119 | returnValue.secondaryWeapon = defaultWeapons.secondary; 120 | } 121 | else 122 | { 123 | returnValue.secondaryWeapon = weapons.secondary; 124 | } 125 | 126 | 127 | //ZEUS 128 | if (this.Config.EnableZeus) 129 | { 130 | returnValue.zeus = random.Next(1,100) <= this.Config.ZeusChance; 131 | } 132 | 133 | 134 | //DEFUSEKIT 135 | returnValue.kit = this.Config.DefuseKitChance == 100 || random.Next(1, 100) <= this.Config.DefuseKitChance; 136 | 137 | 138 | 139 | return returnValue; 140 | } 141 | 142 | public void OnAllocatorConfigParsed(CommandAllocatorConfig config) 143 | { 144 | if (this.Config.Version > config.Version) 145 | { 146 | MessageUtils.Log(Microsoft.Extensions.Logging.LogLevel.Warning, $"The command allocator configuration is out of date. Consider updating the config. [Current Version: {config.Version} - Allocator Version: {this.Config.Version}]"); 147 | } 148 | 149 | this.Config = config; 150 | 151 | var fullBuyConfig = FullBuyMenu.Instance.Config; 152 | _ = MidMenu.Instance; 153 | _ = PistolMenu.Instance; 154 | 155 | this._awpChanceCT = fullBuyConfig.AWPChanceCT; 156 | this._awpChanceT = fullBuyConfig.AWPChanceT; 157 | 158 | MessageUtils.Log(LogLevel.Warning, $"For some reason sometimes an exception happens with the sqlite stuff. It still works. So no worries needed."); 159 | 160 | DBManager.Instance.DBType = Config.DatabaseType; 161 | DBManager.Instance.AllocatorConfigDirectoryPath = Config.AllocatorConfigDirectoryPath; 162 | DBManager.Instance.ConnectionString = Config.ConnectionString; 163 | DBManager.Instance.Init(); 164 | 165 | PlayerUtils.GetValidPlayerControllers().ForEach(x => this.OnPlayerConnected(x)); 166 | 167 | 168 | if(this.Config.HowToMessageDelayInMinutes > 0) 169 | { 170 | this._howToTimer = new CounterStrikeSharp.API.Modules.Timers.Timer(this.Config.HowToMessageDelayInMinutes * 60, PrintHowToMessage, CounterStrikeSharp.API.Modules.Timers.TimerFlags.REPEAT); 171 | } 172 | 173 | } 174 | 175 | public override void OnGunsCommand(CCSPlayerController? player) 176 | { 177 | if(this.BasePluginInstance == null) 178 | { 179 | return; 180 | } 181 | 182 | ChooserMenu.OpenMenu(player, this.BasePluginInstance, this.Config.EnableRoundTypePistolMenu, this.Config.EnableRoundTypeMidMenu, this.Config.EnableRoundTypeFullBuyMenu); 183 | } 184 | 185 | public override void OnPlayerConnected(CCSPlayerController? player) 186 | { 187 | if (player == null || !player.IsValid || player.PlayerPawn == null || !player.PlayerPawn.IsValid || player.PlayerPawn.Value == null || !player.PlayerPawn.Value!.IsValid) 188 | { 189 | return; 190 | } 191 | 192 | //----------------------------FULLBUY-------------------------------------- 193 | 194 | var fullBuyConfig = FullBuyMenu.Instance.Config; 195 | 196 | (string? primaryWeapon, string? secondaryWeapon, int? awpChance) fullBuyCT = DBManager.Instance.GetFullBuyWeapons(player.SteamID, (int)CsTeam.CounterTerrorist); 197 | 198 | if(!string.IsNullOrWhiteSpace(fullBuyCT.primaryWeapon)) 199 | { 200 | CacheManager.Instance.AddOrUpdateFullBuyPrimaryCache(player, fullBuyCT.primaryWeapon, CsTeam.CounterTerrorist); 201 | } 202 | if (!string.IsNullOrWhiteSpace(fullBuyCT.secondaryWeapon)) 203 | { 204 | CacheManager.Instance.AddOrUpdateFullBuySecondaryCache(player, fullBuyCT.secondaryWeapon, CsTeam.CounterTerrorist); 205 | } 206 | if(fullBuyCT.awpChance != null && fullBuyCT.awpChance.HasValue) 207 | { 208 | var chance = fullBuyCT.awpChance.Value; 209 | var highestChance = fullBuyConfig.AWPChanceCT.Chances.OrderDescending().FirstOrDefault(); 210 | 211 | if (!fullBuyConfig.EnableAWPChance) 212 | { 213 | chance = 0; 214 | } 215 | 216 | CacheManager.Instance.AddOrUpdateFullBuyAWPChanceCache(player, chance <= highestChance ? chance : highestChance, CsTeam.CounterTerrorist); 217 | } 218 | 219 | var fullBuyT = DBManager.Instance.GetFullBuyWeapons(player.SteamID, (int)CsTeam.Terrorist); 220 | 221 | if (!string.IsNullOrWhiteSpace(fullBuyT.primaryWeapon)) 222 | { 223 | CacheManager.Instance.AddOrUpdateFullBuyPrimaryCache(player, fullBuyT.primaryWeapon, CsTeam.Terrorist); 224 | } 225 | if (!string.IsNullOrWhiteSpace(fullBuyT.secondaryWeapon)) 226 | { 227 | CacheManager.Instance.AddOrUpdateFullBuySecondaryCache(player, fullBuyT.secondaryWeapon, CsTeam.Terrorist); 228 | } 229 | if (fullBuyT.awpChance != null && fullBuyT.awpChance.HasValue) 230 | { 231 | var chance = fullBuyT.awpChance.Value; 232 | var highestChance = fullBuyConfig.AWPChanceT.Chances.OrderDescending().FirstOrDefault(); 233 | 234 | if(!fullBuyConfig.EnableAWPChance) 235 | { 236 | chance = 0; 237 | } 238 | 239 | CacheManager.Instance.AddOrUpdateFullBuyAWPChanceCache(player, chance <= highestChance ? chance : highestChance, CsTeam.Terrorist); 240 | } 241 | 242 | MessageUtils.LogDebug($"SteamID: {player.SteamID} - CT: {fullBuyCT.primaryWeapon}, {fullBuyCT.secondaryWeapon}, {fullBuyCT.awpChance} - T: {fullBuyT.primaryWeapon}, {fullBuyT.secondaryWeapon}, {fullBuyT.awpChance}"); 243 | 244 | //----------------------------MID-------------------------------------- 245 | 246 | var midCT = DBManager.Instance.GetMidWeapons(player.SteamID, (int)CsTeam.CounterTerrorist); 247 | 248 | if (!string.IsNullOrWhiteSpace(midCT.primaryWeapon)) 249 | { 250 | CacheManager.Instance.AddOrUpdateMidPrimaryCache(player, midCT.primaryWeapon, CsTeam.CounterTerrorist); 251 | } 252 | if (!string.IsNullOrWhiteSpace(midCT.secondaryWeapon)) 253 | { 254 | CacheManager.Instance.AddOrUpdateMidSecondaryCache(player, midCT.secondaryWeapon, CsTeam.CounterTerrorist); 255 | } 256 | 257 | var midT = DBManager.Instance.GetMidWeapons(player.SteamID, (int)CsTeam.Terrorist); 258 | 259 | if (!string.IsNullOrWhiteSpace(midT.primaryWeapon)) 260 | { 261 | CacheManager.Instance.AddOrUpdateMidPrimaryCache(player, midT.primaryWeapon, CsTeam.Terrorist); 262 | } 263 | if (!string.IsNullOrWhiteSpace(midT.secondaryWeapon)) 264 | { 265 | CacheManager.Instance.AddOrUpdateMidSecondaryCache(player, midT.secondaryWeapon, CsTeam.Terrorist); 266 | } 267 | 268 | MessageUtils.LogDebug($"SteamID: {player.SteamID} - CT: {midCT.primaryWeapon}, {midCT.secondaryWeapon}, {midCT.awpChance} - T: {midT.primaryWeapon}, {midT.secondaryWeapon}, {midT.awpChance}"); 269 | 270 | //----------------------------PISTOLS-------------------------------------- 271 | 272 | var pistolCT = DBManager.Instance.GetPistolWeapons(player.SteamID, (int)CsTeam.CounterTerrorist); 273 | 274 | if (!string.IsNullOrWhiteSpace(pistolCT.secondaryWeapon)) 275 | { 276 | CacheManager.Instance.AddOrUpdatePistolCache(player, pistolCT.secondaryWeapon, CsTeam.CounterTerrorist); 277 | } 278 | 279 | var pistolT = DBManager.Instance.GetPistolWeapons(player.SteamID, (int)CsTeam.Terrorist); 280 | 281 | if (!string.IsNullOrWhiteSpace(pistolT.secondaryWeapon)) 282 | { 283 | CacheManager.Instance.AddOrUpdatePistolCache(player, pistolT.secondaryWeapon, CsTeam.Terrorist); 284 | } 285 | 286 | MessageUtils.LogDebug($"SteamID: {player.SteamID} - CT: {pistolCT.primaryWeapon}, {pistolCT.secondaryWeapon}, {pistolCT.awpChance} - T: {pistolT.primaryWeapon}, {pistolT.secondaryWeapon}, {pistolT.awpChance}"); 287 | } 288 | 289 | public override void OnPlayerDisconnected(CCSPlayerController? player) 290 | { 291 | if (player == null || !player.IsValid || player.PlayerPawn == null || !player.PlayerPawn.IsValid || player.PlayerPawn.Value == null || !player.PlayerPawn.Value!.IsValid) 292 | { 293 | return; 294 | } 295 | 296 | CacheManager.Instance.RemoveUserFromCache(player); 297 | } 298 | 299 | public override void ResetForNextRound(bool completeReset = true) 300 | { 301 | this._awpInUseCountCT = 0; 302 | this._awpInUseCountT = 0; 303 | } 304 | 305 | private void PrintHowToMessage() 306 | { 307 | Server.PrintToChatAll($"[{ChatColors.Gold}CommandAllocator{ChatColors.White}] {this.Config.HowToMessage}"); 308 | } 309 | 310 | public void Dispose() 311 | { 312 | this._howToTimer?.Kill(); 313 | } 314 | } 315 | } 316 | -------------------------------------------------------------------------------- /CS2Retake/Allocators/Implementations/CommandAllocator/Repository/SQLiteRepository.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | using System.Data.SQLite; 7 | using CS2Retake.Utils; 8 | using CS2Retake.Allocators.Implementations.CommandAllocator.Interfaces; 9 | 10 | namespace CS2Retake.Allocators.Implementations.CommandAllocator.Repository 11 | { 12 | public class SQLiteRepository : IDisposable, IRetakeRepository 13 | { 14 | private SQLiteConnection _connection; 15 | 16 | public SQLiteRepository(string path) 17 | { 18 | this._connection = new SQLiteConnection($"Data Source={path}/cs2retake.db;Version=3;"); 19 | this.Init(); 20 | } 21 | 22 | private void OpenConnection() 23 | { 24 | try 25 | { 26 | this._connection.Open(); 27 | } 28 | catch (Exception ex) 29 | { 30 | MessageUtils.Log(Microsoft.Extensions.Logging.LogLevel.Error, $"Error while creating a connection to the cs2retake.db - Message: {ex.Message}"); 31 | } 32 | } 33 | 34 | public void Init() 35 | { 36 | SQLiteCommand? cmd = null; 37 | 38 | try 39 | { 40 | if (this._connection.State != System.Data.ConnectionState.Open) 41 | { 42 | this.OpenConnection(); 43 | } 44 | 45 | cmd = this._connection.CreateCommand(); 46 | 47 | cmd.CommandText = $"CREATE TABLE IF NOT EXISTS FullBuyPrimary (UserId UNSIGNED BIG INT, WeaponString VARCHAR(255), Team INT)"; 48 | cmd.ExecuteNonQuery(); 49 | 50 | cmd.CommandText = $"CREATE TABLE IF NOT EXISTS FullBuySecondary (UserId UNSIGNED BIG INT, WeaponString VARCHAR(255), Team INT)"; 51 | cmd.ExecuteNonQuery(); 52 | 53 | cmd.CommandText = $"CREATE TABLE IF NOT EXISTS FullBuyAWPChance (UserId UNSIGNED BIG INT, AWPChance INT, Team INT)"; 54 | cmd.ExecuteNonQuery(); 55 | 56 | cmd.CommandText = $"CREATE TABLE IF NOT EXISTS MidPrimary (UserId UNSIGNED BIG INT, WeaponString VARCHAR(255), Team INT)"; 57 | cmd.ExecuteNonQuery(); 58 | 59 | cmd.CommandText = $"CREATE TABLE IF NOT EXISTS MidSecondary (UserId UNSIGNED BIG INT, WeaponString VARCHAR(255), Team INT)"; 60 | cmd.ExecuteNonQuery(); 61 | 62 | cmd.CommandText = $"CREATE TABLE IF NOT EXISTS Pistol (UserId UNSIGNED BIG INT, WeaponString VARCHAR(255), Team INT)"; 63 | cmd.ExecuteNonQuery(); 64 | 65 | cmd.Dispose(); 66 | } 67 | catch (Exception ex) 68 | { 69 | MessageUtils.Log(Microsoft.Extensions.Logging.LogLevel.Error, ex.ToString()); 70 | } 71 | finally 72 | { 73 | if (cmd != null) 74 | { 75 | cmd.Dispose(); 76 | } 77 | } 78 | } 79 | 80 | public void Dispose() 81 | { 82 | if (this._connection.State != System.Data.ConnectionState.Closed) 83 | { 84 | this._connection.Close(); 85 | } 86 | } 87 | 88 | public bool InsertOrUpdateFullBuyPrimaryWeaponString(ulong userId, string weaponString, int team) 89 | { 90 | SQLiteCommand? cmd = null; 91 | 92 | try 93 | { 94 | 95 | if (this._connection.State != System.Data.ConnectionState.Open) 96 | { 97 | this.OpenConnection(); 98 | } 99 | 100 | cmd = this._connection.CreateCommand(); 101 | 102 | cmd.Parameters.AddWithValue("@id", userId); 103 | cmd.Parameters.AddWithValue("@weapon", weaponString); 104 | cmd.Parameters.AddWithValue("@team", team); 105 | 106 | cmd.CommandText = $"SELECT COUNT(*) FROM FullBuyPrimary WHERE UserId = @id AND Team = @team"; 107 | 108 | cmd.Prepare(); 109 | 110 | var reader = cmd.ExecuteReader(); 111 | 112 | var stmt = string.Empty; 113 | while (reader.Read()) 114 | { 115 | if (reader.GetInt32(0) > 0) 116 | { 117 | stmt = $"UPDATE FullBuyPrimary SET WeaponString = @weapon WHERE UserId = @id AND Team = @team"; 118 | } 119 | else 120 | { 121 | 122 | stmt = $"INSERT INTO FullBuyPrimary (UserId, WeaponString, Team) VALUES (@id, @weapon, @team)"; 123 | } 124 | } 125 | 126 | reader.Close(); 127 | 128 | cmd.CommandText = stmt; 129 | 130 | cmd.Prepare(); 131 | 132 | return cmd.ExecuteNonQuery() == 1; 133 | 134 | } 135 | catch (Exception ex) 136 | { 137 | MessageUtils.Log(Microsoft.Extensions.Logging.LogLevel.Error, ex.ToString()); 138 | 139 | } 140 | finally 141 | { 142 | if (cmd != null) 143 | { 144 | cmd.Dispose(); 145 | } 146 | } 147 | 148 | return false; 149 | } 150 | 151 | public bool InsertOrUpdateFullBuySecondaryWeaponString(ulong userId, string weaponString, int team) 152 | { 153 | SQLiteCommand? cmd = null; 154 | 155 | try 156 | { 157 | 158 | if (this._connection.State != System.Data.ConnectionState.Open) 159 | { 160 | this.OpenConnection(); 161 | } 162 | 163 | cmd = this._connection.CreateCommand(); 164 | 165 | cmd.Parameters.AddWithValue("@id", userId); 166 | cmd.Parameters.AddWithValue("@weapon", weaponString); 167 | cmd.Parameters.AddWithValue("@team", team); 168 | 169 | cmd.CommandText = $"SELECT COUNT(*) FROM FullBuySecondary WHERE UserId = @id AND Team = @team"; 170 | 171 | cmd.Prepare(); 172 | 173 | var reader = cmd.ExecuteReader(); 174 | 175 | var stmt = string.Empty; 176 | while (reader.Read()) 177 | { 178 | if (reader.GetInt32(0) > 0) 179 | { 180 | stmt = $"UPDATE FullBuySecondary SET WeaponString = @weapon WHERE UserId = @id AND Team = @team"; 181 | } 182 | else 183 | { 184 | 185 | stmt = $"INSERT INTO FullBuySecondary (UserId, WeaponString, Team) VALUES (@id, @weapon, @team)"; 186 | } 187 | } 188 | 189 | reader.Close(); 190 | 191 | cmd.CommandText = stmt; 192 | 193 | cmd.Prepare(); 194 | 195 | return cmd.ExecuteNonQuery() == 1; 196 | } 197 | catch (Exception ex) 198 | { 199 | MessageUtils.Log(Microsoft.Extensions.Logging.LogLevel.Error, ex.ToString()); 200 | 201 | } 202 | finally 203 | { 204 | if (cmd != null) 205 | { 206 | cmd.Dispose(); 207 | } 208 | } 209 | 210 | return false; 211 | } 212 | 213 | public bool InsertOrUpdateFullBuyAWPChance(ulong userId, int chance, int team) 214 | { 215 | SQLiteCommand? cmd = null; 216 | 217 | try 218 | { 219 | 220 | if (this._connection.State != System.Data.ConnectionState.Open) 221 | { 222 | this.OpenConnection(); 223 | } 224 | 225 | cmd = this._connection.CreateCommand(); 226 | 227 | cmd.Parameters.AddWithValue("@id", userId); 228 | cmd.Parameters.AddWithValue("@chance", chance); 229 | cmd.Parameters.AddWithValue("@team", team); 230 | 231 | cmd.CommandText = $"SELECT COUNT(*) FROM FullBuyAWPChance WHERE UserId = @id AND Team = @team"; 232 | 233 | cmd.Prepare(); 234 | 235 | var reader = cmd.ExecuteReader(); 236 | 237 | var stmt = string.Empty; 238 | while (reader.Read()) 239 | { 240 | if (reader.GetInt32(0) > 0) 241 | { 242 | stmt = $"UPDATE FullBuyAWPChance SET AWPChance = @chance WHERE UserId = @id AND Team = @team"; 243 | } 244 | else 245 | { 246 | 247 | stmt = $"INSERT INTO FullBuyAWPChance (UserId, AWPChance, Team) VALUES (@id, @chance, @team)"; 248 | } 249 | } 250 | 251 | reader.Close(); 252 | 253 | cmd.CommandText = stmt; 254 | 255 | cmd.Prepare(); 256 | 257 | return cmd.ExecuteNonQuery() == 1; 258 | } 259 | catch (Exception ex) 260 | { 261 | MessageUtils.Log(Microsoft.Extensions.Logging.LogLevel.Error, ex.ToString()); 262 | 263 | } 264 | finally 265 | { 266 | if (cmd != null) 267 | { 268 | cmd.Dispose(); 269 | } 270 | } 271 | 272 | return false; 273 | } 274 | 275 | public bool InsertOrUpdateMidPrimaryWeaponString(ulong userId, string weaponString, int team) 276 | { 277 | SQLiteCommand? cmd = null; 278 | 279 | try 280 | { 281 | 282 | if (this._connection.State != System.Data.ConnectionState.Open) 283 | { 284 | this.OpenConnection(); 285 | } 286 | 287 | cmd = this._connection.CreateCommand(); 288 | 289 | cmd.Parameters.AddWithValue("@id", userId); 290 | cmd.Parameters.AddWithValue("@weapon", weaponString); 291 | cmd.Parameters.AddWithValue("@team", team); 292 | 293 | cmd.CommandText = $"SELECT COUNT(*) FROM MidPrimary WHERE UserId = @id AND Team = @team"; 294 | 295 | cmd.Prepare(); 296 | 297 | var reader = cmd.ExecuteReader(); 298 | 299 | var stmt = string.Empty; 300 | while (reader.Read()) 301 | { 302 | if (reader.GetInt32(0) > 0) 303 | { 304 | stmt = $"UPDATE MidPrimary SET WeaponString = @weapon WHERE UserId = @id AND Team = @team"; 305 | } 306 | else 307 | { 308 | 309 | stmt = $"INSERT INTO MidPrimary (UserId, WeaponString, Team) VALUES (@id, @weapon, @team)"; 310 | } 311 | } 312 | 313 | reader.Close(); 314 | 315 | cmd.CommandText = stmt; 316 | 317 | cmd.Prepare(); 318 | 319 | return cmd.ExecuteNonQuery() == 1; 320 | } 321 | catch (Exception ex) 322 | { 323 | MessageUtils.Log(Microsoft.Extensions.Logging.LogLevel.Error, ex.ToString()); 324 | 325 | } 326 | finally 327 | { 328 | if (cmd != null) 329 | { 330 | cmd.Dispose(); 331 | } 332 | } 333 | 334 | return false; 335 | } 336 | 337 | public bool InsertOrUpdateMidSecondaryWeaponString(ulong userId, string weaponString, int team) 338 | { 339 | SQLiteCommand? cmd = null; 340 | 341 | try 342 | { 343 | 344 | if (this._connection.State != System.Data.ConnectionState.Open) 345 | { 346 | this.OpenConnection(); 347 | } 348 | 349 | cmd = this._connection.CreateCommand(); 350 | 351 | cmd.Parameters.AddWithValue("@id", userId); 352 | cmd.Parameters.AddWithValue("@weapon", weaponString); 353 | cmd.Parameters.AddWithValue("@team", team); 354 | 355 | cmd.CommandText = $"SELECT COUNT(*) FROM MidSecondary WHERE UserId = @id AND Team = @team"; 356 | 357 | cmd.Prepare(); 358 | 359 | var reader = cmd.ExecuteReader(); 360 | 361 | var stmt = string.Empty; 362 | while (reader.Read()) 363 | { 364 | if (reader.GetInt32(0) > 0) 365 | { 366 | stmt = $"UPDATE MidSecondary SET WeaponString = @weapon WHERE UserId = @id AND Team = @team"; 367 | } 368 | else 369 | { 370 | 371 | stmt = $"INSERT INTO MidSecondary (UserId, WeaponString, Team) VALUES (@id, @weapon, @team)"; 372 | } 373 | } 374 | 375 | reader.Close(); 376 | 377 | cmd.CommandText = stmt; 378 | 379 | cmd.Prepare(); 380 | 381 | return cmd.ExecuteNonQuery() == 1; 382 | } 383 | catch (Exception ex) 384 | { 385 | MessageUtils.Log(Microsoft.Extensions.Logging.LogLevel.Error, ex.ToString()); 386 | 387 | } 388 | finally 389 | { 390 | if (cmd != null) 391 | { 392 | cmd.Dispose(); 393 | } 394 | } 395 | 396 | return false; 397 | } 398 | 399 | public bool InsertOrUpdatePistolWeaponString(ulong userId, string weaponString, int team) 400 | { 401 | SQLiteCommand? cmd = null; 402 | 403 | try 404 | { 405 | 406 | if (this._connection.State != System.Data.ConnectionState.Open) 407 | { 408 | this.OpenConnection(); 409 | } 410 | 411 | cmd = this._connection.CreateCommand(); 412 | 413 | cmd.Parameters.AddWithValue("@id", userId); 414 | cmd.Parameters.AddWithValue("@weapon", weaponString); 415 | cmd.Parameters.AddWithValue("@team", team); 416 | 417 | cmd.CommandText = $"SELECT COUNT(*) FROM Pistol WHERE UserId = @id AND Team = @team"; 418 | 419 | cmd.Prepare(); 420 | 421 | var reader = cmd.ExecuteReader(); 422 | 423 | var stmt = string.Empty; 424 | while (reader.Read()) 425 | { 426 | if (reader.GetInt32(0) > 0) 427 | { 428 | stmt = $"UPDATE Pistol SET WeaponString = @weapon WHERE UserId = @id AND Team = @team"; 429 | } 430 | else 431 | { 432 | 433 | stmt = $"INSERT INTO Pistol (UserId, WeaponString, Team) VALUES (@id, @weapon, @team)"; 434 | } 435 | } 436 | 437 | reader.Close(); 438 | 439 | cmd.CommandText = stmt; 440 | 441 | cmd.Prepare(); 442 | 443 | return cmd.ExecuteNonQuery() == 1; 444 | } 445 | catch (Exception ex) 446 | { 447 | MessageUtils.Log(Microsoft.Extensions.Logging.LogLevel.Error, ex.ToString()); 448 | 449 | } 450 | finally 451 | { 452 | if (cmd != null) 453 | { 454 | cmd.Dispose(); 455 | } 456 | } 457 | 458 | return false; 459 | } 460 | 461 | 462 | public (string? primaryWeapon, string? secondaryWeapon, int? awpChance) GetFullBuyWeapons(ulong userId, int team) 463 | { 464 | (string? primaryWeapon, string? secondaryWeapon, int? awpChance) returnValue = (null, null, null); 465 | 466 | SQLiteCommand? cmd = null; 467 | 468 | try 469 | { 470 | 471 | if (this._connection.State != System.Data.ConnectionState.Open) 472 | { 473 | this.OpenConnection(); 474 | } 475 | 476 | cmd = this._connection.CreateCommand(); 477 | 478 | cmd.Parameters.AddWithValue("@id", userId); 479 | cmd.Parameters.AddWithValue("@team", team); 480 | 481 | cmd.CommandText = $"SELECT DISTINCT fp.WeaponString, fs.WeaponString, fa.AWPChance FROM FullBuyPrimary AS fp LEFT JOIN FullBuySecondary AS fs ON fp.UserId = fs.UserId LEFT JOIN FullBuyAWPChance AS fa ON fp.UserId = fa.UserId WHERE fp.UserId = @id AND fp.Team = @team"; 482 | 483 | cmd.Prepare(); 484 | 485 | var reader = cmd.ExecuteReader(); 486 | 487 | if (reader.Read()) 488 | { 489 | returnValue.primaryWeapon = reader.IsDBNull(0) ? string.Empty : reader.GetString(0); 490 | returnValue.secondaryWeapon = reader.IsDBNull(1) ? string.Empty : reader.GetString(1); 491 | returnValue.awpChance = reader.IsDBNull(2) ? 0 : reader.GetInt32(2); 492 | } 493 | 494 | return returnValue; 495 | } 496 | catch (Exception ex) 497 | { 498 | MessageUtils.Log(Microsoft.Extensions.Logging.LogLevel.Error, ex.ToString()); 499 | 500 | } 501 | finally 502 | { 503 | if (cmd != null) 504 | { 505 | cmd.Dispose(); 506 | } 507 | } 508 | 509 | return returnValue; 510 | } 511 | 512 | public (string? primaryWeapon, string? secondaryWeapon, int? awpChance) GetMidWeapons(ulong userId, int team) 513 | { 514 | (string? primaryWeapon, string? secondaryWeapon, int? awpChance) returnValue = (null, null, 0); 515 | SQLiteCommand? cmd = null; 516 | 517 | try 518 | { 519 | 520 | if (this._connection.State != System.Data.ConnectionState.Open) 521 | { 522 | this.OpenConnection(); 523 | } 524 | 525 | cmd = this._connection.CreateCommand(); 526 | 527 | cmd.Parameters.AddWithValue("@id", userId); 528 | cmd.Parameters.AddWithValue("@team", team); 529 | 530 | cmd.CommandText = $"SELECT DISTINCT mp.WeaponString, ms.WeaponString FROM MidPrimary AS mp LEFT JOIN MidSecondary AS ms ON mp.UserId = ms.UserId WHERE mp.UserId = @id AND mp.Team = @team"; 531 | 532 | cmd.Prepare(); 533 | 534 | var reader = cmd.ExecuteReader(); 535 | 536 | if (reader.Read()) 537 | { 538 | returnValue.primaryWeapon = reader.IsDBNull(0) ? string.Empty : reader.GetString(0); 539 | returnValue.secondaryWeapon = reader.IsDBNull(1) ? string.Empty : reader.GetString(1); 540 | } 541 | 542 | return returnValue; 543 | } 544 | catch (Exception ex) 545 | { 546 | MessageUtils.Log(Microsoft.Extensions.Logging.LogLevel.Error, ex.ToString()); 547 | 548 | } 549 | finally 550 | { 551 | if (cmd != null) 552 | { 553 | cmd.Dispose(); 554 | } 555 | } 556 | 557 | return returnValue; 558 | } 559 | 560 | public (string? primaryWeapon, string? secondaryWeapon, int? awpChance) GetPistolWeapons(ulong userId, int team) 561 | { 562 | (string? primaryWeapon, string? secondaryWeapon, int? awpChance) returnValue = (string.Empty, null, 0); 563 | SQLiteCommand? cmd = null; 564 | 565 | try 566 | { 567 | 568 | if (this._connection.State != System.Data.ConnectionState.Open) 569 | { 570 | this.OpenConnection(); 571 | } 572 | 573 | cmd = this._connection.CreateCommand(); 574 | 575 | cmd.Parameters.AddWithValue("@id", userId); 576 | cmd.Parameters.AddWithValue("@team", team); 577 | 578 | cmd.CommandText = $"SELECT DISTINCT p.WeaponString FROM Pistol AS p WHERE p.UserId = @id AND p.Team = @team"; 579 | 580 | cmd.Prepare(); 581 | 582 | var reader = cmd.ExecuteReader(); 583 | 584 | if (reader.Read()) 585 | { 586 | returnValue.secondaryWeapon = reader.IsDBNull(0) ? string.Empty : reader.GetString(0); 587 | } 588 | 589 | return returnValue; 590 | } 591 | catch (Exception ex) 592 | { 593 | MessageUtils.Log(Microsoft.Extensions.Logging.LogLevel.Error, ex.ToString()); 594 | 595 | } 596 | finally 597 | { 598 | if (cmd != null) 599 | { 600 | cmd.Dispose(); 601 | } 602 | } 603 | 604 | return returnValue; 605 | } 606 | } 607 | } 608 | -------------------------------------------------------------------------------- /CS2Retake/Managers/TeamManager.cs: -------------------------------------------------------------------------------- 1 | using CounterStrikeSharp.API; 2 | using CounterStrikeSharp.API.Core; 3 | using CounterStrikeSharp.API.Modules.Entities; 4 | using CounterStrikeSharp.API.Modules.Utils; 5 | using CS2Retake.Configs; 6 | using CS2Retake.Managers.Base; 7 | using CS2Retake.Managers.Interfaces; 8 | using CS2Retake.Utils; 9 | using Microsoft.Extensions.Logging; 10 | using System; 11 | using System.Collections.Generic; 12 | using System.Linq; 13 | using System.Text; 14 | using System.Threading.Tasks; 15 | 16 | namespace CS2Retake.Managers 17 | { 18 | public class TeamManager : BaseManager, ITeamManager 19 | { 20 | private static TeamManager? _instance = null; 21 | 22 | private Dictionary _playerStateDict = new Dictionary(); 23 | private Queue _playerQueue = new Queue(); 24 | 25 | public (int ctRatio, int tRatio) LatestRatio { get; private set; } = (0, 0); 26 | 27 | public bool ScrambleSignal { get; set; } = false; 28 | 29 | public static TeamManager Instance 30 | { 31 | get 32 | { 33 | if (_instance == null) 34 | { 35 | _instance = new TeamManager(); 36 | } 37 | return _instance; 38 | } 39 | } 40 | 41 | private TeamManager() { } 42 | 43 | public void AddQueuePlayers() 44 | { 45 | MessageUtils.LogDebug($"Methode: AddQueuePlayers"); 46 | 47 | if (!FeatureConfig.EnableQueue) 48 | { 49 | MessageUtils.LogDebug($"Feature Queue is disabled!"); 50 | 51 | return; 52 | } 53 | 54 | var playingPlayers = this.GetPlayingPlayers(); 55 | var queuedPlayers = this.GetQueuedPlayers(); 56 | 57 | this.DequeuePlayers(); 58 | var ratio = this.GetPlayerRatio(); 59 | var totalRatio = ratio.ctRatio + ratio.tRatio; 60 | var totalPlayers = this.GetPlayingPlayersCount(); 61 | 62 | MessageUtils.LogDebug($"Ratio CT {ratio.ctRatio}, Ratio T {ratio.tRatio}, Total Ratio {totalRatio}, Total Players {totalPlayers}"); 63 | 64 | if (totalPlayers != totalRatio) 65 | { 66 | MessageUtils.Log(LogLevel.Error, $"AddQueuePlayers - Playing players count [{totalPlayers}] doesnt match the total ratio [CT: {ratio.ctRatio}, T: {ratio.tRatio}]!"); 67 | return; 68 | } 69 | 70 | var random = new Random(); 71 | var playingCounterTerroristPlayers = playingPlayers.Where(x => x.TeamNum == (int)CsTeam.CounterTerrorist).OrderBy(x => random.Next()).ToList(); 72 | var playingTerroristPlayers = playingPlayers.Where(x => x.TeamNum == (int)CsTeam.Terrorist).OrderBy(x => random.Next()).ToList(); 73 | 74 | var ctsNeededInT = ratio.tRatio - playingTerroristPlayers.Count; 75 | 76 | queuedPlayers.ForEach(x => x.SwitchTeam(CsTeam.CounterTerrorist)); 77 | 78 | if(ctsNeededInT > 0) 79 | { 80 | playingCounterTerroristPlayers.Take(ctsNeededInT).ToList().ForEach(x => x.SwitchTeam(CsTeam.Terrorist)); 81 | } 82 | else if(ctsNeededInT < 0) 83 | { 84 | playingTerroristPlayers.Take(ctsNeededInT * -1).ToList().ForEach(x => x.SwitchTeam(CsTeam.CounterTerrorist)); 85 | } 86 | 87 | } 88 | 89 | public void ScrambleTeams() 90 | { 91 | MessageUtils.LogDebug($"Methode: ScrambleTeams"); 92 | 93 | if (!FeatureConfig.EnableScramble) 94 | { 95 | MessageUtils.LogDebug($"Feature Scramble is disabled!"); 96 | 97 | if (FeatureConfig.EnableQueue) 98 | { 99 | this.AddQueuePlayers(); 100 | } 101 | return; 102 | } 103 | 104 | this.DequeuePlayers(); 105 | var ratio = this.GetPlayerRatio(); 106 | var totalRatio = ratio.ctRatio + ratio.tRatio; 107 | var totalPlayers = this.GetPlayingPlayersCount(); 108 | 109 | MessageUtils.LogDebug($"Ratio CT {ratio.ctRatio}, Ratio T {ratio.tRatio}, Total Ratio {totalRatio}, Total Players {totalPlayers}"); 110 | 111 | if (totalPlayers != totalRatio) 112 | { 113 | MessageUtils.Log(LogLevel.Error, $"ScrambleTeams - Playing players count [{totalPlayers}] doesnt match the total ratio [CT: {ratio.ctRatio}, T: {ratio.tRatio}]!"); 114 | return; 115 | } 116 | 117 | var random = new Random(); 118 | var playingPlayers = this.GetPlayingPlayers().OrderBy(x => random.Next()).ToList(); 119 | 120 | playingPlayers.Take(ratio.ctRatio).ToList().ForEach(x => x.SwitchTeam(CsTeam.CounterTerrorist)); 121 | playingPlayers.TakeLast(ratio.tRatio).ToList().ForEach(x => x.SwitchTeam(CsTeam.Terrorist)); 122 | } 123 | 124 | public void SwitchTeams() 125 | { 126 | MessageUtils.LogDebug($"Methode: SwitchTeams"); 127 | 128 | if (!FeatureConfig.EnableSwitchOnRoundWin) 129 | { 130 | MessageUtils.LogDebug($"Feature SwitchOnRoundWin is disabled!"); 131 | 132 | if (FeatureConfig.EnableQueue) 133 | { 134 | this.AddQueuePlayers(); 135 | } 136 | return; 137 | } 138 | 139 | var playingPlayers = this.GetPlayingPlayers(); 140 | var queuedPlayers = this.GetQueuedPlayers(); 141 | 142 | this.DequeuePlayers(); 143 | var ratio = this.GetPlayerRatio(); 144 | var totalRatio = ratio.ctRatio + ratio.tRatio; 145 | var totalPlayers = this.GetPlayingPlayersCount(); 146 | 147 | MessageUtils.LogDebug($"Ratio CT {ratio.ctRatio}, Ratio T {ratio.tRatio}, Total Ratio {totalRatio}, Total Players {totalPlayers}"); 148 | 149 | if (totalPlayers != totalRatio) 150 | { 151 | MessageUtils.Log(LogLevel.Error, $"SwitchTeams - Playing players count [{totalPlayers}] doesnt match the total ratio [CT: {ratio.ctRatio}, T: {ratio.tRatio}]!"); 152 | return; 153 | } 154 | 155 | var random = new Random(); 156 | var playingCounterTerroristPlayers = playingPlayers.Where(x => x.TeamNum == (int)CsTeam.CounterTerrorist).OrderBy(x => random.Next()).ToList(); 157 | var playingTerroristPlayers = playingPlayers.Where(x => x.TeamNum == (int)CsTeam.Terrorist).OrderBy(x => random.Next()).ToList(); 158 | 159 | var ctsToSwitchToT = playingCounterTerroristPlayers.Count >= ratio.tRatio ? ratio.tRatio : playingCounterTerroristPlayers.Count; 160 | var tsToSwitchToCT = playingTerroristPlayers.Count - (ratio.tRatio - ctsToSwitchToT); 161 | 162 | MessageUtils.LogDebug($"CT->T {ctsToSwitchToT}, T->CT {tsToSwitchToCT}"); 163 | 164 | playingCounterTerroristPlayers.Take(ctsToSwitchToT).ToList().ForEach(x => x.SwitchTeam(CsTeam.Terrorist)); 165 | playingTerroristPlayers.Take(tsToSwitchToCT).ToList().ForEach(x => x.SwitchTeam(CsTeam.CounterTerrorist)); 166 | queuedPlayers.ForEach(x => x.SwitchTeam(CsTeam.CounterTerrorist)); 167 | } 168 | 169 | public void FixTeams() 170 | { 171 | MessageUtils.LogDebug($"Methode: FixTeams"); 172 | 173 | var playingPlayers = this.GetPlayingPlayers(); 174 | 175 | var playingCounterTerroristPlayers = playingPlayers.Where(x => x.TeamNum == (int)CsTeam.CounterTerrorist).Where(x => x.UserId.HasValue).Select(x => x.UserId).ToList(); 176 | var playingTerroristPlayers = playingPlayers.Where(x => x.TeamNum == (int)CsTeam.Terrorist).Where(x => x.UserId.HasValue).Select(x => x.UserId).ToList(); 177 | 178 | foreach(var ct in PlayerUtils.GetCounterTerroristPlayers()) 179 | { 180 | if(!ct.UserId.HasValue) 181 | { 182 | continue; 183 | } 184 | 185 | if(playingCounterTerroristPlayers.Contains(ct.UserId.Value)) 186 | { 187 | continue; 188 | } 189 | 190 | this.UpdatePlayerStateDict(ct.UserId.Value, PlayerStateEnum.Playing); 191 | } 192 | 193 | foreach(var t in PlayerUtils.GetTerroristPlayers()) 194 | { 195 | if (!t.UserId.HasValue) 196 | { 197 | continue; 198 | } 199 | 200 | if (playingTerroristPlayers.Contains(t.UserId.Value)) 201 | { 202 | continue; 203 | } 204 | 205 | this.UpdatePlayerStateDict(t.UserId.Value, PlayerStateEnum.Playing); 206 | } 207 | 208 | this.ScrambleTeams(); 209 | } 210 | 211 | public void OnTick() 212 | { 213 | if (GameRuleManager.Instance.IsWarmup) 214 | { 215 | return; 216 | } 217 | 218 | var playerIds = this._playerStateDict.Where(x => x.Value == PlayerStateEnum.Connected || x.Value == PlayerStateEnum.Spectating || x.Value == PlayerStateEnum.Queue).ToList(); 219 | 220 | foreach (var playerId in playerIds) 221 | { 222 | var player = Utilities.GetPlayerFromUserid(playerId.Key); 223 | var state = playerId.Value; 224 | 225 | if (player == null || !player.IsValid || player.UserId == null || !player.UserId.HasValue) 226 | { 227 | continue; 228 | } 229 | 230 | if(state == PlayerStateEnum.Connected && player.TeamNum == (int)CsTeam.Spectator) 231 | { 232 | this.UpdatePlayerStateDict(player.UserId.Value, PlayerStateEnum.Spectating); 233 | } 234 | 235 | if((state == PlayerStateEnum.Connected || state == PlayerStateEnum.Spectating) && (player.TeamNum == (int)CsTeam.Terrorist || player.TeamNum == (int)CsTeam.CounterTerrorist)) 236 | { 237 | this.PlayerSwitchTeam(player, CsTeam.None, CsTeam.Spectator); 238 | } 239 | 240 | if(state == PlayerStateEnum.Queue && (player.TeamNum == (int)CsTeam.Terrorist || player.TeamNum == (int)CsTeam.CounterTerrorist)) 241 | { 242 | player.ChangeTeam(CsTeam.Spectator); 243 | } 244 | } 245 | 246 | 247 | } 248 | 249 | 250 | public void PlayerConnected(CCSPlayerController player) 251 | { 252 | if(player == null || !player.IsValid) 253 | { 254 | return; 255 | } 256 | 257 | if(player.UserId == null || !player.UserId.HasValue) 258 | { 259 | return; 260 | } 261 | 262 | if(!this._playerStateDict.ContainsKey(player.UserId.Value)) 263 | { 264 | this._playerStateDict.Add(player.UserId.Value, PlayerStateEnum.Connecting); 265 | } 266 | else 267 | { 268 | this._playerStateDict[player.UserId.Value] = PlayerStateEnum.Connecting; 269 | } 270 | 271 | MessageUtils.Log(LogLevel.Information, $"Player {player.UserId.Value} is connecting..."); 272 | } 273 | 274 | public void PlayerConnectedFull(CCSPlayerController player) 275 | { 276 | if (player == null || !player.IsValid) 277 | { 278 | return; 279 | } 280 | 281 | if (player.UserId == null || !player.UserId.HasValue) 282 | { 283 | return; 284 | } 285 | 286 | if (!this._playerStateDict.ContainsKey(player.UserId.Value)) 287 | { 288 | this._playerStateDict.Add(player.UserId.Value, PlayerStateEnum.Connected); 289 | } 290 | else 291 | { 292 | this._playerStateDict[player.UserId.Value] = PlayerStateEnum.Connected; 293 | } 294 | 295 | MessageUtils.Log(LogLevel.Information, $"Player {player.UserId.Value} is now connected."); 296 | } 297 | 298 | public void PlayerDisconnected(CCSPlayerController player) 299 | { 300 | if (player == null || !player.IsValid) 301 | { 302 | return; 303 | } 304 | 305 | if (player.UserId == null || !player.UserId.HasValue) 306 | { 307 | return; 308 | } 309 | 310 | var userId = player.UserId.Value; 311 | 312 | if (this._playerStateDict.ContainsKey(userId)) 313 | { 314 | this._playerStateDict.Remove(userId); 315 | } 316 | 317 | this.RemoveFromQueue(userId); 318 | 319 | MessageUtils.Log(LogLevel.Information, $"Player {player.UserId.Value} is now disconnected."); 320 | } 321 | 322 | public void PlayerSwitchTeam(CCSPlayerController player, CsTeam previousTeam, CsTeam newTeam) 323 | { 324 | if (player == null || !player.IsValid) 325 | { 326 | return; 327 | } 328 | 329 | if (player.UserId == null || !player.UserId.HasValue) 330 | { 331 | return; 332 | } 333 | 334 | var userId = player.UserId.Value; 335 | var currentState = this.GetCurrentPlayerState(userId); 336 | 337 | MessageUtils.LogDebug($"UserId: {userId}, State: {currentState}, OldTeam: {previousTeam}, NewTeam: {newTeam}"); 338 | 339 | if(currentState == PlayerStateEnum.Spectating && newTeam == CsTeam.Spectator && (previousTeam == CsTeam.None || previousTeam == CsTeam.Spectator)) 340 | { 341 | return; 342 | } 343 | 344 | if (GameRuleManager.Instance.IsWarmup && (newTeam == CsTeam.Terrorist || newTeam == CsTeam.CounterTerrorist)) 345 | { 346 | MessageUtils.LogDebug($"Switch to playing while warmup {userId}"); 347 | this.UpdatePlayerStateDict(userId, PlayerStateEnum.Playing); 348 | this.RemoveFromQueue(userId); 349 | 350 | player.ChangeTeam(newTeam); 351 | 352 | return; 353 | } 354 | 355 | //Allow switch to spectator 356 | if ((currentState == PlayerStateEnum.Connected || currentState == PlayerStateEnum.Playing) && newTeam == CsTeam.Spectator) 357 | { 358 | MessageUtils.LogDebug($"Switch to spectator from playing or connected {userId}"); 359 | this.UpdatePlayerStateDict(userId, PlayerStateEnum.Spectating); 360 | 361 | player.ChangeTeam(CsTeam.Spectator); 362 | } 363 | //Place player into queue 364 | else if((currentState == PlayerStateEnum.Connected || currentState == PlayerStateEnum.Spectating) && (newTeam == CsTeam.Terrorist || newTeam == CsTeam.CounterTerrorist)) 365 | { 366 | MessageUtils.LogDebug($"Switch to queue {userId}"); 367 | 368 | if(!GameRuleManager.Instance.IsWarmup && !PlayerUtils.AreMoreThenOrEqualPlayersConnected(2)) 369 | { 370 | MessageUtils.PrintToPlayerOrServer($"You have been placed into the queue! Please wait for the next round to start.", player); 371 | } 372 | this.UpdatePlayerStateDict(userId, PlayerStateEnum.Queue); 373 | this.UpdateQueue(userId); 374 | 375 | player.ChangeTeam(CsTeam.Spectator); 376 | 377 | if(!GameRuleManager.Instance.IsWarmup && !PlayerUtils.AreMoreThenOrEqualPlayersConnected(1)) 378 | { 379 | GameRuleManager.Instance.TerminateRound(); 380 | } 381 | } 382 | //Place player into queue from queue spectator combo 383 | else if (currentState == PlayerStateEnum.Queue && (newTeam == CsTeam.Terrorist || newTeam == CsTeam.CounterTerrorist) && (previousTeam == CsTeam.None || previousTeam == CsTeam.Spectator)) 384 | { 385 | MessageUtils.LogDebug($"Switch to queue {userId} from queue spectator"); 386 | 387 | if (!GameRuleManager.Instance.IsWarmup || !PlayerUtils.AreMoreThenOrEqualPlayersConnected(2)) 388 | { 389 | MessageUtils.PrintToPlayerOrServer($"You have been placed into the queue! Please wait for the next round to start.", player); 390 | } 391 | this.UpdatePlayerStateDict(userId, PlayerStateEnum.Queue); 392 | this.UpdateQueue(userId); 393 | 394 | player.ChangeTeam(CsTeam.Spectator); 395 | 396 | } 397 | //Remove player from queue when the player wants to switch to spectator 398 | else if(currentState == PlayerStateEnum.Queue && newTeam == CsTeam.Spectator) 399 | { 400 | MessageUtils.LogDebug($"Switch to spectator from queue {userId}"); 401 | this.UpdatePlayerStateDict(userId, PlayerStateEnum.Spectating); 402 | this.RemoveFromQueue(userId); 403 | 404 | player.ChangeTeam(CsTeam.Spectator); 405 | } 406 | } 407 | 408 | private PlayerStateEnum GetCurrentPlayerState(CCSPlayerController player) 409 | { 410 | if (player == null || !player.IsValid) 411 | { 412 | return PlayerStateEnum.None; 413 | } 414 | 415 | if (player.UserId == null || !player.UserId.HasValue) 416 | { 417 | return PlayerStateEnum.None; 418 | } 419 | 420 | return this.GetCurrentPlayerState(player.UserId.Value); 421 | } 422 | 423 | private PlayerStateEnum GetCurrentPlayerState(int userId) 424 | { 425 | if(!this._playerStateDict.TryGetValue(userId, out PlayerStateEnum state)) 426 | { 427 | return PlayerStateEnum.None; 428 | } 429 | 430 | return state; 431 | } 432 | 433 | private void UpdatePlayerStateDict(int userId, PlayerStateEnum state) 434 | { 435 | if (this._playerStateDict.ContainsKey(userId)) 436 | { 437 | this._playerStateDict[userId] = state; 438 | } 439 | else 440 | { 441 | this._playerStateDict.Add(userId, state); 442 | } 443 | } 444 | 445 | private void UpdateQueue(int userId) 446 | { 447 | if(!this._playerQueue.Contains(userId)) 448 | { 449 | this._playerQueue.Enqueue(userId); 450 | } 451 | } 452 | 453 | private void RemoveFromQueue(int userId) 454 | { 455 | //Removing specific queued player from queue 456 | var queueList = this._playerQueue.ToList(); 457 | this._playerQueue.Clear(); 458 | 459 | foreach (var queueUserId in queueList) 460 | { 461 | if (queueUserId != userId) 462 | { 463 | this._playerQueue.Enqueue(queueUserId); 464 | } 465 | } 466 | } 467 | 468 | private List GetQueuedPlayers() => this.GetPlayersByState(PlayerStateEnum.Queue); 469 | private int GetQueuedPlayersCount() => this.GetPlayersCountByState(PlayerStateEnum.Queue); 470 | 471 | private List GetPlayingPlayers() => this.GetPlayersByState(PlayerStateEnum.Playing); 472 | private int GetPlayingPlayersCount() => this.GetPlayersCountByState(PlayerStateEnum.Playing); 473 | 474 | private List GetPlayersByState(PlayerStateEnum playerState) 475 | { 476 | var playerControllerList = new List(); 477 | 478 | var userIdList = this._playerStateDict.Where(x => x.Value == playerState).Select(x => x.Key).ToList(); 479 | 480 | foreach (var userId in userIdList) 481 | { 482 | playerControllerList.Add(Utilities.GetPlayerFromUserid(userId)); 483 | } 484 | 485 | return playerControllerList; 486 | } 487 | 488 | private int GetPlayersCountByState(PlayerStateEnum playerState) 489 | { 490 | return this._playerStateDict.Where(x => x.Value == playerState).Count(); 491 | } 492 | 493 | private (int ctRatio, int tRatio) GetPlayerRatio() 494 | { 495 | (int ctRatio, int tRatio) playerRatio = (0,0); 496 | 497 | var playingPlayers = this.GetQueuedPlayersCount(); 498 | var queuedPlayers = this.GetPlayingPlayersCount(); 499 | 500 | var totalPlayers = playingPlayers + queuedPlayers; 501 | 502 | if(totalPlayers > RuntimeConfig.MaxPlayers) 503 | { 504 | totalPlayers = RuntimeConfig.MaxPlayers; 505 | } 506 | 507 | var tRatio = (int)Math.Round(totalPlayers * RuntimeConfig.TeamBalanceRatio); 508 | 509 | if(totalPlayers == 1 && tRatio == 0) 510 | { 511 | playerRatio.ctRatio = 0; 512 | playerRatio.tRatio = 1; 513 | } 514 | else 515 | { 516 | playerRatio.ctRatio = totalPlayers - tRatio; 517 | playerRatio.tRatio = tRatio; 518 | } 519 | 520 | this.LatestRatio = playerRatio; 521 | 522 | return playerRatio; 523 | } 524 | 525 | private void DequeuePlayers() 526 | { 527 | var playersToDequeueCount = RuntimeConfig.MaxPlayers - this.GetPlayingPlayersCount(); 528 | 529 | for(int i = 0; i < playersToDequeueCount; i++) 530 | { 531 | if(!this._playerQueue.TryDequeue(out int userId)) 532 | { 533 | continue; 534 | } 535 | 536 | this.UpdatePlayerStateDict(userId, PlayerStateEnum.Playing); 537 | } 538 | } 539 | 540 | public override void ResetForNextRound(bool completeReset = true) 541 | { 542 | 543 | } 544 | 545 | public override void ResetForNextMap(bool completeReset = true) 546 | { 547 | 548 | } 549 | } 550 | } 551 | -------------------------------------------------------------------------------- /CS2Retake/Allocators/Implementations/CommandAllocator/Repository/PostgreSqlRepository.cs: -------------------------------------------------------------------------------- 1 | using CounterStrikeSharp.API.Modules.Utils; 2 | using CS2Retake.Allocators.Implementations.CommandAllocator.Interfaces; 3 | using CS2Retake.Utils; 4 | using Npgsql; 5 | using NpgsqlTypes; 6 | using System; 7 | using System.Collections.Generic; 8 | using System.Data.SQLite; 9 | using System.Linq; 10 | using System.Text; 11 | using System.Threading.Tasks; 12 | 13 | namespace CS2Retake.Allocators.Implementations.CommandAllocator.Repository 14 | { 15 | public class PostgreSqlRepository : IDisposable, IRetakeRepository 16 | { 17 | private List _connectionPool = new List(); 18 | 19 | private string _connectionString = string.Empty; 20 | 21 | 22 | public PostgreSqlRepository(string connectionString) 23 | { 24 | this._connectionString = connectionString; 25 | this.Init(); 26 | } 27 | 28 | private void OpenConnection() 29 | { 30 | try 31 | { 32 | this.GetConnectionFromPool(); 33 | } 34 | catch (Exception ex) 35 | { 36 | MessageUtils.Log(Microsoft.Extensions.Logging.LogLevel.Error, $"Error while creating a connection to the cs2retake.db - Message: {ex.Message}"); 37 | } 38 | } 39 | 40 | private NpgsqlConnection GetConnectionFromPool() 41 | { 42 | var openConnections = this._connectionPool.Where(x => x.FullState == System.Data.ConnectionState.Closed); 43 | 44 | var connection = openConnections.FirstOrDefault() ?? null; 45 | 46 | if (!openConnections.Any() || connection == null) 47 | { 48 | connection = new NpgsqlConnection(this._connectionString); 49 | 50 | this._connectionPool.Add(connection); 51 | } 52 | 53 | connection.Open(); 54 | return connection; 55 | } 56 | 57 | public void Dispose() 58 | { 59 | foreach(var connection in this._connectionPool) 60 | { 61 | connection.Close(); 62 | } 63 | this._connectionPool.Clear(); 64 | } 65 | 66 | public void Init() 67 | { 68 | NpgsqlCommand? cmd = null; 69 | 70 | var connection = this.GetConnectionFromPool(); 71 | 72 | try 73 | { 74 | cmd = connection.CreateCommand(); 75 | 76 | cmd.CommandText = $"CREATE TABLE IF NOT EXISTS FullBuyPrimary (UserId VARCHAR(64), WeaponString VARCHAR(255), Team INT)"; 77 | cmd.ExecuteNonQuery(); 78 | 79 | cmd.CommandText = $"CREATE TABLE IF NOT EXISTS FullBuySecondary (UserId VARCHAR(64), WeaponString VARCHAR(255), Team INT)"; 80 | cmd.ExecuteNonQuery(); 81 | 82 | cmd.CommandText = $"CREATE TABLE IF NOT EXISTS FullBuyAWPChance (UserId VARCHAR(64), AWPChance INT, Team INT)"; 83 | cmd.ExecuteNonQuery(); 84 | 85 | cmd.CommandText = $"CREATE TABLE IF NOT EXISTS MidPrimary (UserId VARCHAR(64), WeaponString VARCHAR(255), Team INT)"; 86 | cmd.ExecuteNonQuery(); 87 | 88 | cmd.CommandText = $"CREATE TABLE IF NOT EXISTS MidSecondary (UserId VARCHAR(64), WeaponString VARCHAR(255), Team INT)"; 89 | cmd.ExecuteNonQuery(); 90 | 91 | cmd.CommandText = $"CREATE TABLE IF NOT EXISTS Pistol (UserId VARCHAR(64), WeaponString VARCHAR(255), Team INT)"; 92 | cmd.ExecuteNonQuery(); 93 | 94 | cmd.Dispose(); 95 | } 96 | catch (Exception ex) 97 | { 98 | MessageUtils.Log(Microsoft.Extensions.Logging.LogLevel.Error, ex.ToString()); 99 | } 100 | finally 101 | { 102 | if (cmd != null) 103 | { 104 | cmd.Dispose(); 105 | } 106 | if(connection.State != System.Data.ConnectionState.Closed) 107 | { 108 | connection.Close(); 109 | } 110 | } 111 | } 112 | 113 | public bool InsertOrUpdateFullBuyPrimaryWeaponString(ulong userId, string weaponString, int team) 114 | { 115 | NpgsqlCommand? cmd = null; 116 | 117 | var connection = this.GetConnectionFromPool(); 118 | 119 | try 120 | { 121 | cmd = connection.CreateCommand(); 122 | 123 | cmd.Parameters.AddWithValue("@id", userId.ToString()); 124 | cmd.Parameters.AddWithValue("@weapon", weaponString); 125 | cmd.Parameters.AddWithValue("@team", team); 126 | 127 | cmd.CommandText = $"SELECT COUNT(*) FROM FullBuyPrimary WHERE UserId = @id AND Team = @team"; 128 | 129 | cmd.Prepare(); 130 | 131 | var reader = cmd.ExecuteReader(); 132 | 133 | var stmt = string.Empty; 134 | while (reader.Read()) 135 | { 136 | if (reader.GetInt32(0) > 0) 137 | { 138 | stmt = $"UPDATE FullBuyPrimary SET WeaponString = @weapon WHERE UserId = @id AND Team = @team"; 139 | } 140 | else 141 | { 142 | 143 | stmt = $"INSERT INTO FullBuyPrimary (UserId, WeaponString, Team) VALUES (@id, @weapon, @team)"; 144 | } 145 | } 146 | 147 | reader.Close(); 148 | 149 | cmd.CommandText = stmt; 150 | 151 | cmd.Prepare(); 152 | 153 | return cmd.ExecuteNonQuery() == 1; 154 | } 155 | catch (Exception ex) 156 | { 157 | MessageUtils.Log(Microsoft.Extensions.Logging.LogLevel.Error, ex.ToString()); 158 | 159 | } 160 | finally 161 | { 162 | if (cmd != null) 163 | { 164 | cmd.Dispose(); 165 | } 166 | if (connection.State != System.Data.ConnectionState.Closed) 167 | { 168 | connection.Close(); 169 | } 170 | } 171 | 172 | return false; 173 | } 174 | 175 | public bool InsertOrUpdateFullBuySecondaryWeaponString(ulong userId, string weaponString, int team) 176 | { 177 | NpgsqlCommand? cmd = null; 178 | 179 | var connection = this.GetConnectionFromPool(); 180 | 181 | try 182 | { 183 | cmd = connection.CreateCommand(); 184 | 185 | cmd.Parameters.AddWithValue("@id", userId.ToString()); 186 | cmd.Parameters.AddWithValue("@weapon", weaponString); 187 | cmd.Parameters.AddWithValue("@team", team); 188 | 189 | cmd.CommandText = $"SELECT COUNT(*) FROM FullBuySecondary WHERE UserId = @id AND Team = @team"; 190 | 191 | cmd.Prepare(); 192 | 193 | var reader = cmd.ExecuteReader(); 194 | 195 | var stmt = string.Empty; 196 | while (reader.Read()) 197 | { 198 | if (reader.GetInt32(0) > 0) 199 | { 200 | stmt = $"UPDATE FullBuySecondary SET WeaponString = @weapon WHERE UserId = @id AND Team = @team"; 201 | } 202 | else 203 | { 204 | 205 | stmt = $"INSERT INTO FullBuySecondary (UserId, WeaponString, Team) VALUES (@id, @weapon, @team)"; 206 | } 207 | } 208 | 209 | reader.Close(); 210 | 211 | cmd.CommandText = stmt; 212 | 213 | cmd.Prepare(); 214 | 215 | return cmd.ExecuteNonQuery() == 1; 216 | } 217 | catch (Exception ex) 218 | { 219 | MessageUtils.Log(Microsoft.Extensions.Logging.LogLevel.Error, ex.ToString()); 220 | 221 | } 222 | finally 223 | { 224 | if (cmd != null) 225 | { 226 | cmd.Dispose(); 227 | } 228 | if (connection.State != System.Data.ConnectionState.Closed) 229 | { 230 | connection.Close(); 231 | } 232 | } 233 | 234 | return false; 235 | } 236 | 237 | public bool InsertOrUpdateFullBuyAWPChance(ulong userId, int chance, int team) 238 | { 239 | NpgsqlCommand? cmd = null; 240 | 241 | var connection = this.GetConnectionFromPool(); 242 | 243 | try 244 | { 245 | cmd = connection.CreateCommand(); 246 | 247 | cmd.Parameters.AddWithValue("@id", userId.ToString()); 248 | cmd.Parameters.AddWithValue("@chance", chance); 249 | cmd.Parameters.AddWithValue("@team", team); 250 | 251 | cmd.CommandText = $"SELECT COUNT(*) FROM FullBuyAWPChance WHERE UserId = @id AND Team = @team"; 252 | 253 | cmd.Prepare(); 254 | 255 | var reader = cmd.ExecuteReader(); 256 | 257 | var stmt = string.Empty; 258 | while (reader.Read()) 259 | { 260 | if (reader.GetInt32(0) > 0) 261 | { 262 | stmt = $"UPDATE FullBuyAWPChance SET AWPChance = @chance WHERE UserId = @id AND Team = @team"; 263 | } 264 | else 265 | { 266 | 267 | stmt = $"INSERT INTO FullBuyAWPChance (UserId, AWPChance, Team) VALUES (@id, @chance, @team)"; 268 | } 269 | } 270 | 271 | reader.Close(); 272 | 273 | cmd.CommandText = stmt; 274 | 275 | cmd.Prepare(); 276 | 277 | return cmd.ExecuteNonQuery() == 1; 278 | } 279 | catch (Exception ex) 280 | { 281 | MessageUtils.Log(Microsoft.Extensions.Logging.LogLevel.Error, ex.ToString()); 282 | 283 | } 284 | finally 285 | { 286 | if (cmd != null) 287 | { 288 | cmd.Dispose(); 289 | } 290 | if (connection.State != System.Data.ConnectionState.Closed) 291 | { 292 | connection.Close(); 293 | } 294 | } 295 | 296 | return false; 297 | } 298 | 299 | public bool InsertOrUpdateMidPrimaryWeaponString(ulong userId, string weaponString, int team) 300 | { 301 | NpgsqlCommand? cmd = null; 302 | 303 | var connection = this.GetConnectionFromPool(); 304 | 305 | try 306 | { 307 | cmd = connection.CreateCommand(); 308 | 309 | cmd.Parameters.AddWithValue("@id", userId.ToString()); 310 | cmd.Parameters.AddWithValue("@weapon", weaponString); 311 | cmd.Parameters.AddWithValue("@team", team); 312 | 313 | cmd.CommandText = $"SELECT COUNT(*) FROM MidPrimary WHERE UserId = @id AND Team = @team"; 314 | 315 | cmd.Prepare(); 316 | 317 | var reader = cmd.ExecuteReader(); 318 | 319 | var stmt = string.Empty; 320 | while (reader.Read()) 321 | { 322 | if (reader.GetInt32(0) > 0) 323 | { 324 | stmt = $"UPDATE MidPrimary SET WeaponString = @weapon WHERE UserId = @id AND Team = @team"; 325 | } 326 | else 327 | { 328 | 329 | stmt = $"INSERT INTO MidPrimary (UserId, WeaponString, Team) VALUES (@id, @weapon, @team)"; 330 | } 331 | } 332 | 333 | reader.Close(); 334 | 335 | cmd.CommandText = stmt; 336 | 337 | cmd.Prepare(); 338 | 339 | return cmd.ExecuteNonQuery() == 1; 340 | } 341 | catch (Exception ex) 342 | { 343 | MessageUtils.Log(Microsoft.Extensions.Logging.LogLevel.Error, ex.ToString()); 344 | 345 | } 346 | finally 347 | { 348 | if (cmd != null) 349 | { 350 | cmd.Dispose(); 351 | } 352 | if (connection.State != System.Data.ConnectionState.Closed) 353 | { 354 | connection.Close(); 355 | } 356 | } 357 | 358 | return false; 359 | } 360 | 361 | public bool InsertOrUpdateMidSecondaryWeaponString(ulong userId, string weaponString, int team) 362 | { 363 | NpgsqlCommand? cmd = null; 364 | 365 | var connection = this.GetConnectionFromPool(); 366 | 367 | try 368 | { 369 | cmd = connection.CreateCommand(); 370 | 371 | cmd.Parameters.AddWithValue("@id", userId.ToString()); 372 | cmd.Parameters.AddWithValue("@weapon", weaponString); 373 | cmd.Parameters.AddWithValue("@team", team); 374 | 375 | cmd.CommandText = $"SELECT COUNT(*) FROM MidSecondary WHERE UserId = @id AND Team = @team"; 376 | 377 | cmd.Prepare(); 378 | 379 | var reader = cmd.ExecuteReader(); 380 | 381 | var stmt = string.Empty; 382 | while (reader.Read()) 383 | { 384 | if (reader.GetInt32(0) > 0) 385 | { 386 | stmt = $"UPDATE MidSecondary SET WeaponString = @weapon WHERE UserId = @id AND Team = @team"; 387 | } 388 | else 389 | { 390 | 391 | stmt = $"INSERT INTO MidSecondary (UserId, WeaponString, Team) VALUES (@id, @weapon, @team)"; 392 | } 393 | } 394 | 395 | reader.Close(); 396 | 397 | cmd.CommandText = stmt; 398 | 399 | cmd.Prepare(); 400 | 401 | return cmd.ExecuteNonQuery() == 1; ; 402 | } 403 | catch (Exception ex) 404 | { 405 | MessageUtils.Log(Microsoft.Extensions.Logging.LogLevel.Error, ex.ToString()); 406 | 407 | } 408 | finally 409 | { 410 | if (cmd != null) 411 | { 412 | cmd.Dispose(); 413 | } 414 | if (connection.State != System.Data.ConnectionState.Closed) 415 | { 416 | connection.Close(); 417 | } 418 | } 419 | 420 | return false; 421 | } 422 | 423 | public bool InsertOrUpdatePistolWeaponString(ulong userId, string weaponString, int team) 424 | { 425 | NpgsqlCommand? cmd = null; 426 | 427 | var connection = this.GetConnectionFromPool(); 428 | 429 | try 430 | { 431 | cmd = connection.CreateCommand(); 432 | 433 | cmd.Parameters.AddWithValue("@id", userId.ToString()); 434 | cmd.Parameters.AddWithValue("@weapon", weaponString); 435 | cmd.Parameters.AddWithValue("@team", team); 436 | 437 | cmd.CommandText = $"SELECT COUNT(*) FROM Pistol WHERE UserId = @id AND Team = @team"; 438 | 439 | cmd.Prepare(); 440 | 441 | var reader = cmd.ExecuteReader(); 442 | 443 | var stmt = string.Empty; 444 | while (reader.Read()) 445 | { 446 | if (reader.GetInt32(0) > 0) 447 | { 448 | stmt = $"UPDATE Pistol SET WeaponString = @weapon WHERE UserId = @id AND Team = @team"; 449 | } 450 | else 451 | { 452 | 453 | stmt = $"INSERT INTO Pistol (UserId, WeaponString, Team) VALUES (@id, @weapon, @team)"; 454 | } 455 | } 456 | 457 | reader.Close(); 458 | 459 | cmd.CommandText = stmt; 460 | 461 | cmd.Prepare(); 462 | 463 | return cmd.ExecuteNonQuery() == 1; 464 | } 465 | catch (Exception ex) 466 | { 467 | MessageUtils.Log(Microsoft.Extensions.Logging.LogLevel.Error, ex.ToString()); 468 | 469 | } 470 | finally 471 | { 472 | if (cmd != null) 473 | { 474 | cmd.Dispose(); 475 | } 476 | if (connection.State != System.Data.ConnectionState.Closed) 477 | { 478 | connection.Close(); 479 | } 480 | } 481 | 482 | return false; 483 | } 484 | 485 | 486 | public (string? primaryWeapon, string? secondaryWeapon, int? awpChance) GetFullBuyWeapons(ulong userId, int team) 487 | { 488 | (string? primaryWeapon, string? secondaryWeapon, int? awpChance) returnValue = (null, null, null); 489 | 490 | NpgsqlCommand? cmd = null; 491 | 492 | var connection = this.GetConnectionFromPool(); 493 | 494 | try 495 | { 496 | cmd = connection.CreateCommand(); 497 | 498 | cmd.Parameters.AddWithValue("@id", userId.ToString()); 499 | cmd.Parameters.AddWithValue("@team", team); 500 | 501 | cmd.CommandText = $"SELECT DISTINCT fp.WeaponString, fs.WeaponString, fa.AWPChance FROM FullBuyPrimary AS fp LEFT JOIN FullBuySecondary AS fs ON fp.UserId = fs.UserId LEFT JOIN FullBuyAWPChance AS fa ON fp.UserId = fa.UserId WHERE fp.UserId = @id AND fp.Team = @team"; 502 | 503 | cmd.Prepare(); 504 | 505 | var reader = cmd.ExecuteReader(); 506 | 507 | if (reader.Read()) 508 | { 509 | returnValue.primaryWeapon = reader.IsDBNull(0) ? string.Empty : reader.GetString(0); 510 | returnValue.secondaryWeapon = reader.IsDBNull(1) ? string.Empty : reader.GetString(1); 511 | returnValue.awpChance = reader.IsDBNull(2) ? 0 : reader.GetInt32(2); 512 | } 513 | 514 | return returnValue; 515 | } 516 | catch (Exception ex) 517 | { 518 | MessageUtils.Log(Microsoft.Extensions.Logging.LogLevel.Error, ex.ToString()); 519 | 520 | } 521 | finally 522 | { 523 | if (cmd != null) 524 | { 525 | cmd.Dispose(); 526 | } 527 | if (connection.State != System.Data.ConnectionState.Closed) 528 | { 529 | connection.Close(); 530 | } 531 | } 532 | 533 | return returnValue; 534 | } 535 | 536 | public (string? primaryWeapon, string? secondaryWeapon, int? awpChance) GetMidWeapons(ulong userId, int team) 537 | { 538 | (string? primaryWeapon, string? secondaryWeapon, int? awpChance) returnValue = (null, null, 0); 539 | NpgsqlCommand? cmd = null; 540 | 541 | var connection = this.GetConnectionFromPool(); 542 | 543 | try 544 | { 545 | cmd = connection.CreateCommand(); 546 | 547 | cmd.Parameters.AddWithValue("@id", userId.ToString()); 548 | cmd.Parameters.AddWithValue("@team", team); 549 | 550 | cmd.CommandText = $"SELECT DISTINCT mp.WeaponString, ms.WeaponString FROM MidPrimary AS mp LEFT JOIN MidSecondary AS ms ON mp.UserId = ms.UserId WHERE mp.UserId = @id AND mp.Team = @team"; 551 | 552 | cmd.Prepare(); 553 | 554 | var reader = cmd.ExecuteReader(); 555 | 556 | if (reader.Read()) 557 | { 558 | returnValue.primaryWeapon = reader.IsDBNull(0) ? string.Empty : reader.GetString(0); 559 | returnValue.secondaryWeapon = reader.IsDBNull(1) ? string.Empty : reader.GetString(1); 560 | } 561 | 562 | return returnValue; 563 | } 564 | catch (Exception ex) 565 | { 566 | MessageUtils.Log(Microsoft.Extensions.Logging.LogLevel.Error, ex.ToString()); 567 | 568 | } 569 | finally 570 | { 571 | if (cmd != null) 572 | { 573 | cmd.Dispose(); 574 | } 575 | if (connection.State != System.Data.ConnectionState.Closed) 576 | { 577 | connection.Close(); 578 | } 579 | } 580 | 581 | return returnValue; 582 | } 583 | 584 | public (string? primaryWeapon, string? secondaryWeapon, int? awpChance) GetPistolWeapons(ulong userId, int team) 585 | { 586 | (string? primaryWeapon, string? secondaryWeapon, int? awpChance) returnValue = (string.Empty, null, 0); 587 | NpgsqlCommand? cmd = null; 588 | 589 | var connection = this.GetConnectionFromPool(); 590 | 591 | try 592 | { 593 | cmd = connection.CreateCommand(); 594 | 595 | cmd.Parameters.AddWithValue("@id", userId.ToString()); 596 | cmd.Parameters.AddWithValue("@team", team); 597 | 598 | cmd.CommandText = $"SELECT DISTINCT p.WeaponString FROM Pistol AS p WHERE p.UserId = @id AND p.Team = @team"; 599 | 600 | cmd.Prepare(); 601 | 602 | var reader = cmd.ExecuteReader(); 603 | 604 | if (reader.Read()) 605 | { 606 | returnValue.secondaryWeapon = reader.IsDBNull(0) ? string.Empty : reader.GetString(0); 607 | } 608 | 609 | cmd.Dispose(); 610 | 611 | return returnValue; 612 | } 613 | catch (Exception ex) 614 | { 615 | MessageUtils.Log(Microsoft.Extensions.Logging.LogLevel.Error, ex.ToString()); 616 | 617 | } 618 | finally 619 | { 620 | if (cmd != null) 621 | { 622 | cmd.Dispose(); 623 | } 624 | if (connection.State != System.Data.ConnectionState.Closed) 625 | { 626 | connection.Close(); 627 | } 628 | } 629 | 630 | return returnValue; 631 | } 632 | } 633 | } 634 | --------------------------------------------------------------------------------