├── 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 |
--------------------------------------------------------------------------------