From 92cbfe95a280e8ee85696365aa9df274030b8208 Mon Sep 17 00:00:00 2001 From: K4ryuu <104531589+K4ryuu@users.noreply.github.com> Date: Thu, 11 Apr 2024 19:35:40 +0200 Subject: [PATCH] Release v4.1.0 --- CHANGELOG | 14 + K4-SharedApi/src/K4-SharedApi.cs | 16 +- K4-SharedApi/src/K4-SharedApi.csproj | 9 +- K4-SharedApi/src/K4-SharedApi.dll | Bin 4096 -> 4096 bytes K4-System/src/K4-System.csproj | 7 +- K4-System/src/Models/PlayerModel.cs | 61 ++ .../src/Module/Interfaces/IModuleTime.cs | 3 +- K4-System/src/Module/ModuleRank.cs | 30 +- K4-System/src/Module/ModuleStat.cs | 13 +- K4-System/src/Module/ModuleTime.cs | 13 +- K4-System/src/Module/ModuleUtils.cs | 11 +- K4-System/src/Module/Rank/RankCommands.cs | 324 ++++------ K4-System/src/Module/Rank/RankConfig.cs | 4 +- K4-System/src/Module/Rank/RankEvents.cs | 208 +++---- K4-System/src/Module/Rank/RankFunctions.cs | 210 ++++--- K4-System/src/Module/Rank/RankGlobals.cs | 6 +- K4-System/src/Module/Rank/RankMenus.cs | 128 ++-- K4-System/src/Module/Stat/StatCommands.cs | 11 +- K4-System/src/Module/Stat/StatEvents.cs | 219 +++++-- K4-System/src/Module/Stat/StatFunctions.cs | 29 +- K4-System/src/Module/Stat/StatGlobals.cs | 5 +- K4-System/src/Module/Time/TimeCommands.cs | 11 +- K4-System/src/Module/Time/TimeEvents.cs | 41 +- K4-System/src/Module/Time/TimeFunctions.cs | 52 +- K4-System/src/Module/Time/TimeGlobals.cs | 4 +- K4-System/src/Module/Utils/UtilsCommands.cs | 56 +- K4-System/src/Module/Utils/UtilsGlobals.cs | 4 +- K4-System/src/Plugin/Plugin.cs | 108 ++-- K4-System/src/Plugin/PluginAPI.cs | 77 ++- K4-System/src/Plugin/PluginBasics.cs | 206 ++++++- K4-System/src/Plugin/PluginCache.cs | 108 ---- K4-System/src/Plugin/PluginConfig.cs | 2 +- K4-System/src/Plugin/PluginDatabase.cs | 541 ++++++++++++++--- K4-System/src/Plugin/PluginManifest.cs | 2 +- K4-System/src/Plugin/PluginStock.cs | 552 +----------------- K4-System/src/lang/en.json | 4 +- .../src/lang/{fr.json => fr.json.outdated} | 0 .../src/lang/{ro.json => ro.json.outdated} | 0 README.md | 12 +- 39 files changed, 1522 insertions(+), 1579 deletions(-) create mode 100644 K4-System/src/Models/PlayerModel.cs delete mode 100644 K4-System/src/Plugin/PluginCache.cs rename K4-System/src/lang/{fr.json => fr.json.outdated} (100%) rename K4-System/src/lang/{ro.json => ro.json.outdated} (100%) diff --git a/CHANGELOG b/CHANGELOG index 8887d3c..60a2662 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,3 +1,17 @@ +-- 2023.04.11 - V4.1.0 + +- feat: Add Reset option for all module +- feat: Add 25 new statistics +- feat: Add new API endpoints +- fix: Rank name not updating in MySQL +- fix: Admin shows a user multiple times +- fix: Toplist and Rank command lag spikes +- fix: Rank resets sets 0 instead of starting points +- fix: AutoPurge delete freshly moved database +- fix: Add loading message to commands where needed +- optimise: The full plugins code +- upgrade: Changed support from NET7 to NET8 + -- 2023.03.28 - V4.0.4 - fix: Table generation problems because of lastseen default value diff --git a/K4-SharedApi/src/K4-SharedApi.cs b/K4-SharedApi/src/K4-SharedApi.cs index e01dac9..2f73936 100644 --- a/K4-SharedApi/src/K4-SharedApi.cs +++ b/K4-SharedApi/src/K4-SharedApi.cs @@ -1,8 +1,16 @@ -namespace K4SharedApi +using CounterStrikeSharp.API.Core; + +namespace K4SharedApi { - public interface IK4SharedApi + public interface IPlayerAPI { - int PlayerPoints { get; } - int PlayerRankID { get; } + bool IsLoaded { get; } + bool IsValid { get; } + bool IsPlayer { get; } + CCSPlayerController Controller { get; } + int Points { get; set; } + int RankID { get; } + string RankName { get; } + string RankClanTag { get; } } } diff --git a/K4-SharedApi/src/K4-SharedApi.csproj b/K4-SharedApi/src/K4-SharedApi.csproj index c9fe644..a786503 100644 --- a/K4-SharedApi/src/K4-SharedApi.csproj +++ b/K4-SharedApi/src/K4-SharedApi.csproj @@ -6,9 +6,16 @@ ./ - net7.0 + net8.0 K4_SharedApi enable enable + + + none + runtime + compile; build; native; contentfiles; analyzers; buildtransitive + + \ No newline at end of file diff --git a/K4-SharedApi/src/K4-SharedApi.dll b/K4-SharedApi/src/K4-SharedApi.dll index ee904ecdfb022a84f13215595286920fd82036ec..60de566535a351939f9e781efa17aa9033909c29 100644 GIT binary patch delta 1465 zcmZvcOKcle7=_Or+Y`r0oVX=XUmyn;IaQ*;k2H4315y`KQwSzb3*lA9@i?_IvB7q# zc7p?|M1)u*noW?Z0ul>gQMF1WkU$8r=mG_Sgo=dNuwhXNLAPZAoO{QCAaSkv?m7Q| zpX1+N4X=jR*Sya@aNymIw~uJoq8lHd4ZaWso&wt=z@}%Te}Cu8K&SLf9C7-!A!lAh z#kTW0x#@f)o;kSAjBKa)Xz(Ey2JYy9a6;`V=e%~bxvBw?*MMKy??|0*)XJ5`x}5c? z5Ad>&oXO*J>K>`5&ty-s-=RM$=ug$Z~5|;I59-(I7kL?jO;;Naa3_iF{}8r;=H0s3Y5uSMDaWh z2?HH?6bANVhCGaAGJ*@_AhyY75;xh6;d?TJpUKlIktZ`)z&%1!w;01Pk9tb6ph=zA z(&Sg#1Q~?n9Hz)0#T4%*;2~wrX;f{bmtV8=9n9&Z#%NcQogdv~JMZjWbqj>6wAx>M zgR(+)+abKDq;}=htHz(K7FTR*5{p*j+^ktyVy$MXE0u;-lXbmh<NXcb%LtZA2BEti=wmo4Nk)f?8bG0Wx3wEw@Zk*U_4dEedO z%!+NB1>4G0x%)+DT0hZy!L(Pbe~y;1&B~m)=&b1j&J8^>X3Sb8+gfOps}(gyPOMdJ z+o~ax$*a@W_Qc#XyJ6=yJ=>IlT(w+j)KULew%*L`z?)xxa`>Io>)UtwCSu=RgEuUM z-xG%LQG`O$1|@v{>kpr6hHeEOVp8*lv2T<@jtqJR7#NU&0R{#T^o!Paa~~_+`Lb<7 zq@6$8`pK~0m`n;7aU*8L221I5F+H+yycA0m62;W0nJN`xrZt)z9$pwpnu$a_IUG;K zSyVptli|7@&RhQb_g!xNeaTB)l{&o9->0odoge&bPB1VT9QU;D$8Ox?JQuhst~hrC z5pi+r-j2_N=M6mQ{M0#^A4Caheu{iXSk=erV@RODr-&3rVUi{K7?nkzWTg*tVWD2iWdT%Zso3 z$iBBBJ?PeGNDm&^Ou|7U!HY)1q1r?a!~~3T@IW+?izbF}!i$E)nBc+qo7vLv+stQv ze`bEanK!eu=B~My)?@49*w?L(y7_v`ZfNL~lVjaIfRc@{{LeTf3yGRt3* zr&te*$@hul@=La`_dZ3GOj6^BT~n6NRr;E^ojZ6IQ89RC*@bXtGV#ZRc4b1|FT2a zO*Ki~K~2q6y6UOCo~>*uqMOoH1Ab=h-o!!H%}q2l1K4~AhH1EokMSupvBVw`|AFGB z=1+LkBx6bkKG3dVs1%YtApas)T)+dJzg>i>qO22rDDhWFZ(F;lq_Q|ZqXm7>ejqo8 zfjs_T2QclbWnSE4P-cd=wkPzFA_U3sCig( zRI{jgLi3bnnP_5_xC@@vd$1FX70`i~Q9u`7CGKm|{tC$9EU_O~h=;I2Jc@6LWB8F+ z(*9ZE7~aM`qC>1`y|SfO@GBja+Q0?gPfSxb^#u%KjILB{?a6UbP|FP(oOrT-<=IES zEg7B)stsR^U|B?Grv37&sLzyZ@6gqmT@53#QkZGfB5y_Djg>`Ft%~YsME7p75ebY1 zE9X65)F(tu)XUM+q3uEQVR>1!E#8Rack#!vWIFP9^PtQodRS3TCUVc=q^O5pP}?pk zmV8l($Rj^b6q{3Cr5=RAQdAfVs$#TO_E$qM#CDCja-9|*KDC4Cz^jo%$>q!Q*Y^MY z$>cYKw?;}Odj>I=G0Skl;-q9U%1mqZu(fgQOfz%OIl@gfz%qJaSq!pjO|T((-O3X0 zT7{zyLwv9>P#B=Uj5>zAx{c}<&hgZ!?*|vA8h+%R_XRoZV(px$d-QQYsXFM5kZ;./bin/K4-System/plugins/K4-System - net7.0 + net8.0 enable enable - + none runtime compile; build; native; contentfiles; analyzers; buildtransitive - + + ../../K4-SharedApi/src/K4-SharedApi.dll diff --git a/K4-System/src/Models/PlayerModel.cs b/K4-System/src/Models/PlayerModel.cs new file mode 100644 index 0000000..a558731 --- /dev/null +++ b/K4-System/src/Models/PlayerModel.cs @@ -0,0 +1,61 @@ + +using CounterStrikeSharp.API; +using CounterStrikeSharp.API.Core; +using CounterStrikeSharp.API.Modules.Commands; +using static K4System.ModuleRank; +using static K4System.ModuleStat; +using static K4System.ModuleTime; + +namespace K4System.Models; + +public class K4Player +{ + //** ? Main */ + private readonly Plugin Plugin; + + //** ? Player */ + public readonly CCSPlayerController Controller; + public readonly ulong SteamID; + public readonly string PlayerName; + + //** ? Data */ + public RankData? rankData { get; set; } + public StatData? statData { get; set; } + public TimeData? timeData { get; set; } + public (int killStreak, DateTime lastKillTime) KillStreak = (0, DateTime.MinValue); + + public K4Player(Plugin plugin, CCSPlayerController playerController) + { + Plugin = plugin; + + Controller = playerController; + SteamID = playerController.SteamID; + PlayerName = playerController.PlayerName; + } + + public bool IsValid + { + get + { + return Controller?.IsValid == true && Controller.PlayerPawn?.IsValid == true && Controller.Connected == PlayerConnectedState.PlayerConnected; + } + } + + public bool IsPlayer + { + get + { + return !Controller.IsBot && !Controller.IsHLTV; + } + } + + public string ClanTag + { + get { return Controller.Clan; } + set + { + Controller.Clan = value; + Utilities.SetStateChanged(Controller, "CCSPlayerController", "m_szClan"); + } + } +} \ No newline at end of file diff --git a/K4-System/src/Module/Interfaces/IModuleTime.cs b/K4-System/src/Module/Interfaces/IModuleTime.cs index 400a35c..ac1dadc 100644 --- a/K4-System/src/Module/Interfaces/IModuleTime.cs +++ b/K4-System/src/Module/Interfaces/IModuleTime.cs @@ -1,4 +1,5 @@ using CounterStrikeSharp.API.Core; +using K4System.Models; namespace K4System; @@ -8,5 +9,5 @@ public interface IModuleTime public void Release(bool hotReload); - public void BeforeDisconnect(CCSPlayerController player); + public void BeforeDisconnect(K4Player k4player); } diff --git a/K4-System/src/Module/ModuleRank.cs b/K4-System/src/Module/ModuleRank.cs index d902658..d7c47e1 100644 --- a/K4-System/src/Module/ModuleRank.cs +++ b/K4-System/src/Module/ModuleRank.cs @@ -6,40 +6,40 @@ namespace K4System using CounterStrikeSharp.API.Core.Plugin; using CounterStrikeSharp.API.Modules.Timers; using CounterStrikeSharp.API.Modules.Utils; + using K4System.Models; public partial class ModuleRank : IModuleRank { public ModuleRank(ILogger logger, IPluginContext pluginContext) { this.Logger = logger; - this.PluginContext = (pluginContext as PluginContext)!; + this.plugin = (pluginContext.Plugin as Plugin)!; + this.Config = plugin.Config; } public void Initialize(bool hotReload) { this.Logger.LogInformation("Initializing '{0}'", this.GetType().Name); - //** ? Forwarded Variables */ - - Plugin plugin = (this.PluginContext.Plugin as Plugin)!; - - this.Config = plugin.Config; - this.ModuleDirectory = plugin._ModuleDirectory; - //** ? Register Module Parts */ - Initialize_Config(plugin); - Initialize_Menus(plugin); - Initialize_Events(plugin); - Initialize_Commands(plugin); + Initialize_Config(); + Initialize_Menus(); + Initialize_Events(); + Initialize_Commands(); //** ? Register Timers */ plugin.AddTimer(Config.PointSettings.PlaytimeMinutes * 60, () => { - Utilities.GetPlayers().Where(p => p.TeamNum == (int)CsTeam.Terrorist) - .ToList() - .ForEach(p => ModifyPlayerPoints(p, Config.PointSettings.PlaytimePoints, "k4.phrases.playtime")); + foreach (K4Player k4player in plugin.K4Players) + { + if (!k4player.IsValid || !k4player.IsPlayer) + continue; + + if (k4player.Controller.Team == CsTeam.Terrorist) + ModifyPlayerPoints(k4player, Config.PointSettings.PlaytimePoints, "k4.phrases.playtime"); + } }, TimerFlags.REPEAT); } diff --git a/K4-System/src/Module/ModuleStat.cs b/K4-System/src/Module/ModuleStat.cs index 8aeda19..27a0bde 100644 --- a/K4-System/src/Module/ModuleStat.cs +++ b/K4-System/src/Module/ModuleStat.cs @@ -9,23 +9,18 @@ public partial class ModuleStat : IModuleStat public ModuleStat(ILogger logger, IPluginContext pluginContext) { this.Logger = logger; - this.PluginContext = (pluginContext as PluginContext)!; + this.plugin = (pluginContext.Plugin as Plugin)!; + this.Config = plugin.Config; } public void Initialize(bool hotReload) { this.Logger.LogInformation("Initializing '{0}'", this.GetType().Name); - //** ? Forwarded Variables */ - - Plugin plugin = (this.PluginContext.Plugin as Plugin)!; - - this.Config = plugin.Config; - //** ? Register Module Parts */ - Initialize_Events(plugin); - Initialize_Commands(plugin); + Initialize_Events(); + Initialize_Commands(); } public void Release(bool hotReload) diff --git a/K4-System/src/Module/ModuleTime.cs b/K4-System/src/Module/ModuleTime.cs index 33e4bda..876c3ec 100644 --- a/K4-System/src/Module/ModuleTime.cs +++ b/K4-System/src/Module/ModuleTime.cs @@ -9,23 +9,18 @@ public partial class ModuleTime : IModuleTime public ModuleTime(ILogger logger, IPluginContext pluginContext) { this.Logger = logger; - this.PluginContext = (pluginContext as PluginContext)!; + this.plugin = (pluginContext.Plugin as Plugin)!; + this.Config = plugin.Config; } public void Initialize(bool hotReload) { this.Logger.LogInformation("Initializing '{0}'", this.GetType().Name); - //** ? Forwarded Variables */ - - Plugin plugin = (this.PluginContext.Plugin as Plugin)!; - - this.Config = plugin.Config; - //** ? Register Module Parts */ - Initialize_Events(plugin); - Initialize_Commands(plugin); + Initialize_Events(); + Initialize_Commands(); } public void Release(bool hotReload) diff --git a/K4-System/src/Module/ModuleUtils.cs b/K4-System/src/Module/ModuleUtils.cs index 01d6dcf..997fe45 100644 --- a/K4-System/src/Module/ModuleUtils.cs +++ b/K4-System/src/Module/ModuleUtils.cs @@ -9,22 +9,17 @@ public partial class ModuleUtils : IModuleUtils public ModuleUtils(ILogger logger, IPluginContext pluginContext) { this.Logger = logger; - this.PluginContext = (pluginContext as PluginContext)!; + this.plugin = (pluginContext.Plugin as Plugin)!; + this.Config = plugin.Config; } public void Initialize(bool hotReload) { this.Logger.LogInformation("Initializing '{0}'", this.GetType().Name); - //** ? Forwarded Variables */ - - Plugin plugin = (this.PluginContext.Plugin as Plugin)!; - - this.Config = plugin.Config; - //** ? Register Module Parts */ - Initialize_Commands(plugin); + Initialize_Commands(); } public void Release(bool hotReload) diff --git a/K4-System/src/Module/Rank/RankCommands.cs b/K4-System/src/Module/Rank/RankCommands.cs index 4838d57..9122727 100644 --- a/K4-System/src/Module/Rank/RankCommands.cs +++ b/K4-System/src/Module/Rank/RankCommands.cs @@ -10,10 +10,12 @@ namespace K4System using CounterStrikeSharp.API.Modules.Menu; using Microsoft.Extensions.Logging; using System.Data; + using K4System.Models; + using Dapper; public partial class ModuleRank : IModuleRank { - public void Initialize_Commands(Plugin plugin) + public void Initialize_Commands() { CommandSettings commands = Config.CommandSettings; @@ -27,17 +29,12 @@ public void Initialize_Commands(Plugin plugin) plugin.AddCommand($"css_{commandString}", "Check the available ranks and their data", plugin.CallbackAnonymizer(OnCommandRanks)); }); - commands.ResetMyCommands.ForEach(commandString => - { - plugin.AddCommand($"css_{commandString}", "Resets the player's own points to zero", plugin.CallbackAnonymizer(OnCommandResetMyRank)); - }); - commands.TopCommands.ForEach(commandString => { plugin.AddCommand($"css_{commandString}", "Check the top players by points", plugin.CallbackAnonymizer(OnCommandTop)); }); - plugin.AddCommand("css_resetrank", "Resets the targeted player's points to zero", plugin.CallbackAnonymizer(OnCommandResetRank)); + plugin.AddCommand("css_setpoints", "SEt the targeted player's points", plugin.CallbackAnonymizer(OnCommandSetPoints)); plugin.AddCommand("css_givepoints", "Give points the targeted player", plugin.CallbackAnonymizer(OnCommandGivePoints)); plugin.AddCommand("css_removepoints", "Remove points from the targeted player", plugin.CallbackAnonymizer(OnCommandRemovePoints)); @@ -47,12 +44,18 @@ public void Initialize_Commands(Plugin plugin) public void OnCommandTogglePointMessages(CCSPlayerController? player, CommandInfo info) { - Plugin plugin = (this.PluginContext.Plugin as Plugin)!; - if (!plugin.CommandHelper(player, info, CommandUsage.CLIENT_ONLY)) return; - RankData? playerData = PlayerCache.Instance.GetPlayerData(player!).rankData; + K4Player? k4player = plugin.GetK4Player(player!); + + if (k4player is null) + { + info.ReplyToCommand($" {plugin.Localizer["k4.general.prefix"]} {plugin.Localizer["k4.general.loading"]}"); + return; + } + + RankData? playerData = k4player.rankData; if (playerData is null) return; @@ -63,37 +66,37 @@ public void OnCommandTogglePointMessages(CCSPlayerController? player, CommandInf public void OnCommandToggleTag(CCSPlayerController? player, CommandInfo info) { - Plugin plugin = (this.PluginContext.Plugin as Plugin)!; - if (!plugin.CommandHelper(player, info, CommandUsage.CLIENT_ONLY)) return; - RankData? playerData = PlayerCache.Instance.GetPlayerData(player!).rankData; + K4Player? k4player = plugin.GetK4Player(player!); + + if (k4player is null) + { + info.ReplyToCommand($" {plugin.Localizer["k4.general.prefix"]} {plugin.Localizer["k4.general.loading"]}"); + return; + } + + RankData? playerData = k4player.rankData; if (playerData is null) return; playerData.HideAdminTag = !playerData.HideAdminTag; - string tag = string.Empty; - if (Config.RankSettings.ScoreboardClantags) - { - tag = playerData.Rank.Tag ?? $"[{playerData.Rank.Name}]"; - } - - SetPlayerClanTag(player!, playerData, tag); + SetPlayerClanTag(k4player); info.ReplyToCommand($" {plugin.Localizer["k4.general.prefix"]} {plugin.Localizer["k4.general.toggletag", playerData.HideAdminTag ? plugin.Localizer["k4.general.state.disabled"] : plugin.Localizer["k4.general.state.enabled"]]}"); } public void OnCommandRank(CCSPlayerController? player, CommandInfo info) { - if (player == null || !player.IsValid || player.PlayerPawn.Value == null) + if (!plugin.CommandHelper(player, info, CommandUsage.CLIENT_ONLY)) return; - Plugin plugin = (this.PluginContext.Plugin as Plugin)!; + K4Player? k4player = plugin.GetK4Player(player!); - if (!PlayerCache.Instance.ContainsPlayer(player)) + if (k4player is null) { info.ReplyToCommand($" {plugin.Localizer["k4.general.prefix"]} {plugin.Localizer["k4.general.loading"]}"); return; @@ -106,99 +109,61 @@ public void OnCommandRank(CCSPlayerController? player, CommandInfo info) printCount = Math.Clamp(parsedInt, 1, 25); } - string steamID = player!.SteamID.ToString(); - - Task<(int playerPlace, int totalPlayers)> task = Task.Run(() => FetchRankDataAsync(steamID)); - task.Wait(); - var result = task.Result; - - int playerPlace = result.playerPlace; - int totalPlayers = result.totalPlayers; + Task.Run(async () => + { + (int playerPlace, int totalPlayers) taskValues = await GetPlayerPlaceAndCountAsync(k4player); - RankData? playerData = PlayerCache.Instance.GetPlayerData(player).rankData; + int playerPlace = taskValues.playerPlace; + int totalPlayers = taskValues.totalPlayers; - if (playerData is null) - return; + RankData? playerData = k4player.rankData; - int higherRanksCount = rankDictionary.Count(kv => kv.Value.Point > playerData.Points); + if (playerData is null) + return; - info.ReplyToCommand($" {plugin.Localizer["k4.general.prefix"]} {plugin.Localizer["k4.ranks.rank.title", player.PlayerName]}"); - info.ReplyToCommand(plugin.Localizer["k4.ranks.rank.line1", playerData.Points, playerData.Rank.Color, playerData.Rank.Name, rankDictionary.Count - higherRanksCount, rankDictionary.Count]); + int higherRanksCount = rankDictionary.Count(kv => kv.Value.Point > playerData.Points); - KeyValuePair nextRankEntry = rankDictionary - .Where(kv => kv.Value.Point > playerData.Rank.Point) - .OrderBy(kv => kv.Value.Point) - .FirstOrDefault(); + Server.NextFrame(() => + { + info.ReplyToCommand($" {plugin.Localizer["k4.general.prefix"]} {plugin.Localizer["k4.ranks.rank.title", k4player.PlayerName]}"); + info.ReplyToCommand(plugin.Localizer["k4.ranks.rank.line1", playerData.Points, playerData.Rank.Color, playerData.Rank.Name, rankDictionary.Count - higherRanksCount, rankDictionary.Count]); - if (nextRankEntry.Value != null) - { - Rank nextRank = nextRankEntry.Value; + KeyValuePair nextRankEntry = rankDictionary + .Where(kv => kv.Value.Point > playerData.Rank.Point) + .OrderBy(kv => kv.Value.Point) + .FirstOrDefault(); - info.ReplyToCommand(plugin.Localizer["k4.ranks.rank.line2", nextRank.Color, nextRank.Name]); - info.ReplyToCommand(plugin.Localizer["k4.ranks.rank.line3", nextRank.Point - playerData.Points]); - } + if (nextRankEntry.Value != null) + { + Rank nextRank = nextRankEntry.Value; - info.ReplyToCommand(plugin.Localizer["k4.ranks.rank.line4", playerPlace, totalPlayers]); - } + info.ReplyToCommand(plugin.Localizer["k4.ranks.rank.line2", nextRank.Color, nextRank.Name]); + info.ReplyToCommand(plugin.Localizer["k4.ranks.rank.line3", nextRank.Point - playerData.Points]); + } - public async Task<(int, int)> FetchRankDataAsync(string steamID) - { - try - { - var (playerPlace, totalPlayers) = await GetPlayerPlaceAndCountAsync(steamID); - return (playerPlace, totalPlayers); - } - catch (Exception ex) - { - Logger.LogError($"Error fetching rank data: {ex.Message}"); - return (0, 0); - } + info.ReplyToCommand(plugin.Localizer["k4.ranks.rank.line4", playerPlace, totalPlayers]); + }); + }); } public void OnCommandRanks(CCSPlayerController? player, CommandInfo info) { - Plugin plugin = (this.PluginContext.Plugin as Plugin)!; - if (!plugin.CommandHelper(player, info, CommandUsage.CLIENT_ONLY)) return; MenuManager.OpenChatMenu(player!, ranksMenu); } - public void OnCommandResetMyRank(CCSPlayerController? player, CommandInfo info) - { - Plugin plugin = (this.PluginContext.Plugin as Plugin)!; - - if (!plugin.CommandHelper(player, info, CommandUsage.CLIENT_ONLY)) - return; - - if (!PlayerCache.Instance.ContainsPlayer(player!)) - { - info.ReplyToCommand($" {plugin.Localizer["k4.general.prefix"]} {plugin.Localizer["k4.general.loading"]}"); - return; - } - - RankData? playerData = PlayerCache.Instance.GetPlayerData(player!).rankData; - - if (playerData is null) - return; - - playerData.RoundPoints -= playerData.Points; - playerData.Points = 0; - plugin.SavePlayerCache(player!, false); - - Server.PrintToChatAll($" {plugin.Localizer["k4.general.prefix"]} {plugin.Localizer["k4.ranks.resetmyrank", player!.PlayerName]}"); - } public void OnCommandTop(CCSPlayerController? player, CommandInfo info) { - if (player == null || !player.IsValid || player.PlayerPawn.Value == null) + if (!plugin.CommandHelper(player, info, CommandUsage.CLIENT_ONLY)) return; - Plugin plugin = (this.PluginContext.Plugin as Plugin)!; + K4Player? k4player = plugin.GetK4Player(player!); - if (!PlayerCache.Instance.ContainsPlayer(player)) + if (k4player is null) { info.ReplyToCommand($" {plugin.Localizer["k4.general.prefix"]} {plugin.Localizer["k4.general.loading"]}"); return; @@ -211,56 +176,47 @@ public void OnCommandTop(CCSPlayerController? player, CommandInfo info) printCount = Math.Clamp(parsedInt, 1, 25); } - CCSPlayerController savedPlayer = player; - List playersData = plugin.PreparePlayersData(); - - Task?> task = Task.Run(() => FetchTopDataAsync(printCount)); - task.Wait(); - List<(int points, string name)>? rankData = task.Result; - - if (rankData?.Count > 0) + Task.Run(async () => { - for (int i = 0; i < rankData.Count; i++) + List<(int points, string name)>? rankData = await FetchTopDataAsync(printCount); + + if (rankData?.Count > 0) { - int points = rankData[i].points; - string name = rankData[i].name; + for (int i = 0; i < rankData.Count; i++) + { + int points = rankData[i].points; + string name = rankData[i].name; - Rank rank = GetPlayerRank(points); + Rank rank = GetPlayerRank(points); + + Server.NextFrame(() => + { + player!.PrintToChat($" {plugin.Localizer["k4.ranks.top.line", i + 1, rank.Color, rank.Name, name, points]}"); + }); + } - player.PrintToChat($" {plugin.Localizer["k4.ranks.top.line", i + 1, rank.Color, rank.Name, name, points]}"); } - } - else - { - player.PrintToChat($" {plugin.Localizer["k4.general.prefix"]} {plugin.Localizer["k4.ranks.top.notfound", printCount]}"); - } + else + { + player!.PrintToChat($" {plugin.Localizer["k4.general.prefix"]} {plugin.Localizer["k4.ranks.top.notfound", printCount]}"); + } + }); } public async Task?> FetchTopDataAsync(int printCount) { - string query = $"SELECT `points`, `name` FROM `{Config.DatabaseSettings.TablePrefix}k4ranks` ORDER BY `points` DESC LIMIT {printCount};"; - - List<(int points, string name)> rankData = new List<(int points, string name)>(); + string query = $@"SELECT `points`, `name` FROM `{Config.DatabaseSettings.TablePrefix}k4ranks` ORDER BY `points` DESC LIMIT @PrintCount;"; try { - - using (MySqlCommand command = new MySqlCommand(query)) + using (var connection = plugin.CreateConnection(Config)) { - DataTable dataTable = await Database.Instance.ExecuteReaderAsync(command.CommandText); + await connection.OpenAsync(); - if (dataTable.Rows.Count > 0) - { - foreach (DataRow row in dataTable.Rows) - { - int points = Convert.ToInt32(row[0]); - string name = Convert.ToString(row[1]) ?? "Unknown"; - rankData.Add((points, name)); - } - } - } + var rankData = (await connection.QueryAsync<(int, string)>(query, new { PrintCount = printCount })).ToList(); - return rankData; + return rankData; + } } catch (Exception ex) { @@ -269,62 +225,8 @@ public void OnCommandTop(CCSPlayerController? player, CommandInfo info) } } - public void OnCommandResetRank(CCSPlayerController? player, CommandInfo info) - { - Plugin plugin = (this.PluginContext.Plugin as Plugin)!; - - if (!plugin.CommandHelper(player, info, CommandUsage.CLIENT_AND_SERVER, 1, "", "@k4system/admin")) - return; - - string playerName = player != null && player.IsValid && player.PlayerPawn.Value != null ? player.PlayerName : "SERVER"; - - TargetResult targetResult = info.GetArgTargetResult(1); - - if (!targetResult.Any()) - { - info.ReplyToCommand($" {plugin.Localizer["k4.general.prefix"]} {plugin.Localizer["k4.general.targetnotfound"]}"); - return; - } - - foreach (CCSPlayerController target in targetResult.Players) - { - if (target.IsBot || target.IsHLTV) - { - info.ReplyToCommand($" {plugin.Localizer["k4.general.prefix"]} {plugin.Localizer["k4.general.targetnobot", target.PlayerName]}"); - continue; - } - - if (!AdminManager.CanPlayerTarget(player, target)) - { - info.ReplyToCommand($" {plugin.Localizer["k4.general.prefix"]} {plugin.Localizer["k4.general.targetimmunity", target.PlayerName]}"); - continue; - } - - if (!PlayerCache.Instance.ContainsPlayer(target)) - { - info.ReplyToCommand($" {plugin.Localizer["k4.general.prefix"]} {plugin.Localizer["k4.general.targetloading", target.PlayerName]}"); - continue; - } - - RankData? playerData = PlayerCache.Instance.GetPlayerData(target).rankData; - - if (playerData is null) - return; - - playerData.RoundPoints -= playerData.Points; - playerData.Points = 0; - - plugin.SavePlayerCache(target, false); - - if (playerName != "SERVER") - Server.PrintToChatAll($" {plugin.Localizer["k4.general.prefix"]} {plugin.Localizer["k4.ranks.resetrank", target.PlayerName, playerName]}"); - } - } - public void OnCommandSetPoints(CCSPlayerController? player, CommandInfo info) { - Plugin plugin = (this.PluginContext.Plugin as Plugin)!; - if (!plugin.CommandHelper(player, info, CommandUsage.CLIENT_AND_SERVER, 2, " ", "@k4system/admin")) return; @@ -346,25 +248,27 @@ public void OnCommandSetPoints(CCSPlayerController? player, CommandInfo info) foreach (CCSPlayerController target in targetResult.Players) { - if (target.IsBot || target.IsHLTV) + K4Player? k4player = plugin.GetK4Player(target); + + if (k4player is null || !k4player.IsValid) { - info.ReplyToCommand($" {plugin.Localizer["k4.general.prefix"]} {plugin.Localizer["k4.general.targetnobot", target.PlayerName]}"); + info.ReplyToCommand($" {plugin.Localizer["k4.general.prefix"]} {plugin.Localizer["k4.general.targetloading", target.PlayerName]}"); continue; } - if (!AdminManager.CanPlayerTarget(player, target)) + if (!k4player.IsPlayer) { - info.ReplyToCommand($" {plugin.Localizer["k4.general.prefix"]} {plugin.Localizer["k4.general.targetimmunity", target.PlayerName]}"); + info.ReplyToCommand($" {plugin.Localizer["k4.general.prefix"]} {plugin.Localizer["k4.general.targetnobot", target.PlayerName]}"); continue; } - if (!PlayerCache.Instance.ContainsPlayer(target)) + if (!AdminManager.CanPlayerTarget(player, target)) { - info.ReplyToCommand($" {plugin.Localizer["k4.general.prefix"]} {plugin.Localizer["k4.general.targetloading", target.PlayerName]}"); + info.ReplyToCommand($" {plugin.Localizer["k4.general.prefix"]} {plugin.Localizer["k4.general.targetimmunity", target.PlayerName]}"); continue; } - RankData? playerData = PlayerCache.Instance.GetPlayerData(target).rankData; + RankData? playerData = k4player.rankData; if (playerData is null) return; @@ -372,17 +276,15 @@ public void OnCommandSetPoints(CCSPlayerController? player, CommandInfo info) playerData.RoundPoints = parsedInt; playerData.Points = 0; - plugin.SavePlayerCache(target, false); - if (playerName != "SERVER") Server.PrintToChatAll($" {plugin.Localizer["k4.general.prefix"]} {plugin.Localizer["k4.ranks.setpoints", target.PlayerName, parsedInt, playerName]}"); + + Task.Run(() => plugin.SavePlayerDataAsync(k4player, false)); } } public void OnCommandGivePoints(CCSPlayerController? player, CommandInfo info) { - Plugin plugin = (this.PluginContext.Plugin as Plugin)!; - if (!plugin.CommandHelper(player, info, CommandUsage.CLIENT_AND_SERVER, 2, " ", "@k4system/admin")) return; @@ -404,25 +306,27 @@ public void OnCommandGivePoints(CCSPlayerController? player, CommandInfo info) foreach (CCSPlayerController target in targetResult.Players) { - if (target.IsBot || target.IsHLTV) + K4Player? k4player = plugin.GetK4Player(target); + + if (k4player is null || !k4player.IsValid) { - info.ReplyToCommand($" {plugin.Localizer["k4.general.prefix"]} {plugin.Localizer["k4.general.targetnobot", target.PlayerName]}"); + info.ReplyToCommand($" {plugin.Localizer["k4.general.prefix"]} {plugin.Localizer["k4.general.targetloading", target.PlayerName]}"); continue; } - if (!AdminManager.CanPlayerTarget(player, target)) + if (!k4player.IsPlayer) { - info.ReplyToCommand($" {plugin.Localizer["k4.general.prefix"]} {plugin.Localizer["k4.general.targetimmunity", target.PlayerName]}"); + info.ReplyToCommand($" {plugin.Localizer["k4.general.prefix"]} {plugin.Localizer["k4.general.targetnobot", target.PlayerName]}"); continue; } - if (!PlayerCache.Instance.ContainsPlayer(target)) + if (!AdminManager.CanPlayerTarget(player, target)) { - info.ReplyToCommand($" {plugin.Localizer["k4.general.prefix"]} {plugin.Localizer["k4.general.targetloading", target.PlayerName]}"); + info.ReplyToCommand($" {plugin.Localizer["k4.general.prefix"]} {plugin.Localizer["k4.general.targetimmunity", target.PlayerName]}"); continue; } - RankData? playerData = PlayerCache.Instance.GetPlayerData(target).rankData; + RankData? playerData = k4player.rankData; if (playerData is null) return; @@ -430,17 +334,15 @@ public void OnCommandGivePoints(CCSPlayerController? player, CommandInfo info) playerData.RoundPoints += parsedInt; playerData.Points += parsedInt; - plugin.SavePlayerCache(target, false); - if (playerName != "SERVER") Server.PrintToChatAll($" {plugin.Localizer["k4.general.prefix"]} {plugin.Localizer["k4.ranks.givepoints", playerName, parsedInt, target.PlayerName]}"); + + Task.Run(() => plugin.SavePlayerDataAsync(k4player, false)); } } public void OnCommandRemovePoints(CCSPlayerController? player, CommandInfo info) { - Plugin plugin = (this.PluginContext.Plugin as Plugin)!; - if (!plugin.CommandHelper(player, info, CommandUsage.CLIENT_AND_SERVER, 2, " ", "@k4system/admin")) return; @@ -462,25 +364,27 @@ public void OnCommandRemovePoints(CCSPlayerController? player, CommandInfo info) foreach (CCSPlayerController target in targetResult.Players) { - if (target.IsBot || target.IsHLTV) + K4Player? k4player = plugin.GetK4Player(target); + + if (k4player is null || !k4player.IsValid) { - info.ReplyToCommand($" {plugin.Localizer["k4.general.prefix"]} {plugin.Localizer["k4.general.targetnobot", target.PlayerName]}"); + info.ReplyToCommand($" {plugin.Localizer["k4.general.prefix"]} {plugin.Localizer["k4.general.targetloading", target.PlayerName]}"); continue; } - if (!AdminManager.CanPlayerTarget(player, target)) + if (!k4player.IsPlayer) { - info.ReplyToCommand($" {plugin.Localizer["k4.general.prefix"]} {plugin.Localizer["k4.general.targetimmunity", target.PlayerName]}"); + info.ReplyToCommand($" {plugin.Localizer["k4.general.prefix"]} {plugin.Localizer["k4.general.targetnobot", target.PlayerName]}"); continue; } - if (!PlayerCache.Instance.ContainsPlayer(target)) + if (!AdminManager.CanPlayerTarget(player, target)) { - info.ReplyToCommand($" {plugin.Localizer["k4.general.prefix"]} {plugin.Localizer["k4.general.targetloading", target.PlayerName]}"); + info.ReplyToCommand($" {plugin.Localizer["k4.general.prefix"]} {plugin.Localizer["k4.general.targetimmunity", target.PlayerName]}"); continue; } - RankData? playerData = PlayerCache.Instance.GetPlayerData(target).rankData; + RankData? playerData = k4player.rankData; if (playerData is null) return; @@ -488,10 +392,10 @@ public void OnCommandRemovePoints(CCSPlayerController? player, CommandInfo info) playerData.RoundPoints -= parsedInt; playerData.Points -= parsedInt; - plugin.SavePlayerCache(target, false); - if (playerName != "SERVER") Server.PrintToChatAll($" {plugin.Localizer["k4.general.prefix"]} {plugin.Localizer["k4.ranks.removepoints", playerName, parsedInt, target.PlayerName]}"); + + Task.Run(() => plugin.SavePlayerDataAsync(k4player, false)); } } } diff --git a/K4-System/src/Module/Rank/RankConfig.cs b/K4-System/src/Module/Rank/RankConfig.cs index ed52709..e4718a9 100644 --- a/K4-System/src/Module/Rank/RankConfig.cs +++ b/K4-System/src/Module/Rank/RankConfig.cs @@ -7,9 +7,9 @@ namespace K4System public partial class ModuleRank : IModuleRank { - public void Initialize_Config(Plugin plugin) + public void Initialize_Config() { - string ranksFilePath = Path.Join(ModuleDirectory, "ranks.jsonc"); + string ranksFilePath = Path.Join(plugin.ModuleDirectory, "ranks.jsonc"); string defaultRanksContent = @"{ ""None"": { // Whatever you set here, be unique. Not read by plugin diff --git a/K4-System/src/Module/Rank/RankEvents.cs b/K4-System/src/Module/Rank/RankEvents.cs index 2024b46..1b0b596 100644 --- a/K4-System/src/Module/Rank/RankEvents.cs +++ b/K4-System/src/Module/Rank/RankEvents.cs @@ -6,19 +6,19 @@ namespace K4System using CounterStrikeSharp.API.Modules.Admin; using CounterStrikeSharp.API.Modules.Memory; using CounterStrikeSharp.API.Modules.Utils; + using K4System.Models; public partial class ModuleRank : IModuleRank { - public void Initialize_Events(Plugin plugin) + public void Initialize_Events() { plugin.RegisterEventHandler((EventPlayerTeam @event, GameEventInfo info) => { - CCSPlayerController player = @event.Userid; - - if (player is null || !player.IsValid || !player.PlayerPawn.IsValid || player.IsBot || player.IsHLTV || !PlayerCache.Instance.ContainsPlayer(player)) + K4Player? k4player = plugin.GetK4Player(@event.Userid); + if (k4player is null || !k4player.IsValid || !k4player.IsPlayer) return HookResult.Continue; - RankData? rankData = PlayerCache.Instance.GetPlayerData(player).rankData; + RankData? rankData = k4player.rankData; if (rankData is null) return HookResult.Continue; @@ -27,7 +27,7 @@ public void Initialize_Events(Plugin plugin) { foreach (Permission permission in rankData.Rank.Permissions) { - AdminManager.AddPlayerPermissions(Utilities.GetPlayerFromSlot(player.Slot), permission.PermissionName); + AdminManager.AddPlayerPermissions(k4player.Controller, permission.PermissionName); } } @@ -36,136 +36,123 @@ public void Initialize_Events(Plugin plugin) rankData.PlayedRound = false; } - if (Config.RankSettings.ScoreboardClantags) - { - string tag = rankData.Rank.Tag ?? $"[{rankData.Rank.Name}]"; - SetPlayerClanTag(player, rankData, tag); - } + SetPlayerClanTag(k4player); return HookResult.Continue; }); plugin.RegisterEventHandler((EventPlayerSpawn @event, GameEventInfo info) => { - CCSPlayerController player = @event.Userid; - - if (player is null || !player.IsValid || !player.PlayerPawn.IsValid || player.IsBot || player.IsHLTV) - return HookResult.Continue; - - if (!PlayerCache.Instance.ContainsPlayer(player)) + K4Player? k4player = plugin.GetK4Player(@event.Userid); + if (k4player is null || !k4player.IsValid || !k4player.IsPlayer) return HookResult.Continue; - if (Config.RankSettings.ScoreboardClantags) - { - RankData? rankData = PlayerCache.Instance.GetPlayerData(player).rankData; - - if (rankData is null) - return HookResult.Continue; - - string tag = rankData.Rank.Tag ?? $"[{rankData.Rank.Name}]"; - SetPlayerClanTag(player, rankData, tag); - } - + SetPlayerClanTag(k4player); return HookResult.Continue; }); plugin.RegisterEventHandler((EventHostageRescued @event, GameEventInfo info) => { - CCSPlayerController player = @event.Userid; - ModifyPlayerPoints(player, Config.PointSettings.HostageRescue, "k4.phrases.hostagerescued"); + ModifyPlayerPointsConnector(@event.Userid, Config.PointSettings.HostageRescue, "k4.phrases.hostagerescued"); return HookResult.Continue; }); plugin.RegisterEventHandler((EventHostageKilled @event, GameEventInfo info) => { - CCSPlayerController player = @event.Userid; - ModifyPlayerPoints(player, Config.PointSettings.HostageKill, "k4.phrases.hostagekilled"); + ModifyPlayerPointsConnector(@event.Userid, Config.PointSettings.HostageKill, "k4.phrases.hostagekilled"); return HookResult.Continue; }); plugin.RegisterEventHandler((EventHostageHurt @event, GameEventInfo info) => { - CCSPlayerController player = @event.Userid; - ModifyPlayerPoints(player, Config.PointSettings.HostageHurt, "k4.phrases.hostagehurt"); + ModifyPlayerPointsConnector(@event.Userid, Config.PointSettings.HostageHurt, "k4.phrases.hostagehurt"); return HookResult.Continue; }); plugin.RegisterEventHandler((EventBombPickup @event, GameEventInfo info) => { - CCSPlayerController player = @event.Userid; - ModifyPlayerPoints(player, Config.PointSettings.BombPickup, "k4.phrases.bombpickup"); + ModifyPlayerPointsConnector(@event.Userid, Config.PointSettings.BombPickup, "k4.phrases.bombpickup"); return HookResult.Continue; }); plugin.RegisterEventHandler((EventBombDefused @event, GameEventInfo info) => { - CCSPlayerController player = @event.Userid; - ModifyPlayerPoints(player, Config.PointSettings.BombDefused, "k4.phrases.bombdefused"); + K4Player? k4player = plugin.GetK4Player(@event.Userid); + if (k4player is null || !k4player.IsValid || !k4player.IsPlayer) + return HookResult.Continue; + + ModifyPlayerPoints(k4player, Config.PointSettings.BombDefused, "k4.phrases.bombdefused"); - Utilities.GetPlayers().Where(p => p.TeamNum == (int)CsTeam.CounterTerrorist && p.Slot != player.Slot) - .ToList() - .ForEach(p => ModifyPlayerPoints(p, Config.PointSettings.BombDefusedOthers, "k4.phrases.bombdefused")); + var players = plugin.K4Players.Where(p => p.IsValid && p.IsPlayer && p.Controller.Team == CsTeam.CounterTerrorist && p != k4player); + foreach (K4Player player in players) + { + ModifyPlayerPoints(player, Config.PointSettings.BombDefusedOthers, "k4.phrases.bombdefused"); + } return HookResult.Continue; }); plugin.RegisterEventHandler((EventBombDropped @event, GameEventInfo info) => { - CCSPlayerController player = @event.Userid; - ModifyPlayerPoints(player, Config.PointSettings.BombDrop, "k4.phrases.bombdropped"); + ModifyPlayerPointsConnector(@event.Userid, Config.PointSettings.BombDrop, "k4.phrases.bombdropped"); return HookResult.Continue; }); plugin.RegisterEventHandler((EventBombExploded @event, GameEventInfo info) => { - Utilities.GetPlayers().Where(p => p.TeamNum == (int)CsTeam.Terrorist) - .ToList() - .ForEach(p => ModifyPlayerPoints(p, Config.PointSettings.BombExploded, "k4.phrases.bombexploded")); - + foreach (K4Player k4player in plugin.K4Players) + { + if (k4player.IsValid && k4player.IsPlayer && k4player.Controller.Team == CsTeam.Terrorist) + { + ModifyPlayerPoints(k4player, Config.PointSettings.BombExploded, "k4.phrases.bombexploded"); + } + } return HookResult.Continue; }); plugin.RegisterEventHandler((EventHostageRescuedAll @event, GameEventInfo info) => { - Utilities.GetPlayers().Where(p => p.TeamNum == (int)CsTeam.CounterTerrorist) - .ToList() - .ForEach(p => ModifyPlayerPoints(p, Config.PointSettings.HostageRescueAll, "k4.phrases.hostagerescuedall")); - + foreach (K4Player k4player in plugin.K4Players) + { + if (k4player.IsValid && k4player.IsPlayer && k4player.Controller.Team == CsTeam.CounterTerrorist) + { + ModifyPlayerPoints(k4player, Config.PointSettings.HostageRescueAll, "k4.phrases.hostagerescuedall"); + } + } return HookResult.Continue; }); plugin.RegisterEventHandler((EventRoundMvp @event, GameEventInfo info) => { - CCSPlayerController player = @event.Userid; - ModifyPlayerPoints(player, Config.PointSettings.MVP, "k4.phrases.mvp"); + ModifyPlayerPointsConnector(@event.Userid, Config.PointSettings.MVP, "k4.phrases.mvp"); return HookResult.Continue; }); plugin.RegisterEventHandler((EventRoundStart @event, GameEventInfo info) => { - List players = Utilities.GetPlayers().Where(p => p?.IsValid == true && p.PlayerPawn?.IsValid == true && !p.IsBot && !p.IsHLTV && p.SteamID.ToString().Length == 17).ToList(); - - players.Where(p => p.PawnIsAlive && PlayerCache.Instance.ContainsPlayer(p)) - .ToList() - .ForEach(p => + foreach (K4Player k4player in plugin.K4Players) + { + if (k4player.IsValid && k4player.IsPlayer) { - RankData? rankData = PlayerCache.Instance.GetPlayerData(p).rankData; + RankData? rankData = k4player.rankData; if (rankData is not null) { rankData.PlayedRound = true; } - }); + } + } - if (players.Count < Config.RankSettings.MinPlayers) + if (plugin.K4Players.Count(p => p.IsPlayer) < Config.RankSettings.MinPlayers) + { Server.PrintToChatAll($" {plugin.Localizer["k4.general.prefix"]} {plugin.Localizer["k4.ranks.notenoughplayers", Config.RankSettings.MinPlayers]}"); + } return HookResult.Continue; }, HookMode.Post); plugin.RegisterEventHandler((EventBombPlanted @event, GameEventInfo info) => { - CCSPlayerController player = @event.Userid; - ModifyPlayerPoints(player, Config.PointSettings.BombPlant, "k4.phrases.bombplanted"); + ModifyPlayerPointsConnector(@event.Userid, Config.PointSettings.BombPlant, "k4.phrases.bombplanted"); return HookResult.Continue; }); @@ -176,69 +163,71 @@ public void Initialize_Events(Plugin plugin) return HookResult.Continue; } - CCSPlayerController victim = @event.Userid; - - if (victim is null || !victim.IsValid || !victim.PlayerPawn.IsValid || victim.Connected == PlayerConnectedState.PlayerDisconnecting) + K4Player? k4victim = plugin.GetK4Player(@event.Userid); + if (k4victim is null || !k4victim.IsValid) return HookResult.Continue; - CCSPlayerController killer = @event.Attacker; + K4Player? k4attacker = plugin.GetK4Player(@event.Attacker); - if (!victim.IsBot) + if (!k4victim.IsPlayer) { - playerKillStreaks[victim.Slot] = (0, DateTime.Now); + k4victim.KillStreak = (0, DateTime.Now); - if (killer is null || !killer.IsValid || victim.UserId == killer.UserId) + if (k4attacker is null || !k4attacker.IsValid) { - ModifyPlayerPoints(victim, Config.PointSettings.Suicide, "k4.phrases.suicide"); + ModifyPlayerPoints(k4victim, Config.PointSettings.Suicide, "k4.phrases.suicide"); } - else if (killer != null && killer.IsValid && (Config.RankSettings.PointsForBots || !killer.IsBot)) + else { - string? extraInfo = Config.RankSettings.PlayerNameKillMessages ? plugin.Localizer["k4.phrases.dying.extra", killer.PlayerName] : null!; - ModifyPlayerPoints(victim, CalculateDynamicPoints(killer, victim, Config.PointSettings.Death), "k4.phrases.dying", extraInfo); + if (Config.RankSettings.PointsForBots || k4attacker.IsPlayer) + { + string? extraInfo = Config.RankSettings.PlayerNameKillMessages ? plugin.Localizer["k4.phrases.dying.extra", k4attacker.PlayerName] : null!; + ModifyPlayerPoints(k4victim, CalculateDynamicPoints(k4attacker, k4victim, Config.PointSettings.Death), "k4.phrases.dying", extraInfo); + } } } - if (killer?.IsValid == true && killer.PlayerPawn?.IsValid == true && !killer.IsBot && victim.UserId != killer.UserId && (Config.RankSettings.PointsForBots || !victim.IsBot)) + if (k4attacker?.IsValid == true && k4attacker.IsPlayer && (Config.RankSettings.PointsForBots || k4victim.IsPlayer)) { - if (!Config.GeneralSettings.FFAMode && killer.TeamNum == victim.TeamNum) + if (!Config.GeneralSettings.FFAMode && k4attacker.Controller.Team == k4victim.Controller.Team) { - ModifyPlayerPoints(killer, Config.PointSettings.TeamKill, "k4.phrases.teamkill"); + ModifyPlayerPoints(k4attacker, Config.PointSettings.TeamKill, "k4.phrases.teamkill"); } else { - string? extraInfo = Config.RankSettings.PlayerNameKillMessages ? plugin.Localizer["k4.phrases.kill.extra", victim.PlayerName] : null!; - ModifyPlayerPoints(killer, CalculateDynamicPoints(killer, victim, Config.PointSettings.Kill), "k4.phrases.kill", extraInfo); + string? extraInfo = Config.RankSettings.PlayerNameKillMessages ? plugin.Localizer["k4.phrases.kill.extra", k4victim.PlayerName] : null!; + ModifyPlayerPoints(k4attacker, CalculateDynamicPoints(k4attacker, k4victim, Config.PointSettings.Kill), "k4.phrases.kill", extraInfo); if (@event.Headshot) { - ModifyPlayerPoints(killer, Config.PointSettings.Headshot, "k4.phrases.headshot"); + ModifyPlayerPoints(k4attacker, Config.PointSettings.Headshot, "k4.phrases.headshot"); } int penetrateCount = @event.Penetrated; if (penetrateCount > 0 && Config.PointSettings.Penetrated > 0) { int calculatedPoints = penetrateCount * Config.PointSettings.Penetrated; - ModifyPlayerPoints(killer, calculatedPoints, "k4.phrases.penetrated"); + ModifyPlayerPoints(k4attacker, calculatedPoints, "k4.phrases.penetrated"); } if (@event.Noscope) { - ModifyPlayerPoints(killer, Config.PointSettings.NoScope, "k4.phrases.noscope"); + ModifyPlayerPoints(k4attacker, Config.PointSettings.NoScope, "k4.phrases.noscope"); } if (@event.Thrusmoke) { - ModifyPlayerPoints(killer, Config.PointSettings.Thrusmoke, "k4.phrases.thrusmoke"); + ModifyPlayerPoints(k4attacker, Config.PointSettings.Thrusmoke, "k4.phrases.thrusmoke"); } if (@event.Attackerblind) { - ModifyPlayerPoints(killer, Config.PointSettings.BlindKill, "k4.phrases.blindkill"); + ModifyPlayerPoints(k4attacker, Config.PointSettings.BlindKill, "k4.phrases.blindkill"); } if (@event.Distance >= Config.PointSettings.LongDistance) { - ModifyPlayerPoints(killer, Config.PointSettings.LongDistanceKill, "k4.phrases.longdistance"); + ModifyPlayerPoints(k4attacker, Config.PointSettings.LongDistanceKill, "k4.phrases.longdistance"); } string lowerCaseWeaponName = @event.Weapon.ToLower(); @@ -248,45 +237,43 @@ public void Initialize_Events(Plugin plugin) //Killed by grenade explosion case var _ when lowerCaseWeaponName.Contains("hegrenade"): { - ModifyPlayerPoints(killer, Config.PointSettings.GrenadeKill, "k4.phrases.grenadekill"); + ModifyPlayerPoints(k4attacker, Config.PointSettings.GrenadeKill, "k4.phrases.grenadekill"); break; } //Molotov or Incendiary fire kill case var _ when lowerCaseWeaponName.Contains("inferno"): { - ModifyPlayerPoints(killer, Config.PointSettings.InfernoKill, "k4.phrases.infernokill"); + ModifyPlayerPoints(k4attacker, Config.PointSettings.InfernoKill, "k4.phrases.infernokill"); break; } // Grenade impact kill (hitting a player and killing them with a grenade when they are 1hp for example) case var _ when lowerCaseWeaponName.Contains("grenade") || lowerCaseWeaponName.Contains("molotov") || lowerCaseWeaponName.Contains("flashbang") || lowerCaseWeaponName.Contains("bumpmine"): { - ModifyPlayerPoints(killer, Config.PointSettings.ImpactKill, "k4.phrases.impactkill"); + ModifyPlayerPoints(k4attacker, Config.PointSettings.ImpactKill, "k4.phrases.impactkill"); break; } // knife_ would not handle default knives (therefore changed to just "knife"), this will also not handle other Danger Zone items such as axes and wrenches (if they are even implemented yet in CS2) case var _ when lowerCaseWeaponName.Contains("knife") || lowerCaseWeaponName.Contains("bayonet"): { - ModifyPlayerPoints(killer, Config.PointSettings.KnifeKill, "k4.phrases.knifekill"); + ModifyPlayerPoints(k4attacker, Config.PointSettings.KnifeKill, "k4.phrases.knifekill"); break; } case "taser": { - ModifyPlayerPoints(killer, Config.PointSettings.TaserKill, "k4.phrases.taserkill"); + ModifyPlayerPoints(k4attacker, Config.PointSettings.TaserKill, "k4.phrases.taserkill"); break; } } - if (playerKillStreaks.ContainsKey(killer.Slot)) - { - int time = Config.PointSettings.SecondsBetweenKills; - bool isTimeBetweenKills = time <= 0 || DateTime.Now - playerKillStreaks[killer.Slot].lastKillTime <= TimeSpan.FromSeconds(time); + int time = Config.PointSettings.SecondsBetweenKills; + bool isTimeBetweenKills = time <= 0 || DateTime.Now - k4attacker.KillStreak.lastKillTime <= TimeSpan.FromSeconds(time); - if (playerKillStreaks[killer.Slot].killStreak > 0 && isTimeBetweenKills) - { - playerKillStreaks[killer.Slot] = (playerKillStreaks[killer.Slot].killStreak + 1, DateTime.Now); - int killStreak = playerKillStreaks[killer.Slot].killStreak; + if (k4attacker.KillStreak.killStreak > 0 && isTimeBetweenKills) + { + k4attacker.KillStreak = (k4attacker.KillStreak.killStreak + 1, DateTime.Now); + int killStreak = k4attacker.KillStreak.killStreak; - Dictionary killStreakMap = new Dictionary + Dictionary killStreakMap = new Dictionary { { 2, (Config.PointSettings.DoubleKill, "k4.phrases.doublekill") }, { 3, (Config.PointSettings.TripleKill, "k4.phrases.triplekill") }, @@ -301,30 +288,27 @@ public void Initialize_Events(Plugin plugin) { 12, (Config.PointSettings.GodLike, "k4.phrases.godlike") } }; - if (killStreakMap.TryGetValue(killStreak, out var killStreakInfo)) - { - ModifyPlayerPoints(killer, killStreakInfo.points, killStreakInfo.message); - } - else - playerKillStreaks[killer.Slot] = (1, DateTime.Now); + if (killStreakMap.TryGetValue(killStreak, out var killStreakInfo)) + { + ModifyPlayerPoints(k4attacker, killStreakInfo.points, killStreakInfo.message); } else - playerKillStreaks[killer.Slot] = (1, DateTime.Now); + k4attacker.KillStreak = (1, DateTime.Now); } else - playerKillStreaks[killer.Slot] = (1, DateTime.Now); + k4attacker.KillStreak = (1, DateTime.Now); + } } - CCSPlayerController assiter = @event.Assister; - - if (assiter != null && assiter.IsValid && assiter.PlayerPawn.IsValid && !assiter.IsBot && (Config.RankSettings.PointsForBots || !victim.IsBot)) + K4Player? k4assister = plugin.GetK4Player(@event.Assister); + if (k4assister?.IsValid == true && k4assister.IsPlayer && (Config.RankSettings.PointsForBots || k4victim.IsPlayer)) { - ModifyPlayerPoints(assiter, Config.PointSettings.Assist, "k4.phrases.assist"); + ModifyPlayerPoints(k4assister, Config.PointSettings.Assist, "k4.phrases.assist"); if (@event.Assistedflash) { - ModifyPlayerPoints(assiter, Config.PointSettings.AssistFlash, "k4.phrases.assistflash"); + ModifyPlayerPoints(k4assister, Config.PointSettings.AssistFlash, "k4.phrases.assistflash"); } } diff --git a/K4-System/src/Module/Rank/RankFunctions.cs b/K4-System/src/Module/Rank/RankFunctions.cs index ec2095a..0a7ab02 100644 --- a/K4-System/src/Module/Rank/RankFunctions.cs +++ b/K4-System/src/Module/Rank/RankFunctions.cs @@ -9,13 +9,15 @@ namespace K4System using Microsoft.Extensions.Logging; using System.Data; using K4SharedApi; + using K4System.Models; + using Dapper; + using MaxMind.GeoIP2; + using MaxMind.GeoIP2.Exceptions; public partial class ModuleRank : IModuleRank { public bool IsPointsAllowed() { - Plugin plugin = (this.PluginContext.Plugin as Plugin)!; - if (plugin.GameRules == null) return false; @@ -31,14 +33,15 @@ public Rank GetNoneRank() public void BeforeRoundEnd(int winnerTeam) { - List players = Utilities.GetPlayers().Where(p => p?.IsValid == true && p.PlayerPawn?.IsValid == true && !p.IsBot && !p.IsHLTV && p.SteamID.ToString().Length == 17 && PlayerCache.Instance.ContainsPlayer(p)).ToList(); - - foreach (CCSPlayerController player in players) + foreach (K4Player k4player in plugin.K4Players) { - if (!player.PawnIsAlive) - playerKillStreaks[player.Slot] = (0, DateTime.Now); + if (!k4player.IsValid || !k4player.IsPlayer) + continue; + + if (!k4player.Controller.PawnIsAlive) + k4player.KillStreak = (0, DateTime.Now); - RankData? playerData = PlayerCache.Instance.GetPlayerData(player).rankData; + RankData? playerData = k4player.rankData; if (playerData is null) continue; @@ -46,32 +49,30 @@ public void BeforeRoundEnd(int winnerTeam) if (!playerData.PlayedRound) continue; - if (player.TeamNum <= (int)CsTeam.Spectator) + if (k4player.Controller.TeamNum <= (int)CsTeam.Spectator) continue; - if (winnerTeam == player.TeamNum) + if (winnerTeam == k4player.Controller.TeamNum) { - ModifyPlayerPoints(player, Config.PointSettings.RoundWin, "k4.phrases.roundwin"); + ModifyPlayerPoints(k4player, Config.PointSettings.RoundWin, "k4.phrases.roundwin"); } else { - ModifyPlayerPoints(player, Config.PointSettings.RoundLose, "k4.phrases.roundlose"); + ModifyPlayerPoints(k4player, Config.PointSettings.RoundLose, "k4.phrases.roundlose"); } - Plugin plugin = (this.PluginContext.Plugin as Plugin)!; - if (!playerData.MuteMessages && Config.RankSettings.RoundEndPoints) { if (playerData.RoundPoints > 0) { - player.PrintToChat($" {plugin.Localizer["k4.general.prefix"]} {plugin.Localizer["k4.ranks.summarypoints.gain", playerData.RoundPoints]}"); + k4player.Controller.PrintToChat($" {plugin.Localizer["k4.general.prefix"]} {plugin.Localizer["k4.ranks.summarypoints.gain", playerData.RoundPoints]}"); } else if (playerData.RoundPoints < 0) { - player.PrintToChat($" {plugin.Localizer["k4.general.prefix"]} {plugin.Localizer["k4.ranks.summarypoints.loss", Math.Abs(playerData.RoundPoints)]}"); + k4player.Controller.PrintToChat($" {plugin.Localizer["k4.general.prefix"]} {plugin.Localizer["k4.ranks.summarypoints.loss", Math.Abs(playerData.RoundPoints)]}"); } else - player.PrintToChat($" {plugin.Localizer["k4.general.prefix"]} {plugin.Localizer["k4.ranks.summarypoints.nochange"]}"); + k4player.Controller.PrintToChat($" {plugin.Localizer["k4.general.prefix"]} {plugin.Localizer["k4.ranks.summarypoints.nochange"]}"); } playerData.RoundPoints = 0; @@ -83,78 +84,44 @@ public Rank GetPlayerRank(int points) return rankDictionary.LastOrDefault(kv => points >= kv.Value.Point).Value ?? noneRank!; } - public static int GetPlayerRankID(CCSPlayerController player) + public void ModifyPlayerPointsConnector(CCSPlayerController player, int amount, string reason, string? extraInfo = null) { - if (!PlayerCache.Instance.ContainsPlayer(player)) - return 0; - - RankData? playerData = PlayerCache.Instance.GetPlayerData(player).rankData; - - if (playerData is null) - return 0; - - return playerData.Rank.Id++; - } - - public static int GetPlayerPoints(CCSPlayerController player) - { - if (!PlayerCache.Instance.ContainsPlayer(player)) - return 0; - - RankData? playerData = PlayerCache.Instance.GetPlayerData(player).rankData; - - if (playerData is null) - return 0; + K4Player? k4player = plugin.GetK4Player(player); + if (k4player is null) + return; - return playerData.Points; + ModifyPlayerPoints(k4player, amount, reason, extraInfo); } - public void ModifyPlayerPoints(CCSPlayerController player, int amount, string reason, string? extraInfo = null) + public void ModifyPlayerPoints(K4Player k4player, int amount, string reason, string? extraInfo = null) { if (!IsPointsAllowed()) return; - if (player is null || !player.IsValid || !player.PlayerPawn.IsValid) - return; - - if (player.IsBot || player.IsHLTV) - return; - - if (player.SteamID.ToString().Length != 17) - return; - - if (!PlayerCache.Instance.ContainsPlayer(player)) + if (!k4player.IsValid || !k4player.IsPlayer) return; - RankData? playerData = PlayerCache.Instance.GetPlayerData(player).rankData; + RankData? playerData = k4player.rankData; if (playerData is null) return; - Plugin plugin = (this.PluginContext.Plugin as Plugin)!; - if (Config.RankSettings.RoundEndPoints && plugin.GameRules != null && !plugin.GameRules.WarmupPeriod) playerData.RoundPoints += amount; if (amount == 0) return; - if (amount > 0 && AdminManager.PlayerHasPermissions(player, "@k4system/vip/points-multiplier")) + if (amount > 0 && AdminManager.PlayerHasPermissions(k4player.Controller, "@k4system/vip/points-multiplier")) { amount = (int)Math.Round(amount * Config.RankSettings.VipMultiplier); } playerData.Points += amount; - Server.NextWorldUpdate(() => + Server.NextFrame(() => { - if (player is null || !player.IsValid || !player.PlayerPawn.IsValid) - return; - - if (player.IsBot || player.IsHLTV) - return; - - if (player.SteamID.ToString().Length != 17) + if (!k4player.IsValid || !k4player.IsPlayer) return; if (!playerData.MuteMessages && (!Config.RankSettings.RoundEndPoints || plugin.GameRules == null || plugin.GameRules.WarmupPeriod)) @@ -163,22 +130,22 @@ public void ModifyPlayerPoints(CCSPlayerController player, int amount, string re { if (extraInfo != null) { - player.PrintToChat($" {plugin.Localizer["k4.general.prefix"]} {plugin.Localizer["k4.ranks.points.gain", playerData.Points, amount, plugin.Localizer[reason]]}{extraInfo}"); + k4player.Controller.PrintToChat($" {plugin.Localizer["k4.general.prefix"]} {plugin.Localizer["k4.ranks.points.gain", playerData.Points, amount, plugin.Localizer[reason]]}{extraInfo}"); } else { - player.PrintToChat($" {plugin.Localizer["k4.general.prefix"]} {plugin.Localizer["k4.ranks.points.gain", playerData.Points, amount, plugin.Localizer[reason]]}"); + k4player.Controller.PrintToChat($" {plugin.Localizer["k4.general.prefix"]} {plugin.Localizer["k4.ranks.points.gain", playerData.Points, amount, plugin.Localizer[reason]]}"); } } else if (amount < 0) { if (extraInfo != null) { - player.PrintToChat($" {plugin.Localizer["k4.general.prefix"]} {plugin.Localizer["k4.ranks.points.loss", playerData.Points, Math.Abs(amount), plugin.Localizer[reason]]}{extraInfo}"); + k4player.Controller.PrintToChat($" {plugin.Localizer["k4.general.prefix"]} {plugin.Localizer["k4.ranks.points.loss", playerData.Points, Math.Abs(amount), plugin.Localizer[reason]]}{extraInfo}"); } else { - player.PrintToChat($" {plugin.Localizer["k4.general.prefix"]} {plugin.Localizer["k4.ranks.points.loss", playerData.Points, Math.Abs(amount), plugin.Localizer[reason]]}"); + k4player.Controller.PrintToChat($" {plugin.Localizer["k4.general.prefix"]} {plugin.Localizer["k4.ranks.points.loss", playerData.Points, Math.Abs(amount), plugin.Localizer[reason]]}"); } } } @@ -188,19 +155,19 @@ public void ModifyPlayerPoints(CCSPlayerController player, int amount, string re playerData.Points = 0; if (Config.RankSettings.ScoreboardScoreSync) - player.Score = playerData.Points; + k4player.Controller.Score = playerData.Points; Rank newRank = GetPlayerRank(playerData.Points); if (playerData.Rank.Name != newRank.Name) { - player.PrintToChat($" {plugin.Localizer["k4.general.prefix"]} {plugin.Localizer[playerData.Rank.Point > newRank.Point ? "k4.ranks.demote" : "k4.ranks.promote", newRank.Color, newRank.Name]}"); + k4player.Controller.PrintToChat($" {plugin.Localizer["k4.general.prefix"]} {plugin.Localizer[playerData.Rank.Point > newRank.Point ? "k4.ranks.demote" : "k4.ranks.promote", newRank.Color, newRank.Name]}"); if (playerData.Rank.Permissions != null && playerData.Rank.Permissions.Count > 0) { foreach (Permission permission in playerData.Rank.Permissions) { - AdminManager.RemovePlayerPermissions(Utilities.GetPlayerFromSlot(player.Slot), permission.PermissionName); + AdminManager.RemovePlayerPermissions(k4player.Controller, permission.PermissionName); } } @@ -208,47 +175,37 @@ public void ModifyPlayerPoints(CCSPlayerController player, int amount, string re { foreach (Permission permission in newRank.Permissions) { - AdminManager.AddPlayerPermissions(Utilities.GetPlayerFromSlot(player.Slot), permission.PermissionName); + AdminManager.AddPlayerPermissions(k4player.Controller, permission.PermissionName); } } playerData.Rank = newRank; } - if (Config.RankSettings.ScoreboardClantags) - { - string tag = playerData.Rank.Tag ?? $"[{playerData.Rank.Name}]"; - SetPlayerClanTag(player, playerData, tag); - } + SetPlayerClanTag(k4player); } - public async Task<(int playerPlace, int totalPlayers)> GetPlayerPlaceAndCountAsync(string steamID) + public async Task<(int playerPlace, int totalPlayers)> GetPlayerPlaceAndCountAsync(K4Player k4player) { string query = $@"SELECT (SELECT COUNT(*) FROM `{Config.DatabaseSettings.TablePrefix}k4ranks` WHERE `points` > - (SELECT `points` FROM `{Config.DatabaseSettings.TablePrefix}k4ranks` WHERE `steam_id` = @steamId)) AS playerPlace, + (SELECT `points` FROM `{Config.DatabaseSettings.TablePrefix}k4ranks` WHERE `steam_id` = @SteamId)) AS playerPlace, (SELECT COUNT(*) FROM `{Config.DatabaseSettings.TablePrefix}k4ranks`) AS totalPlayers"; - MySqlParameter[] parameters = new MySqlParameter[] - { - new MySqlParameter("@steamid", steamID), - }; - try { - using (MySqlCommand command = new MySqlCommand(query)) + using (var connection = plugin.CreateConnection(Config)) { - DataTable dataTable = await Database.Instance.ExecuteReaderAsync(command.CommandText, parameters); + await connection.OpenAsync(); - if (dataTable.Rows.Count > 0) + var result = await connection.QueryFirstOrDefaultAsync(query, new { SteamId = k4player.SteamID }); + + if (result != null) { - foreach (DataRow row in dataTable.Rows) - { - int playerPlace = Convert.ToInt32(row[0]) + 1; - int totalPlayers = Convert.ToInt32(row[1]); - return (playerPlace, totalPlayers); - } + int playerPlace = result.playerPlace + 1; + int totalPlayers = result.totalPlayers; + return (playerPlace, totalPlayers); } } } @@ -260,19 +217,16 @@ public void ModifyPlayerPoints(CCSPlayerController player, int amount, string re return (0, 0); } - public int CalculateDynamicPoints(CCSPlayerController from, CCSPlayerController to, int amount) + public int CalculateDynamicPoints(K4Player from, K4Player to, int amount) { if (!Config.RankSettings.DynamicDeathPoints) return amount; - if (to.IsBot || from.IsBot) - return amount; - - if (!PlayerCache.Instance.ContainsPlayer(from) || !PlayerCache.Instance.ContainsPlayer(to)) + if (!to.IsPlayer || !from.IsPlayer) return amount; - RankData? fromCache = PlayerCache.Instance.GetPlayerData(from).rankData; - RankData? toCache = PlayerCache.Instance.GetPlayerData(to).rankData; + RankData? fromCache = from.rankData; + RankData? toCache = to.rankData; if (fromCache is null || toCache is null) return amount; @@ -285,9 +239,16 @@ public int CalculateDynamicPoints(CCSPlayerController from, CCSPlayerController return (int)Math.Round(result); } - public void SetPlayerClanTag(CCSPlayerController player, RankData playerData, string tag) + public void SetPlayerClanTag(K4Player k4player) { - if (!playerData.HideAdminTag) + string tag = string.Empty; + + if (Config.RankSettings.ScoreboardClantags && k4player.rankData != null) + { + tag = k4player.rankData.Rank.Tag ?? $"[{k4player.rankData.Rank.Name}]"; + } + + if (k4player.rankData?.HideAdminTag == false) { foreach (AdminSettingsEntry adminSettings in Config.GeneralSettings.AdminSettingsList) { @@ -297,15 +258,15 @@ public void SetPlayerClanTag(CCSPlayerController player, RankData playerData, st switch (adminSettings.Permission[0]) { case '@': - if (AdminManager.PlayerHasPermissions(player, adminSettings.Permission)) + if (AdminManager.PlayerHasPermissions(k4player.Controller, adminSettings.Permission)) tag = adminSettings.ClanTag; break; case '#': - if (AdminManager.PlayerInGroup(player, adminSettings.Permission)) + if (AdminManager.PlayerInGroup(k4player.Controller, adminSettings.Permission)) tag = adminSettings.ClanTag; break; default: - if (AdminManager.PlayerHasCommandOverride(player, adminSettings.Permission)) + if (AdminManager.PlayerHasCommandOverride(k4player.Controller, adminSettings.Permission)) tag = adminSettings.ClanTag; break; } @@ -314,11 +275,48 @@ public void SetPlayerClanTag(CCSPlayerController player, RankData playerData, st if (Config.RankSettings.CountryTagEnabled) { - Plugin plugin = (this.PluginContext.Plugin as Plugin)!; - tag = $"{plugin.GetPlayerCountryCode(player)} | {tag}"; + string countryTag = GetPlayerCountryCode(k4player.Controller); + tag = tag.Length > 0 ? $"{countryTag} | {tag}" : countryTag; + } + + k4player.ClanTag = tag; + } + + public string GetPlayerCountryCode(CCSPlayerController player) + { + string? playerIp = player.IpAddress; + + if (playerIp == null) + return "??"; + + string[] parts = playerIp.Split(':'); + string realIP = parts.Length == 2 ? parts[0] : playerIp; + + string filePath = Path.Combine(plugin.ModuleDirectory, "GeoLite2-Country.mmdb"); + if (!File.Exists(filePath)) + { + Logger.LogError($"GeoLite2-Country.mmdb not found in {filePath}. Download it from https://github.com/P3TERX/GeoLite.mmdb/releases and place it in the same directory as the plugin."); + return "??"; } - player.Clan = tag; + using (DatabaseReader reader = new DatabaseReader(filePath)) + { + try + { + MaxMind.GeoIP2.Responses.CountryResponse response = reader.Country(realIP); + return response.Country.IsoCode ?? "??"; + } + catch (AddressNotFoundException) + { + Logger.LogError($"The address {realIP} is not in the database."); + return "??"; + } + catch (GeoIP2Exception ex) + { + Logger.LogError($"Error: {ex.Message}"); + return "??"; + } + } } } } diff --git a/K4-System/src/Module/Rank/RankGlobals.cs b/K4-System/src/Module/Rank/RankGlobals.cs index 8b2c699..3f5bd73 100644 --- a/K4-System/src/Module/Rank/RankGlobals.cs +++ b/K4-System/src/Module/Rank/RankGlobals.cs @@ -32,14 +32,12 @@ public class RankData public required bool MuteMessages { get; set; } } - public readonly PluginContext PluginContext; + public readonly Plugin plugin; public readonly ILogger Logger; - public required PluginConfig Config { get; set; } - public required string ModuleDirectory { get; set; } + public readonly PluginConfig Config; public Dictionary rankDictionary = new Dictionary(); - public Dictionary playerKillStreaks = new Dictionary(); public Rank? noneRank; } } \ No newline at end of file diff --git a/K4-System/src/Module/Rank/RankMenus.cs b/K4-System/src/Module/Rank/RankMenus.cs index 7f7a862..b20f532 100644 --- a/K4-System/src/Module/Rank/RankMenus.cs +++ b/K4-System/src/Module/Rank/RankMenus.cs @@ -1,72 +1,79 @@ namespace K4System { - using MySqlConnector; - using CounterStrikeSharp.API.Modules.Menu; using CounterStrikeSharp.API.Modules.Utils; using Microsoft.Extensions.Logging; - using System.Data; + using Dapper; + using K4System.Models; + using CounterStrikeSharp.API; public partial class ModuleRank : IModuleRank { ChatMenu ranksMenu = new ChatMenu("Available Rank List"); - public void Initialize_Menus(Plugin plugin) + public void Initialize_Menus() { foreach (Rank rank in rankDictionary.Values) { ranksMenu.AddMenuOption(rank.Point == -1 ? plugin.Localizer["k4.ranks.listdefault", rank.Color, rank.Name] : plugin.Localizer["k4.ranks.listitem", rank.Color, rank.Name, rank.Point], (player, option) => { - Task<(int playerCount, float percentage)> task = Task.Run(() => FetchRanksMenuDataAsync(rank.Name)); - task.Wait(); - var result = task.Result; - - int playerCount = result.playerCount; - float percentage = result.percentage; + ulong steamID = player.SteamID; - if (!PlayerCache.Instance.ContainsPlayer(player)) - return; - - RankData? playerData = PlayerCache.Instance.GetPlayerData(player).rankData; + Task.Run(async () => + { + (int playerCount, float percentage) taskValues = await FetchRanksMenuDataAsync(rank.Name); - if (playerData is null) - return; + int playerCount = taskValues.playerCount; + float percentage = taskValues.percentage; - int pointsDifference = Math.Abs(rank.Point - playerData.Points); + Server.NextFrame(() => + { + K4Player? k4player = plugin.GetK4Player(steamID); + if (k4player is null || !k4player.IsValid || !k4player.IsPlayer) + return; - player.PrintToChat($" {plugin.Localizer["k4.general.prefix"]} {plugin.Localizer["k4.ranks.selected.title", rank.Color, rank.Name]}"); - player.PrintToChat($" {plugin.Localizer["k4.ranks.selected.line1", playerCount, percentage]}"); + RankData? playerData = k4player.rankData; - if (rank.Name == playerData.Rank.Name) - player.PrintToChat($" {plugin.Localizer["k4.ranks.selected.line2.current", rank.Point]}"); - else - player.PrintToChat($" {plugin.Localizer[rank.Point > playerData.Rank.Point ? "k4.ranks.selected.line2" : "k4.ranks.selected.line2.passed", rank.Point == -1 ? "None" : rank.Point, pointsDifference]}"); + if (playerData is null) + return; - if (rank.Permissions != null && rank.Permissions.Count > 0) - { - player.PrintToChat($" {plugin.Localizer["k4.ranks.selected.benefitline"]}"); + int pointsDifference = Math.Abs(rank.Point - playerData.Points); - int permissionCount = 0; - string permissionLine = ""; + player.PrintToChat($" {plugin.Localizer["k4.general.prefix"]} {plugin.Localizer["k4.ranks.selected.title", rank.Color, rank.Name]}"); + player.PrintToChat($" {plugin.Localizer["k4.ranks.selected.line1", playerCount, percentage]}"); - foreach (Permission permission in rank.Permissions) - { - permissionLine += $"{ChatColors.Lime}{permission.DisplayName}{ChatColors.Silver}, "; - permissionCount++; + if (rank.Name == playerData.Rank.Name) + player.PrintToChat($" {plugin.Localizer["k4.ranks.selected.line2.current", rank.Point]}"); + else + player.PrintToChat($" {plugin.Localizer[rank.Point > playerData.Rank.Point ? "k4.ranks.selected.line2" : "k4.ranks.selected.line2.passed", rank.Point == -1 ? "None" : rank.Point, pointsDifference]}"); - if (permissionCount % 3 == 0) + if (rank.Permissions != null && rank.Permissions.Count > 0) { - player.PrintToChat($" {permissionLine.TrimEnd(',', ' ')}"); - permissionLine = ""; + player.PrintToChat($" {plugin.Localizer["k4.ranks.selected.benefitline"]}"); + + int permissionCount = 0; + string permissionLine = ""; + + foreach (Permission permission in rank.Permissions) + { + permissionLine += $"{ChatColors.Lime}{permission.DisplayName}{ChatColors.Silver}, "; + permissionCount++; + + if (permissionCount % 3 == 0) + { + player.PrintToChat($" {permissionLine.TrimEnd(',', ' ')}"); + permissionLine = ""; + } + } + + if (!string.IsNullOrEmpty(permissionLine)) + { + player.PrintToChat($" {permissionLine.TrimEnd(',', ' ')}"); + } } - } - - if (!string.IsNullOrEmpty(permissionLine)) - { - player.PrintToChat($" {permissionLine.TrimEnd(',', ' ')}"); - } - } + }); + }); }); } } @@ -77,30 +84,30 @@ public void Initialize_Menus(Plugin plugin) float percentage = 0.0f; string query = $@" - SELECT - COUNT(*) AS PlayerCount, - ROUND((COUNT(*) / TotalPlayers) * 100, 2) AS Percentage - FROM - `{Config.DatabaseSettings.TablePrefix}k4ranks`, - (SELECT COUNT(*) AS TotalPlayers FROM `{Config.DatabaseSettings.TablePrefix}k4ranks`) AS Total - WHERE - `rank` = '{rankName}' - GROUP BY - `rank`;"; + SELECT + COUNT(*) AS PlayerCount, + ROUND((COUNT(*) / TotalPlayers.Total) * 100, 2) AS Percentage + FROM + `{Config.DatabaseSettings.TablePrefix}k4ranks` + CROSS JOIN + (SELECT COUNT(*) AS Total FROM `{Config.DatabaseSettings.TablePrefix}k4ranks`) TotalPlayers + WHERE + `rank` = @RankName + GROUP BY + `rank`;"; try { - using (MySqlCommand command = new MySqlCommand(query)) + using (var connection = plugin.CreateConnection(Config)) { - DataTable dataTable = await Database.Instance.ExecuteReaderAsync(command.CommandText); + await connection.OpenAsync(); - if (dataTable.Rows.Count > 0) + var result = await connection.QueryFirstOrDefaultAsync<(int, float)>(query, new { RankName = rankName }); + + if (result != default) { - foreach (DataRow row in dataTable.Rows) - { - playerCount = Convert.ToInt32(row[0]); - percentage = Convert.ToSingle(row[1]); - } + playerCount = result.Item1; + percentage = result.Item2; } } @@ -112,5 +119,6 @@ GROUP BY return (0, 0); } } + } } \ No newline at end of file diff --git a/K4-System/src/Module/Stat/StatCommands.cs b/K4-System/src/Module/Stat/StatCommands.cs index 99466d9..6c7b90c 100644 --- a/K4-System/src/Module/Stat/StatCommands.cs +++ b/K4-System/src/Module/Stat/StatCommands.cs @@ -2,10 +2,11 @@ namespace K4System { using CounterStrikeSharp.API.Core; using CounterStrikeSharp.API.Modules.Commands; + using K4System.Models; public partial class ModuleStat : IModuleStat { - public void Initialize_Commands(Plugin plugin) + public void Initialize_Commands() { CommandSettings commands = Config.CommandSettings; @@ -17,18 +18,18 @@ public void Initialize_Commands(Plugin plugin) public void OnCommandStats(CCSPlayerController? player, CommandInfo info) { - Plugin plugin = (this.PluginContext.Plugin as Plugin)!; - if (!plugin.CommandHelper(player, info, CommandUsage.CLIENT_ONLY)) return; - if (!PlayerCache.Instance.ContainsPlayer(player!)) + K4Player? k4player = plugin.GetK4Player(player!); + + if (k4player is null) { info.ReplyToCommand($" {plugin.Localizer["k4.general.prefix"]} {plugin.Localizer["k4.general.loading"]}"); return; } - StatData? playerData = PlayerCache.Instance.GetPlayerData(player!).statData; + StatData? playerData = k4player.statData; if (playerData is null) return; diff --git a/K4-System/src/Module/Stat/StatEvents.cs b/K4-System/src/Module/Stat/StatEvents.cs index de74e6c..170dd3d 100644 --- a/K4-System/src/Module/Stat/StatEvents.cs +++ b/K4-System/src/Module/Stat/StatEvents.cs @@ -4,46 +4,104 @@ namespace K4System using CounterStrikeSharp.API; using CounterStrikeSharp.API.Core; using CounterStrikeSharp.API.Modules.Utils; + using K4System.Models; public partial class ModuleStat : IModuleStat { - public void Initialize_Events(Plugin plugin) + public void Initialize_Events() { plugin.RegisterEventHandler((EventPlayerDeath @event, GameEventInfo info) => { if (!IsStatsAllowed()) - { return HookResult.Continue; - } - CCSPlayerController victim = @event.Userid; + K4Player? k4victim = plugin.GetK4Player(@event.Userid); - if (victim.IsBot && !Config.StatisticSettings.StatsForBots) + if (k4victim is null || !k4victim.IsValid) return HookResult.Continue; - if (victim != null && victim.IsValid && victim.PlayerPawn.IsValid && victim.UserId.HasValue && victim.UserId != -1 && !victim.IsBot) + if (!k4victim.IsPlayer && !Config.StatisticSettings.StatsForBots) + return HookResult.Continue; + + if (!k4victim.IsPlayer) { - ModifyPlayerStats(victim, "deaths", 1); + ModifyPlayerStats(k4victim, "deaths", 1); } - CCSPlayerController attacker = @event.Attacker; - - if (attacker != null && attacker.IsValid && attacker.PlayerPawn.IsValid && !attacker.IsBot && !attacker.IsHLTV) + K4Player? k4attacker = plugin.GetK4Player(@event.Attacker); + if (k4attacker != null && k4attacker.IsValid && k4attacker.IsPlayer) { - ModifyPlayerStats(attacker, "kills", 1); + ModifyPlayerStats(k4attacker, "kills", 1); if (!FirstBlood) { FirstBlood = true; - ModifyPlayerStats(attacker, "firstblood", 1); + ModifyPlayerStats(k4attacker, "firstblood", 1); } - } - CCSPlayerController assister = @event.Assister; + if (@event.Noscope) + ModifyPlayerStats(k4attacker, "noscope_kill", 1); + + if (@event.Penetrated > 0) + ModifyPlayerStats(k4attacker, "penetrated_kill", 1); + + if (@event.Thrusmoke) + ModifyPlayerStats(k4attacker, "thrusmoke_kill", 1); + + if (@event.Attackerblind) + ModifyPlayerStats(k4attacker, "flashed_kill", 1); - if (assister != null && assister.IsValid && assister.PlayerPawn.IsValid && !assister.IsBot && !assister.IsHLTV) + if (@event.Dominated > 0) + ModifyPlayerStats(k4attacker, "dominated_kill", 1); + + if (@event.Revenge > 0) + ModifyPlayerStats(k4attacker, "revenge_kill", 1); + + if (@event.Assistedflash) + ModifyPlayerStats(k4attacker, "assist_flash", 1); + + switch (@event.Hitgroup) + { + case (int)HitGroup_t.HITGROUP_HEAD: + ModifyPlayerStats(k4attacker, "headshots", 1); + break; + case (int)HitGroup_t.HITGROUP_CHEST: + ModifyPlayerStats(k4attacker, "chest_hits", 1); + break; + case (int)HitGroup_t.HITGROUP_STOMACH: + ModifyPlayerStats(k4attacker, "stomach_hits", 1); + break; + case (int)HitGroup_t.HITGROUP_LEFTARM: + ModifyPlayerStats(k4attacker, "left_arm_hits", 1); + break; + case (int)HitGroup_t.HITGROUP_RIGHTARM: + ModifyPlayerStats(k4attacker, "right_arm_hits", 1); + break; + case (int)HitGroup_t.HITGROUP_LEFTLEG: + ModifyPlayerStats(k4attacker, "left_leg_hits", 1); + break; + case (int)HitGroup_t.HITGROUP_RIGHTLEG: + ModifyPlayerStats(k4attacker, "right_leg_hits", 1); + break; + case (int)HitGroup_t.HITGROUP_NECK: + ModifyPlayerStats(k4attacker, "neck_hits", 1); + break; + case (int)HitGroup_t.HITGROUP_UNUSED: + ModifyPlayerStats(k4attacker, "unused_hits", 1); + break; + case (int)HitGroup_t.HITGROUP_GEAR: + ModifyPlayerStats(k4attacker, "gear_hits", 1); + break; + case (int)HitGroup_t.HITGROUP_SPECIAL: + ModifyPlayerStats(k4attacker, "special_hits", 1); + break; + } + } + + K4Player? k4assister = plugin.GetK4Player(@event.Assister); + if (k4assister != null && k4assister.IsValid && k4assister.IsPlayer) { - ModifyPlayerStats(assister, "assists", 1); + ModifyPlayerStats(k4assister, "assists", 1); } return HookResult.Continue; @@ -52,43 +110,38 @@ public void Initialize_Events(Plugin plugin) plugin.RegisterEventHandler((EventGrenadeThrown @event, GameEventInfo info) => { if (!IsStatsAllowed()) - { return HookResult.Continue; - } - CCSPlayerController player = @event.Userid; - - if (player != null && player.IsValid && player.PlayerPawn.IsValid && !player.IsBot && !player.IsHLTV) - { - ModifyPlayerStats(player, "grenades", 1); - } + K4Player? k4player = plugin.GetK4Player(@event.Userid); + if (k4player is null || !k4player.IsValid || !k4player.IsPlayer) + return HookResult.Continue; + ModifyPlayerStats(k4player, "grenades", 1); return HookResult.Continue; }); plugin.RegisterEventHandler((EventPlayerHurt @event, GameEventInfo info) => { if (!IsStatsAllowed()) - { return HookResult.Continue; - } - CCSPlayerController victim = @event.Userid; + K4Player? k4victim = plugin.GetK4Player(@event.Userid); + if (k4victim is null || !k4victim.IsValid) + return HookResult.Continue; - if (victim != null && victim.IsValid && victim.PlayerPawn.IsValid && victim.UserId.HasValue && victim.UserId != -1 && !victim.IsBot && !victim.IsHLTV) + if (k4victim.IsPlayer) { - ModifyPlayerStats(victim, "hits_taken", 1); + ModifyPlayerStats(k4victim, "hits_taken", 1); } - CCSPlayerController attacker = @event.Attacker; - - if (attacker != null && attacker.IsValid && attacker.PlayerPawn.IsValid && !attacker.IsBot && !attacker.IsHLTV) + K4Player? k4attacker = plugin.GetK4Player(@event.Attacker); + if (k4attacker != null && k4attacker.IsValid && k4attacker.IsPlayer) { - ModifyPlayerStats(attacker, "hits_given", 1); + ModifyPlayerStats(k4attacker, "hits_given", 1); if (@event.Hitgroup == 1) { - ModifyPlayerStats(attacker, "headshots", 1); + ModifyPlayerStats(k4attacker, "headshots", 1); } } @@ -101,68 +154,110 @@ public void Initialize_Events(Plugin plugin) return HookResult.Continue; }); - plugin.RegisterEventHandler((EventWeaponFire @event, GameEventInfo info) => + plugin.RegisterEventHandler((EventBombPlanted @event, GameEventInfo info) => { - if (!IsStatsAllowed()) - { + K4Player? k4player = plugin.GetK4Player(@event.Userid); + if (k4player is null || !k4player.IsValid || !k4player.IsPlayer) return HookResult.Continue; - } - CCSPlayerController player = @event.Userid; + ModifyPlayerStats(k4player, "bomb_planted", 1); + return HookResult.Continue; + }); - if (player is null || !player.IsValid || !player.PlayerPawn.IsValid) + plugin.RegisterEventHandler((EventHostageRescued @event, GameEventInfo info) => + { + K4Player? k4player = plugin.GetK4Player(@event.Userid); + if (k4player is null || !k4player.IsValid || !k4player.IsPlayer) return HookResult.Continue; - if (player.IsBot || player.IsHLTV) + ModifyPlayerStats(k4player, "hostage_rescued", 1); + return HookResult.Continue; + }); + + plugin.RegisterEventHandler((EventHostageKilled @event, GameEventInfo info) => + { + K4Player? k4player = plugin.GetK4Player(@event.Userid); + if (k4player is null || !k4player.IsValid || !k4player.IsPlayer) return HookResult.Continue; - if (@event.Weapon.Contains("knife") || @event.Weapon.Contains("bayonet")) - { + ModifyPlayerStats(k4player, "hostage_killed", 1); + return HookResult.Continue; + }); + + plugin.RegisterEventHandler((EventBombDefused @event, GameEventInfo info) => + { + K4Player? k4player = plugin.GetK4Player(@event.Userid); + if (k4player is null || !k4player.IsValid || !k4player.IsPlayer) return HookResult.Continue; - } - ModifyPlayerStats(player, "shoots", 1); + ModifyPlayerStats(k4player, "bomb_defused", 1); return HookResult.Continue; }); - plugin.RegisterEventHandler((EventRoundMvp @event, GameEventInfo info) => + plugin.RegisterEventHandler((EventRoundEnd @event, GameEventInfo info) => { - if (!IsStatsAllowed()) + foreach (K4Player k4player in plugin.K4Players) { - return HookResult.Continue; + if (!k4player.IsValid || !k4player.IsPlayer) + continue; + + CsTeam team = k4player.Controller.Team; + + if (team <= CsTeam.Spectator) + continue; + + ModifyPlayerStats(k4player, "rounds_overall", 1); + ModifyPlayerStats(k4player, team == CsTeam.Terrorist ? "rounds_t" : "rounds_ct", 1); } + return HookResult.Continue; + }); - CCSPlayerController player = @event.Userid; + plugin.RegisterEventHandler((EventWeaponFire @event, GameEventInfo info) => + { + if (!IsStatsAllowed()) + return HookResult.Continue; - if (player is null || !player.IsValid || !player.PlayerPawn.IsValid) + K4Player? k4player = plugin.GetK4Player(@event.Userid); + if (k4player is null || !k4player.IsValid || !k4player.IsPlayer) return HookResult.Continue; - if (player.IsBot || player.IsHLTV) + if (@event.Weapon.Contains("knife") || @event.Weapon.Contains("bayonet")) return HookResult.Continue; - ModifyPlayerStats(player, "mvp", 1); + ModifyPlayerStats(k4player, "shoots", 1); + return HookResult.Continue; + }); + + plugin.RegisterEventHandler((EventRoundMvp @event, GameEventInfo info) => + { + if (!IsStatsAllowed()) + return HookResult.Continue; + + K4Player? k4player = plugin.GetK4Player(@event.Userid); + if (k4player is null || !k4player.IsValid || !k4player.IsPlayer) + return HookResult.Continue; + + ModifyPlayerStats(k4player, "mvp", 1); return HookResult.Continue; }); plugin.RegisterEventHandler((@event, info) => { if (!IsStatsAllowed()) - { return HookResult.Continue; - } - List players = Utilities.GetPlayers().Where(p => p?.IsValid == true && p.PlayerPawn?.IsValid == true && !p.IsBot && !p.IsHLTV && p.SteamID.ToString().Length == 17).ToList(); + List k4players = plugin.K4Players.Where(p => p.IsValid && p.IsPlayer).ToList(); if (Config.GeneralSettings.FFAMode) { - CCSPlayerController? player = players.OrderByDescending(p => p?.Score).FirstOrDefault(); + K4Player? k4player = k4players.OrderByDescending(p => p.Controller.Score).FirstOrDefault(); - if (player != null) + if (k4player != null) { - ModifyPlayerStats(player, "game_win", 1); + ModifyPlayerStats(k4player, "game_win", 1); } - players.Where(p => p != player).ToList().ForEach(p => ModifyPlayerStats(p, "game_lose", 1)); + k4players.Where(p => p != k4player).ToList().ForEach(p => ModifyPlayerStats(p, "game_lose", 1)); } else { @@ -178,11 +273,11 @@ public void Initialize_Events(Plugin plugin) CsTeam winnerTeam = ctScore > tScore ? CsTeam.CounterTerrorist : tScore > ctScore ? CsTeam.Terrorist : CsTeam.None; - if ((int)winnerTeam > (int)CsTeam.Spectator) + if (winnerTeam > CsTeam.Spectator) { - players.Where(p => p.TeamNum > (int)CsTeam.Spectator) + k4players.Where(p => p.Controller.Team > CsTeam.Spectator) .ToList() - .ForEach(p => ModifyPlayerStats(p, (CsTeam)p.TeamNum == winnerTeam ? "game_win" : "game_lose", 1)); + .ForEach(p => ModifyPlayerStats(p, p.Controller.Team == winnerTeam ? "game_win" : "game_lose", 1)); } } diff --git a/K4-System/src/Module/Stat/StatFunctions.cs b/K4-System/src/Module/Stat/StatFunctions.cs index 16e622d..6667dbf 100644 --- a/K4-System/src/Module/Stat/StatFunctions.cs +++ b/K4-System/src/Module/Stat/StatFunctions.cs @@ -3,14 +3,13 @@ namespace K4System using CounterStrikeSharp.API; using CounterStrikeSharp.API.Core; using CounterStrikeSharp.API.Modules.Utils; + using K4System.Models; public partial class ModuleStat : IModuleStat { public bool IsStatsAllowed() { int notBots = Utilities.GetPlayers().Count(player => !player.IsBot); - - Plugin plugin = (this.PluginContext.Plugin as Plugin)!; return plugin.GameRules != null && (!plugin.GameRules.WarmupPeriod || Config.StatisticSettings.WarmupStats) && (Config.StatisticSettings.MinPlayers <= notBots); } @@ -21,36 +20,22 @@ public void BeforeRoundEnd(int winnerTeam) if (winnerTeam > (int)CsTeam.Spectator) { - List players = Utilities.GetPlayers(); - - foreach (CCSPlayerController player in players) + foreach (K4Player k4player in plugin.K4Players) { - if (player is null || !player.IsValid || !player.PlayerPawn.IsValid) + if (k4player is null || !k4player.IsValid || !k4player.IsPlayer) continue; - if (player.IsBot || player.IsHLTV) + if (k4player.Controller.Team <= CsTeam.Spectator) continue; - if (player.TeamNum <= (int)CsTeam.Spectator) - continue; - - ModifyPlayerStats(player, player.TeamNum == winnerTeam ? "round_win" : "round_lose", 1); + ModifyPlayerStats(k4player, k4player.Controller.TeamNum == winnerTeam ? "round_win" : "round_lose", 1); } } } - public void ModifyPlayerStats(CCSPlayerController player, string field, int amount) + public void ModifyPlayerStats(K4Player k4player, string field, int amount) { - if (player is null || !player.IsValid || !player.PlayerPawn.IsValid) - return; - - if (player.IsBot || player.IsHLTV) - return; - - if (!PlayerCache.Instance.ContainsPlayer(player)) - return; - - StatData? playerData = PlayerCache.Instance.GetPlayerData(player).statData; + StatData? playerData = k4player.statData; if (playerData != null) { diff --git a/K4-System/src/Module/Stat/StatGlobals.cs b/K4-System/src/Module/Stat/StatGlobals.cs index a4e9c5f..d95c798 100644 --- a/K4-System/src/Module/Stat/StatGlobals.cs +++ b/K4-System/src/Module/Stat/StatGlobals.cs @@ -11,9 +11,10 @@ public class StatData public Dictionary StatFields { get; set; } = new Dictionary(); } - public readonly PluginContext PluginContext; + public readonly Plugin plugin; public readonly ILogger Logger; - public required PluginConfig Config { get; set; } + public readonly PluginConfig Config; + public bool FirstBlood = false; } } \ No newline at end of file diff --git a/K4-System/src/Module/Time/TimeCommands.cs b/K4-System/src/Module/Time/TimeCommands.cs index cf180b7..84cdfd9 100644 --- a/K4-System/src/Module/Time/TimeCommands.cs +++ b/K4-System/src/Module/Time/TimeCommands.cs @@ -3,10 +3,11 @@ namespace K4System using CounterStrikeSharp.API.Core; using CounterStrikeSharp.API.Modules.Commands; using CounterStrikeSharp.API.Modules.Utils; + using K4System.Models; public partial class ModuleTime : IModuleTime { - public void Initialize_Commands(Plugin plugin) + public void Initialize_Commands() { CommandSettings commands = Config.CommandSettings; @@ -18,18 +19,18 @@ public void Initialize_Commands(Plugin plugin) public void OnCommandTime(CCSPlayerController? player, CommandInfo info) { - Plugin plugin = (this.PluginContext.Plugin as Plugin)!; - if (!plugin.CommandHelper(player, info, CommandUsage.CLIENT_ONLY)) return; - if (!PlayerCache.Instance.ContainsPlayer(player!)) + K4Player? k4player = plugin.GetK4Player(player!); + + if (k4player is null) { info.ReplyToCommand($" {plugin.Localizer["k4.general.prefix"]} {plugin.Localizer["k4.general.loading"]}"); return; } - TimeData? playerData = PlayerCache.Instance.GetPlayerData(player!).timeData; + TimeData? playerData = k4player.timeData; if (playerData is null) return; diff --git a/K4-System/src/Module/Time/TimeEvents.cs b/K4-System/src/Module/Time/TimeEvents.cs index a417079..d51bce6 100644 --- a/K4-System/src/Module/Time/TimeEvents.cs +++ b/K4-System/src/Module/Time/TimeEvents.cs @@ -3,25 +3,19 @@ namespace K4System { using CounterStrikeSharp.API.Core; using CounterStrikeSharp.API.Modules.Utils; + using K4System.Models; public partial class ModuleTime : IModuleTime { - public void Initialize_Events(Plugin plugin) + public void Initialize_Events() { plugin.RegisterEventHandler((EventPlayerTeam @event, GameEventInfo info) => { - CCSPlayerController player = @event.Userid; - - if (player is null || !player.IsValid || !player.PlayerPawn.IsValid) - return HookResult.Continue; - - if (player.IsBot || player.IsHLTV) + K4Player? k4player = plugin.GetK4Player(@event.Userid); + if (k4player is null || !k4player.IsValid || !k4player.IsPlayer) return HookResult.Continue; - if (!PlayerCache.Instance.ContainsPlayer(player)) - return HookResult.Continue; - - TimeData? playerData = PlayerCache.Instance.GetPlayerData(player).timeData; + TimeData? playerData = k4player.timeData; if (playerData is null) return HookResult.Continue; @@ -43,18 +37,11 @@ public void Initialize_Events(Plugin plugin) plugin.RegisterEventHandler((EventPlayerSpawn @event, GameEventInfo info) => { - CCSPlayerController player = @event.Userid; - - if (player is null || !player.IsValid || !player.PlayerPawn.IsValid) - return HookResult.Continue; - - if (player.IsBot || player.IsHLTV) - return HookResult.Continue; - - if (!PlayerCache.Instance.ContainsPlayer(player)) + K4Player? k4player = plugin.GetK4Player(@event.Userid); + if (k4player is null || !k4player.IsValid || !k4player.IsPlayer) return HookResult.Continue; - TimeData? playerData = PlayerCache.Instance.GetPlayerData(player).timeData; + TimeData? playerData = k4player.timeData; if (playerData is null) return HookResult.Continue; @@ -71,18 +58,12 @@ public void Initialize_Events(Plugin plugin) plugin.RegisterEventHandler((EventPlayerDeath @event, GameEventInfo info) => { - CCSPlayerController player = @event.Userid; - - if (player is null || !player.IsValid || !player.PlayerPawn.IsValid) - return HookResult.Continue; - - if (player.IsBot || player.IsHLTV) - return HookResult.Continue; + K4Player? k4player = plugin.GetK4Player(@event.Userid); - if (!PlayerCache.Instance.ContainsPlayer(player)) + if (k4player is null || !k4player.IsValid || !k4player.IsPlayer) return HookResult.Continue; - TimeData? playerData = PlayerCache.Instance.GetPlayerData(player).timeData; + TimeData? playerData = k4player.timeData; if (playerData is null) return HookResult.Continue; diff --git a/K4-System/src/Module/Time/TimeFunctions.cs b/K4-System/src/Module/Time/TimeFunctions.cs index 518d152..e7a50b4 100644 --- a/K4-System/src/Module/Time/TimeFunctions.cs +++ b/K4-System/src/Module/Time/TimeFunctions.cs @@ -1,26 +1,27 @@ namespace K4System { + using System.Runtime.CompilerServices; using System.Text; using CounterStrikeSharp.API.Core; using CounterStrikeSharp.API.Modules.Utils; + using K4System.Models; public partial class ModuleTime : IModuleTime { - public void BeforeDisconnect(CCSPlayerController player) + public void BeforeDisconnect(K4Player k4player) { DateTime now = DateTime.UtcNow; - - TimeData? playerData = PlayerCache.Instance.GetPlayerData(player).timeData; + TimeData? playerData = k4player.timeData; if (playerData is null) return; playerData.TimeFields["all"] += (int)(now - playerData.Times["Connect"]).TotalSeconds; - playerData.TimeFields[GetFieldForTeam((CsTeam)player.TeamNum)] += (int)(now - playerData.Times["Team"]).TotalSeconds; + playerData.TimeFields[GetFieldForTeam(k4player.Controller.Team)] += (int)(now - playerData.Times["Team"]).TotalSeconds; - if ((CsTeam)player.TeamNum > CsTeam.Spectator) - playerData.TimeFields[player.PawnIsAlive ? "alive" : "dead"] += (int)(now - playerData.Times["Death"]).TotalSeconds; + if (k4player.Controller.Team > CsTeam.Spectator) + playerData.TimeFields[k4player.Controller.PawnIsAlive ? "alive" : "dead"] += (int)(now - playerData.Times["Death"]).TotalSeconds; // This is for the mapchange cases playerData.Times = new Dictionary @@ -33,43 +34,48 @@ public void BeforeDisconnect(CCSPlayerController player) public string GetFieldForTeam(CsTeam team) { - switch (team) + return team switch { - case CsTeam.Terrorist: - return "t"; - case CsTeam.CounterTerrorist: - return "ct"; - default: - return "spec"; - } + CsTeam.Terrorist => "t", + CsTeam.CounterTerrorist => "ct", + _ => "spec" + }; } public string FormatPlaytime(int totalSeconds) { - string[] units = { "k4.phrases.shortyear", "k4.phrases.shortmonth", "k4.phrases.shortday", "k4.phrases.shorthour", "k4.phrases.shortminute", "k4.phrases.shortsecond" }; - int[] values = { totalSeconds / 31536000, totalSeconds % 31536000 / 2592000, totalSeconds % 2592000 / 86400, totalSeconds % 86400 / 3600, totalSeconds % 3600 / 60, totalSeconds % 60 }; + string[] units = { "year", "month", "day", "hour", "minute", "second" }; + int[] timeDivisors = { 31536000, 2592000, 86400, 3600, 60, 1 }; StringBuilder formattedTime = new StringBuilder(); - bool addedValue = false; - Plugin plugin = (this.PluginContext.Plugin as Plugin)!; - for (int i = 0; i < units.Length; i++) { - if (values[i] > 0) + int timeValue = totalSeconds / timeDivisors[i]; + totalSeconds %= timeDivisors[i]; + if (timeValue > 0) { - formattedTime.Append($"{values[i]}{plugin.Localizer[units[i]]}, "); + if (formattedTime.Length > 0) + { + formattedTime.Append(", "); + } + formattedTime.Append($"{timeValue}{GetShortUnit(units[i])}"); addedValue = true; } } if (!addedValue) { - formattedTime.Append("0s"); + return "0" + GetShortUnit("second"); } - return formattedTime.ToString().TrimEnd(' ', ','); + return formattedTime.ToString(); + + string GetShortUnit(string unit) + { + return plugin.Localizer[$"k4.phrases.short{unit}"]; + } } } } diff --git a/K4-System/src/Module/Time/TimeGlobals.cs b/K4-System/src/Module/Time/TimeGlobals.cs index a654973..4337f41 100644 --- a/K4-System/src/Module/Time/TimeGlobals.cs +++ b/K4-System/src/Module/Time/TimeGlobals.cs @@ -12,9 +12,9 @@ public class TimeData public Dictionary TimeFields { get; set; } = new Dictionary(); } - public readonly PluginContext PluginContext; + public readonly Plugin plugin; public readonly ILogger Logger; - public required PluginConfig Config { get; set; } + public readonly PluginConfig Config; } } \ No newline at end of file diff --git a/K4-System/src/Module/Utils/UtilsCommands.cs b/K4-System/src/Module/Utils/UtilsCommands.cs index 3ffa93e..3195971 100644 --- a/K4-System/src/Module/Utils/UtilsCommands.cs +++ b/K4-System/src/Module/Utils/UtilsCommands.cs @@ -5,10 +5,11 @@ namespace K4System using CounterStrikeSharp.API.Modules.Admin; using CounterStrikeSharp.API.Modules.Commands; using CounterStrikeSharp.API.Modules.Utils; + using K4System.Models; public partial class ModuleUtils : IModuleUtils { - public void Initialize_Commands(Plugin plugin) + public void Initialize_Commands() { CommandSettings commands = Config.CommandSettings; @@ -23,36 +24,45 @@ public void OnCommandAdmins(CCSPlayerController? player, CommandInfo info) if (player == null || !player.IsValid || player.PlayerPawn.Value == null) return; - Plugin plugin = (this.PluginContext.Plugin as Plugin)!; + List adminList = new List(); - List onlineAdmins = Utilities.GetPlayers() - .SelectMany(adminPlayer => Config.GeneralSettings.AdminSettingsList.Where(adminSettings => + foreach (K4Player k4player in plugin.K4Players) + { + if (!k4player.IsValid || !k4player.IsPlayer) + continue; + + foreach (AdminSettingsEntry entry in Config.GeneralSettings.AdminSettingsList) { - if (adminSettings.ListColor == null) - return false; - - switch (adminSettings.Permission[0]) - { - case '@': - return AdminManager.PlayerHasPermissions(adminPlayer, adminSettings.Permission); - case '#': - return AdminManager.PlayerInGroup(adminPlayer, adminSettings.Permission); - default: - return AdminManager.PlayerHasCommandOverride(adminPlayer, adminSettings.Permission); - } - }).Select(adminSettings => new { adminPlayer, adminSettings })) - .Select(x => $"{plugin.ApplyPrefixColors(x.adminSettings.ListColor ?? "default")}{x.adminPlayer.PlayerName}") - .ToList(); - - if (onlineAdmins.Count > 0) + if (entry.ListColor == null) + continue; + + if (PlayerHasPermission(k4player, entry.Permission)) + adminList.Add($"{plugin.ApplyPrefixColors(entry.ListColor ?? "default")}{k4player.PlayerName}"); + } + } + + if (adminList.Count > 0) { - string adminList = string.Join($"{ChatColors.Silver},", onlineAdmins); + string onlineAdmins = string.Join($"{ChatColors.Silver},", adminList); info.ReplyToCommand($" {plugin.Localizer["k4.general.prefix"]} {plugin.Localizer["k4.adminlist.title"]}"); - info.ReplyToCommand($" {adminList}"); + info.ReplyToCommand($" {onlineAdmins}"); } else info.ReplyToCommand($" {plugin.Localizer["k4.adminlist.no-admins"]}"); + + bool PlayerHasPermission(K4Player k4player, string permission) + { + switch (permission[0]) + { + case '@': + return AdminManager.PlayerHasPermissions(k4player.Controller, permission); + case '#': + return AdminManager.PlayerInGroup(k4player.Controller, permission); + default: + return AdminManager.PlayerHasCommandOverride(k4player.Controller, permission); + } + } } } } \ No newline at end of file diff --git a/K4-System/src/Module/Utils/UtilsGlobals.cs b/K4-System/src/Module/Utils/UtilsGlobals.cs index 4958260..619452c 100644 --- a/K4-System/src/Module/Utils/UtilsGlobals.cs +++ b/K4-System/src/Module/Utils/UtilsGlobals.cs @@ -6,9 +6,9 @@ namespace K4System public partial class ModuleUtils : IModuleUtils { - public readonly PluginContext PluginContext; + public readonly Plugin plugin; public readonly ILogger Logger; - public required PluginConfig Config { get; set; } + public readonly PluginConfig Config; } } \ No newline at end of file diff --git a/K4-System/src/Plugin/Plugin.cs b/K4-System/src/Plugin/Plugin.cs index b90c80f..8a48b03 100644 --- a/K4-System/src/Plugin/Plugin.cs +++ b/K4-System/src/Plugin/Plugin.cs @@ -6,6 +6,8 @@ using CounterStrikeSharp.API.Core; using CounterStrikeSharp.API.Core.Attributes; using CounterStrikeSharp.API; + using K4System.Models; + using Dapper; [MinimumApiVersion(191)] public sealed partial class Plugin : BasePlugin, IPluginConfig @@ -21,6 +23,8 @@ public sealed partial class Plugin : BasePlugin, IPluginConfig private readonly IModuleTime ModuleTime; private readonly IModuleUtils ModuleUtils; + public List K4Players = new List(); + public Plugin(ModuleRank moduleRank, ModuleStat moduleStat, ModuleTime moduleTime, ModuleUtils moduleUtils) { this.ModuleRank = moduleRank; @@ -36,19 +40,6 @@ public void OnConfigParsed(PluginConfig config) base.Logger.LogWarning("Configuration version mismatch (Expected: {0} | Current: {1})", this.Config.Version, config.Version); } - //** ? Database Connection Init */ - - DatabaseSettings databaseSettings = config.DatabaseSettings; - - Database.Instance.Initialize( - server: databaseSettings.Host, - database: databaseSettings.Database, - userId: databaseSettings.Username, - password: databaseSettings.Password, - port: databaseSettings.Port, - sslMode: databaseSettings.Sslmode, - logger: base.Logger); - //** ? Save Config */ this.Config = config; @@ -97,7 +88,7 @@ public override void Unload(bool hotReload) { //** ? Save Player Caches */ - SaveAllPlayersCache(); + Task.Run(SaveAllPlayersDataAsync); //** ? Release Modules */ @@ -132,25 +123,49 @@ public async Task CreateMultipleTablesAsync() ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;"; string statsModuleTable = $@"CREATE TABLE IF NOT EXISTS `{this.Config.DatabaseSettings.TablePrefix}k4stats` ( - `steam_id` VARCHAR(32) COLLATE 'utf8mb4_unicode_ci' UNIQUE NOT NULL, - `name` VARCHAR(255) COLLATE 'utf8mb4_unicode_ci' NOT NULL, - `lastseen` DATETIME NOT NULL, - `kills` INT NOT NULL DEFAULT 0, - `firstblood` INT NOT NULL DEFAULT 0, - `deaths` INT NOT NULL DEFAULT 0, - `assists` INT NOT NULL DEFAULT 0, - `shoots` INT NOT NULL DEFAULT 0, - `hits_taken` INT NOT NULL DEFAULT 0, - `hits_given` INT NOT NULL DEFAULT 0, - `headshots` INT NOT NULL DEFAULT 0, - `grenades` INT NOT NULL DEFAULT 0, - `mvp` INT NOT NULL DEFAULT 0, - `round_win` INT NOT NULL DEFAULT 0, - `round_lose` INT NOT NULL DEFAULT 0, - `game_win` INT NOT NULL DEFAULT 0, - `game_lose` INT NOT NULL DEFAULT 0, - UNIQUE (`steam_id`) - ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;"; + `steam_id` VARCHAR(32) COLLATE 'utf8mb4_unicode_ci' UNIQUE NOT NULL, + `name` VARCHAR(255) COLLATE 'utf8mb4_unicode_ci' NOT NULL, + `lastseen` DATETIME NOT NULL, + `kills` INT NOT NULL DEFAULT 0, + `firstblood` INT NOT NULL DEFAULT 0, + `deaths` INT NOT NULL DEFAULT 0, + `assists` INT NOT NULL DEFAULT 0, + `shoots` INT NOT NULL DEFAULT 0, + `hits_taken` INT NOT NULL DEFAULT 0, + `hits_given` INT NOT NULL DEFAULT 0, + `headshots` INT NOT NULL DEFAULT 0, + `chest_hits` INT NOT NULL DEFAULT 0, + `stomach_hits` INT NOT NULL DEFAULT 0, + `left_arm_hits` INT NOT NULL DEFAULT 0, + `right_arm_hits` INT NOT NULL DEFAULT 0, + `left_leg_hits` INT NOT NULL DEFAULT 0, + `right_leg_hits` INT NOT NULL DEFAULT 0, + `neck_hits` INT NOT NULL DEFAULT 0, + `unused_hits` INT NOT NULL DEFAULT 0, + `gear_hits` INT NOT NULL DEFAULT 0, + `special_hits` INT NOT NULL DEFAULT 0, + `grenades` INT NOT NULL DEFAULT 0, + `mvp` INT NOT NULL DEFAULT 0, + `round_win` INT NOT NULL DEFAULT 0, + `round_lose` INT NOT NULL DEFAULT 0, + `game_win` INT NOT NULL DEFAULT 0, + `game_lose` INT NOT NULL DEFAULT 0, + `rounds_overall` INT NOT NULL DEFAULT 0, + `rounds_ct` INT NOT NULL DEFAULT 0, + `rounds_t` INT NOT NULL DEFAULT 0, + `bomb_planted` INT NOT NULL DEFAULT 0, + `bomb_defused` INT NOT NULL DEFAULT 0, + `hostage_rescued` INT NOT NULL DEFAULT 0, + `hostage_killed` INT NOT NULL DEFAULT 0, + `noscope_kill` INT NOT NULL DEFAULT 0, + `penetrated_kill` INT NOT NULL DEFAULT 0, + `thrusmoke_kill` INT NOT NULL DEFAULT 0, + `flashed_kill` INT NOT NULL DEFAULT 0, + `dominated_kill` INT NOT NULL DEFAULT 0, + `revenge_kill` INT NOT NULL DEFAULT 0, + `assist_flash` INT NOT NULL DEFAULT 0, + UNIQUE (`steam_id`) + ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;"; string ranksModuleTable = $@"CREATE TABLE IF NOT EXISTS `{this.Config.DatabaseSettings.TablePrefix}k4ranks` ( `steam_id` VARCHAR(32) COLLATE 'utf8mb4_unicode_ci' UNIQUE NOT NULL, @@ -178,25 +193,26 @@ public async Task CreateMultipleTablesAsync() `lastconnect` INT NOT NULL DEFAULT 0 ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;"; - await Database.Instance.ExecuteWithTransactionAsync(async (connection, transaction) => + using (var connection = CreateConnection(Config)) { - MySqlCommand? command1 = new MySqlCommand(timesModuleTable, connection, transaction); - await command1.ExecuteNonQueryAsync(); + await connection.OpenAsync(); - MySqlCommand? command2 = new MySqlCommand(statsModuleTable, connection, transaction); - await command2.ExecuteNonQueryAsync(); + using (var transaction = await connection.BeginTransactionAsync()) + { + await connection.ExecuteAsync(timesModuleTable, transaction: transaction); + await connection.ExecuteAsync(statsModuleTable, transaction: transaction); + await connection.ExecuteAsync(ranksModuleTable, transaction: transaction); - MySqlCommand? command3 = new MySqlCommand(ranksModuleTable, connection, transaction); - await command3.ExecuteNonQueryAsync(); + if (Config.GeneralSettings.LevelRanksCompatibility) + { + await connection.ExecuteAsync(lvlranksModuleTable, transaction: transaction); + } - if (Config.GeneralSettings.LevelRanksCompatibility) - { - MySqlCommand? command4 = new MySqlCommand(lvlranksModuleTable, connection, transaction); - await command4.ExecuteNonQueryAsync(); + await transaction.CommitAsync(); } - }); + } - await PurgeTableRows(); + await PurgeTableRowsAsync(); return true; } } diff --git a/K4-System/src/Plugin/PluginAPI.cs b/K4-System/src/Plugin/PluginAPI.cs index 15ca47f..75e5655 100644 --- a/K4-System/src/Plugin/PluginAPI.cs +++ b/K4-System/src/Plugin/PluginAPI.cs @@ -1,61 +1,82 @@ namespace K4System { using K4SharedApi; - using static K4System.ModuleRank; - - using CounterStrikeSharp.API; using CounterStrikeSharp.API.Core; using CounterStrikeSharp.API.Core.Capabilities; - using CounterStrikeSharp.API.Modules.Commands; - using CounterStrikeSharp.API.Modules.Utils; + using K4System.Models; public sealed partial class Plugin : BasePlugin { - public static PlayerCapability Capability_SharedAPI { get; } = new("k4-system:sharedapi"); + public static PlayerCapability Capability_SharedPlayerAPI { get; } = new("k4-system:sharedapi-player"); public void Initialize_API() { - Capabilities.RegisterPlayerCapability(Capability_SharedAPI, player => new PlayerAPIHandler(player)); + Capabilities.RegisterPlayerCapability(Capability_SharedPlayerAPI, player => new PlayerAPIHandler(player, this)); } } - public class PlayerAPIHandler : IK4SharedApi + public class PlayerAPIHandler : IPlayerAPI { - private readonly CCSPlayerController _player; - private readonly PlayerCacheData? _playerCache; - - public PlayerAPIHandler(CCSPlayerController player) + private readonly K4Player? _player; + public PlayerAPIHandler(CCSPlayerController player, Plugin plugin) { - _player = player; - - if (PlayerCache.Instance.ContainsPlayer(_player)) - _playerCache = PlayerCache.Instance.GetPlayerData(_player); + _player = plugin.GetK4Player(player); } - public int PlayerPoints + public bool IsLoaded => _player is not null; + public bool IsValid => _player?.IsValid == true; + public bool IsPlayer => _player?.IsPlayer == true; + public CCSPlayerController Controller => _player?.Controller ?? throw new Exception("K4-SharedAPI > Controller > Player is not valid or is not a player."); + + public int Points { - get => GetPlayerPoints(); + get + { + if (_player is null || !_player.IsValid || !_player.IsPlayer || _player.rankData is null) + throw new Exception("K4-SharedAPI > Points (get) > Player is not valid or is not a player."); + + return _player.rankData.Points; + } + set + { + if (_player is null || !_player.IsValid || !_player.IsPlayer || _player.rankData is null) + throw new Exception("K4-SharedAPI > Points (set) > Player is not valid or is not a player."); + + _player.rankData.Points = value; + } } - public int PlayerRankID + public int RankID { - get => GetPlayerRankID(); + get + { + if (_player is null || !_player.IsValid || !_player.IsPlayer || _player.rankData is null) + throw new Exception("K4-SharedAPI > RankID (get) > Player is not valid or is not a player."); + + return _player.rankData.Rank.Id + 1; + } } - public int GetPlayerPoints() + public string RankName { - if (_playerCache?.rankData is null) - return 0; + get + { + if (_player is null || !_player.IsValid || !_player.IsPlayer || _player.rankData is null) + throw new Exception("K4-SharedAPI > RankName (get) > Player is not valid or is not a player."); - return _playerCache.rankData.Points; + return _player.rankData.Rank.Name; + } } - public int GetPlayerRankID() + public string RankClanTag { - if (_playerCache?.rankData is null) - return 0; + get + { + if (_player is null || !_player.IsValid || !_player.IsPlayer || _player.rankData is null) + throw new Exception("K4-SharedAPI > RankClanTag (get) > Player is not valid or is not a player."); - return _playerCache.rankData.Rank.Id + 1; + return _player.rankData.Rank.Tag ?? ""; + } } } } \ No newline at end of file diff --git a/K4-System/src/Plugin/PluginBasics.cs b/K4-System/src/Plugin/PluginBasics.cs index 4018c50..726b0ba 100644 --- a/K4-System/src/Plugin/PluginBasics.cs +++ b/K4-System/src/Plugin/PluginBasics.cs @@ -4,8 +4,14 @@ namespace K4System using CounterStrikeSharp.API; using CounterStrikeSharp.API.Core; + using CounterStrikeSharp.API.Modules.Admin; using CounterStrikeSharp.API.Modules.Commands; + using CounterStrikeSharp.API.Modules.Commands.Targeting; using CounterStrikeSharp.API.Modules.Utils; + using K4System.Models; + using static K4System.ModuleRank; + using static K4System.ModuleStat; + using static K4System.ModuleTime; public sealed partial class Plugin : BasePlugin { @@ -73,6 +79,13 @@ public void Initialize_Commands() } } }); + + Config.CommandSettings.ResetMyCommands.ForEach(commandString => + { + AddCommand($"css_{commandString}", "Resets the player's own points to zero", CallbackAnonymizer(OnCommandResetMyData)); + }); + + AddCommand("css_resetdata", "Resets the targeted player's data to zero", CallbackAnonymizer(OnCommandResetData)); } public void Initialize_Events() @@ -81,39 +94,34 @@ public void Initialize_Events() { CCSPlayerController player = @event.Userid; - if (player is null || !player.IsValid || !player.PlayerPawn.IsValid || player.IsBot || player.IsHLTV) + if (player is null || !player.IsValid || !player.PlayerPawn.IsValid) return HookResult.Continue; // Do not load the data, if the user is in the cache already // This free up some resources and prevent plugin to load the same data twice - if (PlayerCache.Instance.ContainsPlayer(player)) + if (K4Players.Any(p => p.Controller == player)) return HookResult.Continue; - LoadPlayerCache(player); + K4Player k4player = new K4Player(this, player); + Task.Run(() => LoadPlayerCacheAsync(k4player)); return HookResult.Continue; }); RegisterEventHandler((EventPlayerDisconnect @event, GameEventInfo info) => { - CCSPlayerController player = @event.Userid; - - if (player is null || !player.IsValid || !player.PlayerPawn.IsValid) - return HookResult.Continue; - - if (player.IsBot || player.IsHLTV) - return HookResult.Continue; + K4Player? k4player = GetK4Player(@event.Userid); - if (!PlayerCache.Instance.ContainsPlayer(player)) + if (k4player is null || !k4player.IsValid || !k4player.IsPlayer) return HookResult.Continue; if (Config.GeneralSettings.ModuleTimes) - ModuleTime.BeforeDisconnect(player); + ModuleTime.BeforeDisconnect(k4player); // Do not save cache for each player on mapchange, because it's handled by an optimised query for all players if (@event.Reason != 1) { - SavePlayerCache(player, true); + Task.Run(() => SavePlayerDataAsync(k4player, true)); } return HookResult.Continue; @@ -132,20 +140,15 @@ public void Initialize_Events() lastRoundStartEventTime = DateTime.Now; - List players = Utilities.GetPlayers(); - - foreach (CCSPlayerController player in players) + foreach (K4Player k4Player in K4Players) { - if (player is null || !player.IsValid || !player.PlayerPawn.IsValid) - continue; - - if (player.IsBot || player.IsHLTV) + if (!k4Player.IsValid || !k4Player.IsPlayer) continue; - if (player.SteamID.ToString().Length != 17) + if (k4Player.Controller.IsBot || k4Player.Controller.IsHLTV) continue; - player.PrintToChat($" {Localizer["k4.general.prefix"]} {ChatColors.Lime}{Localizer["k4.general.spawnmessage"]}"); + k4Player.Controller.PrintToChat($" {Localizer["k4.general.prefix"]} {ChatColors.Lime}{Localizer["k4.general.spawnmessage"]}"); } return HookResult.Continue; @@ -159,8 +162,7 @@ public void Initialize_Events() if (Config.GeneralSettings.ModuleRanks) ModuleRank.BeforeRoundEnd(@event.Winner); - SaveAllPlayersCache(); - + Task.Run(SaveAllPlayersDataAsync); return HookResult.Continue; }, HookMode.Post); @@ -175,8 +177,162 @@ public void Initialize_Events() RegisterListener(() => { GameRules = null; - Task.Run(PurgeTableRows).Wait(); + Task.Run(PurgeTableRowsAsync); }); } + + public void OnCommandResetMyData(CCSPlayerController? player, CommandInfo info) + { + if (!CommandHelper(player, info, CommandUsage.CLIENT_ONLY, 1, "[all|time|stat|rank]")) + return; + + string resetTarget = info.ArgByIndex(1); + + if (resetTarget != "all" && resetTarget != "time" && resetTarget != "stat" && resetTarget != "rank") + { + info.ReplyToCommand($" {Localizer["k4.general.prefix"]} {Localizer["k4.general.commandhelp", info.ArgByIndex(0), "[all|time|stat|rank]"]}"); + return; + } + + K4Player? k4player = GetK4Player(player!); + + if (k4player is null) + { + info.ReplyToCommand($" {Localizer["k4.general.prefix"]} {Localizer["k4.general.loading"]}"); + return; + } + + if (resetTarget == "all" || resetTarget == "time") + { + TimeData? playerData = k4player.timeData; + + if (playerData is null) + return; + + foreach (var field in playerData.TimeFields.Keys.ToList()) + { + playerData.TimeFields[field] = 0; + } + } + + if (resetTarget == "all" || resetTarget == "rank") + { + RankData? playerData = k4player.rankData; + + if (playerData is null) + return; + + playerData.RoundPoints -= playerData.Points; + playerData.Points = Config.RankSettings.StartPoints; + playerData.Rank = ModuleRank.GetNoneRank(); + } + + if (resetTarget == "all" || resetTarget == "stat") + { + StatData? playerData = k4player.statData; + + if (playerData is null) + return; + + foreach (var field in playerData.StatFields.Keys.ToList()) + { + playerData.StatFields[field] = 0; + } + } + + Server.PrintToChatAll($" {Localizer["k4.general.prefix"]} {Localizer["k4.ranks.resetmydata", player!.PlayerName]}"); + + Task.Run(() => SavePlayerDataAsync(k4player, false)); + } + + public void OnCommandResetData(CCSPlayerController? player, CommandInfo info) + { + if (!CommandHelper(player, info, CommandUsage.CLIENT_AND_SERVER, 2, " [all|time|stat|rank]", "@k4system/admin")) + return; + + string resetTarget = info.ArgByIndex(1); + + if (resetTarget != "all" && resetTarget != "time" && resetTarget != "stat" && resetTarget != "rank") + { + info.ReplyToCommand($" {Localizer["k4.general.prefix"]} {Localizer["k4.general.commandhelp", info.ArgByIndex(0), " [all|time|stat|rank]"]}"); + return; + } + + string playerName = player != null && player.IsValid && player.PlayerPawn.Value != null ? player.PlayerName : "SERVER"; + + TargetResult targetResult = info.GetArgTargetResult(1); + + if (!targetResult.Any()) + { + info.ReplyToCommand($" {Localizer["k4.general.prefix"]} {Localizer["k4.general.targetnotfound"]}"); + return; + } + + foreach (CCSPlayerController target in targetResult.Players) + { + K4Player? k4player = GetK4Player(target); + + if (k4player is null || !k4player.IsValid) + { + info.ReplyToCommand($" {Localizer["k4.general.prefix"]} {Localizer["k4.general.targetloading", target.PlayerName]}"); + continue; + } + + if (!k4player.IsPlayer) + { + info.ReplyToCommand($" {Localizer["k4.general.prefix"]} {Localizer["k4.general.targetnobot", target.PlayerName]}"); + continue; + } + + if (!AdminManager.CanPlayerTarget(player, target)) + { + info.ReplyToCommand($" {Localizer["k4.general.prefix"]} {Localizer["k4.general.targetimmunity", target.PlayerName]}"); + continue; + } + + if (resetTarget == "all" || resetTarget == "time") + { + TimeData? playerData = k4player.timeData; + + if (playerData is null) + return; + + foreach (var field in playerData.TimeFields.Keys.ToList()) + { + playerData.TimeFields[field] = 0; + } + } + + if (resetTarget == "all" || resetTarget == "rank") + { + RankData? playerData = k4player.rankData; + + if (playerData is null) + return; + + playerData.RoundPoints -= playerData.Points; + playerData.Points = Config.RankSettings.StartPoints; + playerData.Rank = ModuleRank.GetNoneRank(); + } + + if (resetTarget == "all" || resetTarget == "stat") + { + StatData? playerData = k4player.statData; + + if (playerData is null) + return; + + foreach (var field in playerData.StatFields.Keys.ToList()) + { + playerData.StatFields[field] = 0; + } + } + + if (playerName != "SERVER") + Server.PrintToChatAll($" {Localizer["k4.general.prefix"]} {Localizer["k4.ranks.resetdata", target.PlayerName, playerName]}"); + + Task.Run(() => SavePlayerDataAsync(k4player, false)); + } + } } } \ No newline at end of file diff --git a/K4-System/src/Plugin/PluginCache.cs b/K4-System/src/Plugin/PluginCache.cs deleted file mode 100644 index 88f7465..0000000 --- a/K4-System/src/Plugin/PluginCache.cs +++ /dev/null @@ -1,108 +0,0 @@ - -namespace K4System -{ - using CounterStrikeSharp.API.Core; - - using static K4System.ModuleRank; - using static K4System.ModuleStat; - using static K4System.ModuleTime; - - public class PlayerCacheData - { - public RankData? rankData { get; set; } - public StatData? statData { get; set; } - public TimeData? timeData { get; set; } - } - - public class PlayerCache - { - private static readonly Lazy lazy = new Lazy(() => new PlayerCache()); - public static PlayerCache Instance => lazy.Value; - - private Dictionary cache = new Dictionary(); - - private PlayerCache() { } - - public Dictionary Cache - { - get { return cache; } - } - - private bool IsValidPlayer(CCSPlayerController player) - { - return player is { IsValid: true, IsHLTV: false, IsBot: false, UserId: not null }; - } - - public bool ContainsPlayer(CCSPlayerController player) - { - if (IsValidPlayer(player)) - { - return ContainsPlayer(player.SteamID); - } - else - throw new ArgumentException("The player is invalid"); - } - - public bool ContainsPlayer(ulong steamID) - { - if (steamID.ToString().Length != 17) - return false; - - return cache.ContainsKey(steamID); - } - - public PlayerCacheData GetPlayerData(CCSPlayerController player) - { - if (IsValidPlayer(player)) - { - return GetPlayerData(player.SteamID); - } - else - throw new ArgumentException("The player is invalid"); - } - - public PlayerCacheData GetPlayerData(ulong steamID) - { - if (steamID.ToString().Length != 17) - throw new ArgumentException("The player is invalid"); - - return cache[steamID]; - } - - public void AddOrUpdatePlayer(CCSPlayerController player, PlayerCacheData data) - { - if (IsValidPlayer(player)) - { - AddOrUpdatePlayer(player.SteamID, data); - } - else - throw new ArgumentException("The player is invalid"); - } - - public void AddOrUpdatePlayer(ulong steamID, PlayerCacheData data) - { - if (steamID.ToString().Length != 17) - return; - - cache[steamID] = data; - } - - public bool RemovePlayer(CCSPlayerController player) - { - if (IsValidPlayer(player)) - { - return RemovePlayer(player.SteamID); - } - else - throw new ArgumentException("The player is invalid"); - } - - public bool RemovePlayer(ulong steamID) - { - if (steamID.ToString().Length != 17) - return false; - - return cache.Remove(steamID); - } - } -} \ No newline at end of file diff --git a/K4-System/src/Plugin/PluginConfig.cs b/K4-System/src/Plugin/PluginConfig.cs index 02a72d2..778ca57 100644 --- a/K4-System/src/Plugin/PluginConfig.cs +++ b/K4-System/src/Plugin/PluginConfig.cs @@ -81,7 +81,7 @@ public sealed class CommandSettings [JsonPropertyName("resetmyrank-commands")] public List ResetMyCommands { get; set; } = new List { - "resetmyrank" + "resetmydata" }; [JsonPropertyName("ranks-commands")] diff --git a/K4-System/src/Plugin/PluginDatabase.cs b/K4-System/src/Plugin/PluginDatabase.cs index 5be6eb2..bda26fe 100644 --- a/K4-System/src/Plugin/PluginDatabase.cs +++ b/K4-System/src/Plugin/PluginDatabase.cs @@ -1,148 +1,499 @@ using System.Data; +using CounterStrikeSharp.API; +using CounterStrikeSharp.API.Core; +using Dapper; +using K4System.Models; using Microsoft.Extensions.Logging; using MySqlConnector; +using static K4System.ModuleRank; +using static K4System.ModuleStat; +using static K4System.ModuleTime; -namespace K4System +namespace K4System; + +public sealed partial class Plugin : BasePlugin { - public sealed class Database + public MySqlConnection CreateConnection(PluginConfig config) { - private ILogger? _Logger; - private static readonly Lazy instance = new Lazy(() => new Database()); - public static Database Instance => instance.Value; - - private string? connectionString; - private Database() { } + DatabaseSettings _settings = config.DatabaseSettings; - public void Initialize(ILogger logger, string server, string database, string userId, string password, int port = 3306, string sslMode = "None") + MySqlConnectionStringBuilder builder = new MySqlConnectionStringBuilder { - _Logger = logger; - connectionString = BuildConnectionString(server, database, userId, password, port, sslMode); - } + Server = _settings.Host, + UserID = _settings.Username, + Password = _settings.Password, + Database = _settings.Database, + Port = (uint)_settings.Port, + SslMode = Enum.Parse(_settings.Sslmode, true), + }; + + return new MySqlConnection(builder.ToString()); + } - private static string BuildConnectionString(string server, string database, string userId, string password, int port, string sslMode) + public async Task PurgeTableRowsAsync() + { + if (Config.GeneralSettings.TablePurgeDays <= 0) + return; + + using (var connection = CreateConnection(Config)) { - MySqlConnectionStringBuilder builder = new MySqlConnectionStringBuilder + await connection.OpenAsync(); + + var parameters = new DynamicParameters(); + parameters.Add("@days", Config.GeneralSettings.TablePurgeDays); + + string query = $@" + DELETE FROM `{Config.DatabaseSettings.TablePrefix}k4times` WHERE `lastseen` < NOW() - INTERVAL @days DAY AND `lastseen` != '0000-00-00'; + DELETE FROM `{Config.DatabaseSettings.TablePrefix}k4stats` WHERE `lastseen` < NOW() - INTERVAL @days DAY AND `lastseen` != '0000-00-00'; + DELETE FROM `{Config.DatabaseSettings.TablePrefix}k4ranks` WHERE `lastseen` < NOW() - INTERVAL @days DAY AND `lastseen` != '0000-00-00'; + "; + + if (Config.GeneralSettings.LevelRanksCompatibility) { - Server = server, - Database = database, - UserID = userId, - Password = password, - Port = (uint)port, - SslMode = Enum.Parse(sslMode, true), - // daffyy's solution to prevent crash from pooling runs out with default values - Pooling = true, - MinimumPoolSize = 0, - MaximumPoolSize = 640, - ConnectionIdleTimeout = 30 - }; + query += $@" + DELETE FROM `{Config.DatabaseSettings.LvLRanksTableName}` WHERE `lastconnect` < UNIX_TIMESTAMP(NOW() - INTERVAL @days DAY); + "; + } - return builder.ConnectionString; + await connection.ExecuteAsync(query, parameters); } + } - public async Task ExecuteNonQueryAsync(string query, params MySqlParameter[] parameters) + public async Task SaveAllPlayersDataAsync() + { + using (var connection = CreateConnection(Config)) { - try + await connection.OpenAsync(); + + using (var transaction = await connection.BeginTransactionAsync()) { - using (MySqlConnection connection = new MySqlConnection(connectionString)) + foreach (K4Player k4player in K4Players) { - await connection.OpenAsync(); - using (MySqlCommand command = new MySqlCommand(query, connection)) + try + { + if (k4player.rankData != null) + await ExecuteRankUpdateAsync(transaction, k4player); + + if (k4player.statData != null) + await ExecuteStatUpdateAsync(transaction, k4player); + + if (k4player.timeData != null) + await ExecuteTimeUpdateAsync(transaction, k4player); + + if (Config.GeneralSettings.LevelRanksCompatibility) + await ExecuteLvlRanksUpdateAsync(transaction, k4player); + + await transaction.CommitAsync(); + } + catch (Exception) { - command.Parameters.AddRange(parameters); - await command.ExecuteNonQueryAsync(); + await transaction.RollbackAsync(); + throw; } } } - catch (Exception ex) + } + } + + public async Task SavePlayerDataAsync(K4Player k4player, bool remove) + { + using (var connection = CreateConnection(Config)) + { + await connection.OpenAsync(); + + using (var transaction = await connection.BeginTransactionAsync()) { - string errorMessage = $"An error occurred while executing SQL query: {query}. Error message: {ex.Message}"; - _Logger?.LogError(ex, errorMessage); - throw; + try + { + if (k4player.rankData != null) + await ExecuteRankUpdateAsync(transaction, k4player); + + if (k4player.statData != null) + await ExecuteStatUpdateAsync(transaction, k4player); + + if (k4player.timeData != null) + await ExecuteTimeUpdateAsync(transaction, k4player); + + if (Config.GeneralSettings.LevelRanksCompatibility) + await ExecuteLvlRanksUpdateAsync(transaction, k4player); + + await transaction.CommitAsync(); + } + catch (Exception) + { + await transaction.RollbackAsync(); + throw; + } } } + if (remove) + K4Players.Remove(k4player); + } + + private async Task ExecuteLvlRanksUpdateAsync(MySqlTransaction transaction, K4Player k4player) + { + if (transaction.Connection == null) + throw new InvalidOperationException("The transaction's connection is null."); + + string query = $@"INSERT INTO `{Config.DatabaseSettings.LvLRanksTableName}` + (`steam`, `name`, `kills`, `deaths`, `shoots`, `hits`, `headshots`, `assists`, `round_win`, `round_lose`, `lastconnect`, `value`, `rank`, `playtime`) + VALUES + (@SteamId, @PlayerName, @Kills, @Deaths, @Shoots, @Hits, @Headshots, @Assists, @RoundWin, @RoundLose, UNIX_TIMESTAMP(), @Points, @Rank, @Playtime) + ON DUPLICATE KEY UPDATE + `name` = @PlayerName, + `kills` = @Kills, + `deaths` = @Deaths, + `shoots` = @Shoots, + `hits` = @Hits, + `headshots` = @Headshots, + `assists` = @Assists, + `round_win` = @RoundWin, + `round_lose` = @RoundLose, + `lastconnect` = UNIX_TIMESTAMP(), + `value` = @Points, + `rank` = @Rank, + `playtime` = @Playtime;"; + + var parameters = new + { + SteamId = k4player.SteamID.ToString().Replace("STEAM_0", "STEAM_1"), + k4player.PlayerName, + Kills = k4player.statData?.StatFields["kills"] ?? 0, + Deaths = k4player.statData?.StatFields["deaths"] ?? 0, + Shoots = k4player.statData?.StatFields["shoots"] ?? 0, + Hits = k4player.statData?.StatFields["hits_given"] ?? 0, + Headshots = k4player.statData?.StatFields["headshots"] ?? 0, + Assists = k4player.statData?.StatFields["assists"] ?? 0, + RoundWin = k4player.statData?.StatFields["round_win"] ?? 0, + RoundLose = k4player.statData?.StatFields["round_lose"] ?? 0, + Points = k4player.rankData?.Points ?? 0, + Rank = k4player.rankData?.Rank?.Id ?? -1, + Playtime = k4player.timeData?.TimeFields["all"] ?? 0 + }; + + await transaction.Connection.ExecuteAsync(query, parameters, transaction); + } + + private async Task ExecuteRankUpdateAsync(MySqlTransaction transaction, K4Player k4player) + { + if (transaction.Connection == null) + throw new InvalidOperationException("The transaction's connection is null."); + + string query = $@"INSERT INTO `{Config.DatabaseSettings.TablePrefix}k4ranks` (`name`, `steam_id`, `rank`, `points`, `lastseen`) + VALUES (@PlayerName, @SteamId, @Rank, @Points, CURRENT_TIMESTAMP) + ON DUPLICATE KEY UPDATE `name` = @PlayerName, `points` = @Points, `lastseen` = CURRENT_TIMESTAMP, `rank` = @Rank;"; + + var parameters = new + { + k4player.PlayerName, + SteamId = k4player.SteamID, + Rank = k4player.rankData!.Rank.Name, + k4player.rankData.Points + }; + + await transaction.Connection.ExecuteAsync(query, parameters, transaction); + } + + private async Task ExecuteStatUpdateAsync(MySqlTransaction transaction, K4Player k4player) + { + if (transaction.Connection == null) + throw new InvalidOperationException("The transaction's connection is null."); + + string fieldsForInsert = string.Join(", ", k4player.statData!.StatFields.Select(f => $"`{f.Key}`")); + string valuesForInsert = string.Join(", ", k4player.statData.StatFields.Keys.Select(f => $"@{f}")); + string onDuplicateKeyUpdate = string.Join(", ", k4player.statData.StatFields.Select(f => $"`{f.Key}` = @{f.Key}")); + + string query = $@"INSERT INTO `{Config.DatabaseSettings.TablePrefix}k4stats` (`name`, `steam_id`, `lastseen`, {fieldsForInsert}) + VALUES (@PlayerName, @SteamId, CURRENT_TIMESTAMP, {valuesForInsert}) + ON DUPLICATE KEY UPDATE `name` = @PlayerName, `lastseen` = CURRENT_TIMESTAMP, {onDuplicateKeyUpdate};"; + + var dynamicParameters = new DynamicParameters(); + dynamicParameters.Add("@PlayerName", k4player.PlayerName); + dynamicParameters.Add("@SteamId", k4player.SteamID); + foreach (var field in k4player.statData.StatFields) + { + dynamicParameters.Add($"@{field.Key}", field.Value); + } + + await transaction.Connection.ExecuteAsync(query, dynamicParameters, transaction); + } + + + private async Task ExecuteTimeUpdateAsync(MySqlTransaction transaction, K4Player k4player) + { + if (transaction.Connection == null) + throw new InvalidOperationException("The transaction's connection is null."); + + string fieldsForInsert = string.Join(", ", k4player.timeData!.TimeFields.Select(f => $"`{f.Key}`")); + string valuesForInsert = string.Join(", ", k4player.timeData.TimeFields.Select(f => $"@{f.Key}")); + string onDuplicateKeyUpdate = string.Join(", ", k4player.timeData.TimeFields.Select(f => $"`{f.Key}` = @{f.Key}")); + + string query = $@"INSERT INTO `{Config.DatabaseSettings.TablePrefix}k4times` (`name`, `steam_id`, `lastseen`, {fieldsForInsert}) + VALUES (@PlayerName, @SteamId, CURRENT_TIMESTAMP, {valuesForInsert}) + ON DUPLICATE KEY UPDATE `name` = @PlayerName, `lastseen` = CURRENT_TIMESTAMP, {onDuplicateKeyUpdate};"; + + var dynamicParameters = new DynamicParameters(); + dynamicParameters.Add("@PlayerName", k4player.PlayerName); + dynamicParameters.Add("@SteamId", k4player.SteamID); + foreach (var field in k4player.timeData.TimeFields) + { + dynamicParameters.Add($"@{field.Key}", field.Value); + } + + await transaction.Connection.ExecuteAsync(query, dynamicParameters, transaction); + } + + public async Task LoadPlayerCacheAsync(K4Player k4player) + { + string combinedQuery = $@" + INSERT INTO `{Config.DatabaseSettings.TablePrefix}k4ranks` (`name`, `steam_id`, `rank`, `points`, `lastseen`) + VALUES ( + @escapedName, + @steamid, + @noneRankName, + @startPoints, + CURRENT_TIMESTAMP + ) + ON DUPLICATE KEY UPDATE + `name` = @escapedName, + `lastseen` = CURRENT_TIMESTAMP; + + INSERT INTO `{Config.DatabaseSettings.TablePrefix}k4stats` (`name`, `steam_id`, `lastseen`, `kills`, `firstblood`, `deaths`, `assists`, `shoots`, `hits_taken`, `hits_given`, `headshots`, `grenades`, `mvp`, `round_win`, `round_lose`, `game_win`, `game_lose`, `rounds_overall`, `rounds_ct`, `rounds_t`, `bomb_planted`, `bomb_defused`, `hostage_rescued`, `hostage_killed`) + VALUES ( + @escapedName, + @steamid, + CURRENT_TIMESTAMP, + ) + ON DUPLICATE KEY UPDATE + `name` = @escapedName, + `lastseen` = CURRENT_TIMESTAMP; + + INSERT INTO `{Config.DatabaseSettings.TablePrefix}k4times` (`name`, `steam_id`, `lastseen`) + VALUES ( + @escapedName, + @steamid, + CURRENT_TIMESTAMP + ) + ON DUPLICATE KEY UPDATE + `name` = @escapedName, + `lastseen` = CURRENT_TIMESTAMP; + + SELECT + r.`points`, + s.`kills`, + s.`firstblood`, + s.`deaths`, + s.`assists`, + s.`shoots`, + s.`hits_taken`, + s.`hits_given`, + s.`headshots`, + s.`grenades`, + s.`mvp`, + s.`round_win`, + s.`round_lose`, + s.`game_win`, + s.`game_lose`, + s.`rounds_overall`, + s.`rounds_ct`, + s.`rounds_t`, + s.`bomb_planted`, + s.`bomb_defused`, + s.`hostage_rescued`, + s.`hostage_killed`, + t.`all`, + t.`ct`, + t.`t`, + t.`spec`, + t.`alive`, + t.`dead` + FROM + `{Config.DatabaseSettings.TablePrefix}k4ranks` AS r + LEFT JOIN + `{Config.DatabaseSettings.TablePrefix}k4stats` AS s ON r.`steam_id` = s.`steam_id` + LEFT JOIN + `{Config.DatabaseSettings.TablePrefix}k4times` AS t ON r.`steam_id` = t.`steam_id` + WHERE + r.`steam_id` = @steamid; + "; + + MySqlParameter[] parameters = + [ + new MySqlParameter("@escapedName", k4player.PlayerName), + new MySqlParameter("@steamid", k4player.SteamID), + new MySqlParameter("@noneRankName", ModuleRank.GetNoneRank()?.Name ?? "none"), + new MySqlParameter("@startPoints", Config.RankSettings.StartPoints) + ]; - public async Task ExecuteScalarAsync(string query, params MySqlParameter[] parameters) + try { - try + using (var connection = CreateConnection(Config)) { - using (MySqlConnection connection = new MySqlConnection(connectionString)) + await connection.OpenAsync(); + var rows = await connection.QueryAsync(combinedQuery, parameters); + + foreach (var row in rows) { - await connection.OpenAsync(); - using (MySqlCommand command = new MySqlCommand(query, connection)) - { - command.Parameters.AddRange(parameters); - return await command.ExecuteScalarAsync(); - } + LoadPlayerRowToCache(k4player, row); } } - catch (Exception ex) - { - string errorMessage = $"An error occurred while executing SQL query (ExecuteScalarAsync): {query}. Error message: {ex.Message}"; - _Logger?.LogError(ex, errorMessage); - throw; - } } + catch (Exception ex) + { + Logger.LogError($"A problem occurred while loading single player cache: {ex.Message}"); + } + } + + private void LoadAllPlayersCache() + { + List players = Utilities.GetPlayers().Where(player => player?.IsValid == true && player.PlayerPawn?.IsValid == true).ToList(); + + if (players.Count == 0) + return; + + string combinedQuery = $@"SELECT + r.`steam_id`, + r.`points`, + s.`kills`, + s.`shoots`, + s.`firstblood`, + s.`deaths`, + s.`hits_given`, + s.`hits_taken`, + s.`headshots`, + s.`grenades`, + s.`mvp`, + s.`round_win`, + s.`round_lose`, + s.`game_win`, + s.`game_lose`, + s.`assists`, + t.`all`, + t.`ct`, + t.`t`, + t.`spec`, + t.`alive`, + t.`dead` + FROM + `{Config.DatabaseSettings.TablePrefix}k4ranks` AS r + LEFT JOIN + `{Config.DatabaseSettings.TablePrefix}k4stats` AS s ON r.`steam_id` = s.`steam_id` + LEFT JOIN + `{Config.DatabaseSettings.TablePrefix}k4times` AS t ON r.`steam_id` = t.`steam_id` + WHERE + r.`steam_id` IN (" + string.Join(",", players.Select(player => $"'{player.SteamID}'")) + ");"; - public async Task ExecuteReaderAsync(string query, params MySqlParameter[] parameters) + try { - try + Task.Run(() => LoadAllPlayersCacheAsync(combinedQuery)); + } + catch (Exception ex) + { + Logger.LogError($"LoadAllPlayersCache > {ex.Message}"); + } + } + + public async Task LoadAllPlayersCacheAsync(string combinedQuery) + { + try + { + using (var connection = CreateConnection(Config)) { - using (MySqlConnection connection = new MySqlConnection(connectionString)) + var players = await connection.QueryAsync(combinedQuery); + + foreach (var k4player in K4Players) { await connection.OpenAsync(); - using (MySqlCommand command = new MySqlCommand(query, connection)) + var rows = await connection.QueryAsync(combinedQuery); + + foreach (var row in rows) { - command.Parameters.AddRange(parameters); - using (MySqlDataReader reader = await command.ExecuteReaderAsync()) - { - DataTable dataTable = new DataTable(); - dataTable.Load(reader); - return dataTable; - } + LoadPlayerRowToCache(k4player, row); } } } - catch (Exception ex) + } + catch (Exception ex) + { + Logger.LogError($"A problem occurred while loading all players cache: {ex.Message}"); + } + } + + public void LoadPlayerRowToCache(K4Player k4player, DataRow row) + { + /** ? Load Rank to Cache */ + RankData? rankData = null; + + if (Config.GeneralSettings.ModuleRanks) + { + int points = Convert.ToInt32(row["points"]); + + rankData = new RankData { - string errorMessage = $"An error occurred while executing SQL query (ExecuteReaderAsync): {query}. Error message: {ex.Message}"; - _Logger?.LogError(ex, errorMessage); - throw; - } + Points = points, + Rank = ModuleRank.GetPlayerRank(points), + PlayedRound = false, + RoundPoints = 0, + HideAdminTag = false, + MuteMessages = false + }; } - public async Task ExecuteWithTransactionAsync(Func executeActions) + /** ? Load Stat to Cache */ + StatData? statData = null; + + if (Config.GeneralSettings.ModuleStats) { - try + Dictionary NewStatFields = new Dictionary(); + + string[] statFieldNames = { "kills", "firstblood", "deaths", "assists", "shoots", "hits_taken", "hits_given", "headshots", "chest_hits", "stomach_hits", "left_arm_hits", "right_arm_hits", "left_leg_hits", "right_leg_hits", "neck_hits", "unused_hits", "gear_hits", "special_hits", "grenades", "mvp", "round_win", "round_lose", "game_win", "game_lose", "rounds_overall", "rounds_ct", "rounds_t", "bomb_planted", "bomb_defused", "hostage_rescued", "hostage_killed", "noscope_kill", "penetrated_kill", "thrusmoke_kill", "flashed_kill", "dominated_kill", "revenge_kill", "assist_flash" }; + + foreach (string statField in statFieldNames) { - using (MySqlConnection connection = new MySqlConnection(connectionString)) - { - await connection.OpenAsync(); - using (MySqlTransaction transaction = await connection.BeginTransactionAsync()) - { - try - { - await executeActions(connection, transaction); - await transaction.CommitAsync(); - } - catch (Exception ex) - { - await transaction.RollbackAsync(); - string errorMessage = $"An error occurred while executing actions within a transaction. Error message: {ex.Message}"; - _Logger?.LogError(ex, errorMessage); - throw; - } - } - } + NewStatFields[statField] = Convert.ToInt32(row[statField]); } - catch (Exception ex) + + statData = new StatData { - string errorMessage = $"An error occurred while setting up a transaction. Error message: {ex.Message}"; - _Logger?.LogError(ex, errorMessage); - throw; + StatFields = NewStatFields, + }; + } + + /** ? Load Time to Cache */ + TimeData? timeData = null; + + if (Config.GeneralSettings.ModuleTimes) + { + Dictionary TimeFields = new Dictionary(); + + string[] timeFieldNames = { "all", "ct", "t", "spec", "alive", "dead" }; + + foreach (string timeField in timeFieldNames) + { + TimeFields[timeField] = Convert.ToInt32(row[timeField]); } + + DateTime now = DateTime.UtcNow; + + timeData = new TimeData + { + TimeFields = TimeFields, + Times = new Dictionary + { + { "Connect", now }, + { "Team", now }, + { "Death", now } + } + }; } + + k4player.rankData = rankData; + k4player.statData = statData; + k4player.timeData = timeData; + + K4Players.Add(k4player); } -} +} \ No newline at end of file diff --git a/K4-System/src/Plugin/PluginManifest.cs b/K4-System/src/Plugin/PluginManifest.cs index 36dc896..1cce980 100644 --- a/K4-System/src/Plugin/PluginManifest.cs +++ b/K4-System/src/Plugin/PluginManifest.cs @@ -10,7 +10,7 @@ public sealed partial class Plugin : BasePlugin public override string ModuleAuthor => "K4ryuu"; - public override string ModuleVersion => "4.0.4 " + + public override string ModuleVersion => "4.1.0 " + #if RELEASE "(release)"; #else diff --git a/K4-System/src/Plugin/PluginStock.cs b/K4-System/src/Plugin/PluginStock.cs index 80d508c..776bdd4 100644 --- a/K4-System/src/Plugin/PluginStock.cs +++ b/K4-System/src/Plugin/PluginStock.cs @@ -1,31 +1,12 @@ namespace K4System { - using System.Reflection; - using MySqlConnector; - - using CounterStrikeSharp.API; using CounterStrikeSharp.API.Core; using CounterStrikeSharp.API.Modules.Utils; - using Microsoft.Extensions.Logging; - - using static K4System.ModuleRank; - using static K4System.ModuleStat; - using static K4System.ModuleTime; using CounterStrikeSharp.API.Modules.Commands; using CounterStrikeSharp.API.Modules.Admin; - using CounterStrikeSharp.API.Modules.Entities; using System.Data; - using MaxMind.GeoIP2; - using MaxMind.GeoIP2.Exceptions; - - public class PlayerData - { - public required string PlayerName { get; set; } - public required string SteamId { get; set; } - public required string lvlSteamId { get; set; } - public required PlayerCacheData cacheData { get; set; } - } + using K4System.Models; public sealed partial class Plugin : BasePlugin { @@ -36,472 +17,13 @@ public CommandInfo.CommandCallback CallbackAnonymizer(Action (f.Name, Value: f.GetValue(null)?.ToString())); + foreach (var (name, value) in chatColors) { - if (modifiedValue.Equals(field.Name, StringComparison.OrdinalIgnoreCase)) - { - modifiedValue = modifiedValue.Replace(field.Name, field.GetValue(null)!.ToString(), StringComparison.OrdinalIgnoreCase); - } + msg = msg.Replace(name, value, StringComparison.OrdinalIgnoreCase); } - return modifiedValue; - } - - public List PreparePlayersData() - { - List playersData = new List(); - List players = Utilities.GetPlayers() - .Where(p => p?.IsValid == true && p.PlayerPawn?.IsValid == true && !p.IsBot && !p.IsHLTV && p.Connected == PlayerConnectedState.PlayerConnected && p.SteamID.ToString().Length == 17 && PlayerCache.Instance.ContainsPlayer(p)) - .ToList(); - - foreach (CCSPlayerController player in players) - { - try - { - SteamID steamId = new SteamID(player.SteamID); - - if (!steamId.IsValid()) - continue; - - string playerSteamId = steamId.SteamId64.ToString(); - - PlayerData data = new PlayerData - { - PlayerName = player.PlayerName, - SteamId = playerSteamId, - lvlSteamId = steamId.SteamId2.Replace("STEAM_0", "STEAM_1"), - cacheData = PlayerCache.Instance.GetPlayerData(player) - }; - - playersData.Add(data); - } - catch (Exception ex) - { - Logger.LogError($"PreparePlayersData > {player.PlayerName} > {ex.Message}"); - } - } - - return playersData; - } - - public void SaveAllPlayersCache() - { - List playersData = PreparePlayersData(); - _ = SaveAllPlayersCacheAsync(playersData); - } - - public async Task SaveAllPlayersCacheAsync(List playersData) - { - await Database.Instance.ExecuteWithTransactionAsync(async (connection, transaction) => - { - foreach (PlayerData playerData in playersData) - { - if (playerData.cacheData.rankData != null) - await ExecuteRankUpdateAsync(playerData.PlayerName, playerData.SteamId, playerData.cacheData.rankData); - - if (playerData.cacheData.statData != null) - await ExecuteStatUpdateAsync(playerData.PlayerName, playerData.SteamId, playerData.cacheData.statData); - - if (playerData.cacheData.timeData != null) - await ExecuteTimeUpdateAsync(playerData.PlayerName, playerData.SteamId, playerData.cacheData.timeData); - - if (Config.GeneralSettings.LevelRanksCompatibility) - await ExecuteLvlRanksUpdateAsync(playerData.PlayerName, playerData.lvlSteamId, playerData.cacheData); - } - }); - } - - public void SavePlayerCache(CCSPlayerController player, bool remove) - { - PlayerCacheData cacheData = PlayerCache.Instance.GetPlayerData(player); - - string playerName = player.PlayerName; - string lvlSteamId = new SteamID(player.SteamID).SteamId2.Replace("STEAM_0", "STEAM_1"); - string playerSteamId = player.SteamID.ToString(); - - _ = SavePlayerDataAsync(playerName, playerSteamId, lvlSteamId, cacheData, remove); - } - - private async Task SavePlayerDataAsync(string playerName, string steamId, string lvlSteamId, PlayerCacheData cacheData, bool remove) - { - await Database.Instance.ExecuteWithTransactionAsync(async (connection, transaction) => - { - if (cacheData.rankData != null) - await ExecuteRankUpdateAsync(playerName, steamId, cacheData.rankData); - - if (cacheData.statData != null) - await ExecuteStatUpdateAsync(playerName, steamId, cacheData.statData); - - if (cacheData.timeData != null) - await ExecuteTimeUpdateAsync(playerName, steamId, cacheData.timeData); - - if (Config.GeneralSettings.LevelRanksCompatibility) - await ExecuteLvlRanksUpdateAsync(playerName, lvlSteamId, cacheData); - }); - - if (remove) - PlayerCache.Instance.RemovePlayer(ulong.Parse(steamId)); - } - - private async Task ExecuteLvlRanksUpdateAsync(string playerName, string lvlSteamId, PlayerCacheData cacheData) - { - string query = $@" - INSERT INTO `{Config.DatabaseSettings.LvLRanksTableName}` - (`steam`, `name`, `kills`, `deaths`, `shoots`, `hits`, `headshots`, `assists`, `round_win`, `round_lose`, `lastconnect`, `value`, `rank`, `playtime`) - VALUES - (@steamid, @playerName, @kills, @deaths, @shoots, @hits, @headshots, @assists, @roundWin, @roundLose, {DateTimeOffset.UtcNow.ToUnixTimeSeconds()}, @points, @rank, @playtime) - ON DUPLICATE KEY UPDATE - `name` = @playerName, - `kills` = @kills, - `deaths` = @deaths, - `shoots` = @shoots, - `hits` = @hits, - `headshots` = @headshots, - `assists` = @assists, - `round_win` = @roundWin, - `round_lose` = @roundLose, - `lastconnect` = {DateTimeOffset.UtcNow.ToUnixTimeSeconds()}, - `value` = @points, - `rank` = @rank, - `playtime` = @playtime; - "; - - MySqlParameter[] parameters = new MySqlParameter[] - { - new MySqlParameter("@playerName", playerName), - new MySqlParameter("@steamId", lvlSteamId), - new MySqlParameter("@kills", cacheData.statData?.StatFields["kills"] ?? 0), - new MySqlParameter("@deaths", cacheData.statData?.StatFields["deaths"] ?? 0), - new MySqlParameter("@shoots", cacheData.statData?.StatFields["shoots"] ?? 0), - new MySqlParameter("@hits", cacheData.statData?.StatFields["hits_given"] ?? 0), - new MySqlParameter("@headshots", cacheData.statData?.StatFields["headshots"] ?? 0), - new MySqlParameter("@assists", cacheData.statData?.StatFields["assists"] ?? 0), - new MySqlParameter("@roundWin", cacheData.statData?.StatFields["round_win"] ?? 0), - new MySqlParameter("@roundLose", cacheData.statData?.StatFields["round_lose"] ?? 0), - new MySqlParameter("@points", cacheData.rankData?.Points ?? 0), - new MySqlParameter("@rank", cacheData.rankData?.Rank?.Id ?? -1), - new MySqlParameter("@playtime", cacheData.timeData?.TimeFields["all"] ?? 0), - }; - - await Database.Instance.ExecuteNonQueryAsync(query, parameters); - } - - private async Task ExecuteRankUpdateAsync(string playerName, string steamId, RankData rankData) - { - string query = $@"INSERT INTO `{Config.DatabaseSettings.TablePrefix}k4ranks` (`name`, `steam_id`, `rank`, `points`, `lastseen`) - VALUES (@playerName, @steamId, @rank, @points, CURRENT_TIMESTAMP) - ON DUPLICATE KEY UPDATE `name` = @playerName, `points` = @points, `lastseen` = CURRENT_TIMESTAMP;"; - - MySqlParameter[] parameters = new MySqlParameter[] - { - new MySqlParameter("@playerName", playerName), - new MySqlParameter("@steamId", steamId), - new MySqlParameter("@rank", rankData.Rank.Name), - new MySqlParameter("@points", rankData.Points) - }; - - await Database.Instance.ExecuteNonQueryAsync(query, parameters); - } - - private async Task ExecuteStatUpdateAsync(string playerName, string steamId, StatData statData) - { - string fieldsForInsert = string.Join(", ", statData.StatFields.Select(f => $"`{f.Key}`")); - string valuesForInsert = string.Join(", ", statData.StatFields.Select(f => $"@{f.Key}")); - string onDuplicateKeyUpdate = string.Join(", ", statData.StatFields.Select(f => $"`{f.Key}` = @{f.Key}")); - - string query = $@"INSERT INTO `{Config.DatabaseSettings.TablePrefix}k4stats` (`name`, `steam_id`, `lastseen`, {fieldsForInsert}) - VALUES (@playerName, @steamId, CURRENT_TIMESTAMP, {valuesForInsert}) - ON DUPLICATE KEY UPDATE `name` = @playerName, `lastseen` = CURRENT_TIMESTAMP, {onDuplicateKeyUpdate};"; - - List parameters = new List - { - new MySqlParameter("@playerName", playerName), - new MySqlParameter("@steamId", steamId) - }; - - parameters.AddRange(statData.StatFields.Select(f => new MySqlParameter($"@{f.Key}", f.Value))); - - await Database.Instance.ExecuteNonQueryAsync(query, parameters.ToArray()); - } - - private async Task ExecuteTimeUpdateAsync(string playerName, string steamId, TimeData timeData) - { - string fieldsForInsert = string.Join(", ", timeData.TimeFields.Select(f => $"`{f.Key}`")); - string valuesForInsert = string.Join(", ", timeData.TimeFields.Select(f => $"@{f.Key}")); - string onDuplicateKeyUpdate = string.Join(", ", timeData.TimeFields.Select(f => $"`{f.Key}` = @{f.Key}")); - - string query = $@"INSERT INTO `{Config.DatabaseSettings.TablePrefix}k4times` (`name`, `steam_id`, `lastseen`, {fieldsForInsert}) - VALUES (@playerName, @steamId, CURRENT_TIMESTAMP, {valuesForInsert}) - ON DUPLICATE KEY UPDATE `name` = @playerName, `lastseen` = CURRENT_TIMESTAMP, {onDuplicateKeyUpdate};"; - - List parameters = new List - { - new MySqlParameter("@playerName", playerName), - new MySqlParameter("@steamId", steamId) - }; - - parameters.AddRange(timeData.TimeFields.Select(f => new MySqlParameter($"@{f.Key}", f.Value))); - - await Database.Instance.ExecuteNonQueryAsync(query, parameters.ToArray()); - } - - private void LoadPlayerCache(CCSPlayerController player) - { - string combinedQuery = $@" - INSERT INTO `{Config.DatabaseSettings.TablePrefix}k4ranks` (`name`, `steam_id`, `rank`, `points`, `lastseen`) - VALUES ( - @escapedName, - @steamid, - @noneRankName, - @startPoints, - CURRENT_TIMESTAMP - ) - ON DUPLICATE KEY UPDATE - `name` = @escapedName, - `lastseen` = CURRENT_TIMESTAMP; - - INSERT INTO `{Config.DatabaseSettings.TablePrefix}k4stats` (`name`, `steam_id`, `lastseen`) - VALUES ( - @escapedName, - @steamid, - CURRENT_TIMESTAMP - ) - ON DUPLICATE KEY UPDATE - `name` = @escapedName, - `lastseen` = CURRENT_TIMESTAMP; - - INSERT INTO `{Config.DatabaseSettings.TablePrefix}k4times` (`name`, `steam_id`, `lastseen`) - VALUES ( - @escapedName, - @steamid, - CURRENT_TIMESTAMP - ) - ON DUPLICATE KEY UPDATE - `name` = @escapedName, - `lastseen` = CURRENT_TIMESTAMP; - - SELECT - r.`points`, - s.`kills`, - s.`shoots`, - s.`firstblood`, - s.`deaths`, - s.`hits_given`, - s.`hits_taken`, - s.`headshots`, - s.`grenades`, - s.`mvp`, - s.`round_win`, - s.`round_lose`, - s.`game_win`, - s.`game_lose`, - s.`assists`, - t.`all`, - t.`ct`, - t.`t`, - t.`spec`, - t.`alive`, - t.`dead` - FROM - `{Config.DatabaseSettings.TablePrefix}k4ranks` AS r - LEFT JOIN - `{Config.DatabaseSettings.TablePrefix}k4stats` AS s ON r.`steam_id` = s.`steam_id` - LEFT JOIN - `{Config.DatabaseSettings.TablePrefix}k4times` AS t ON r.`steam_id` = t.`steam_id` - WHERE - r.`steam_id` = @steamid; - "; - - ulong steamID = player.SteamID; - - MySqlParameter[] parameters = new MySqlParameter[] - { - new MySqlParameter("@escapedName", player.PlayerName), - new MySqlParameter("@steamid", steamID), - new MySqlParameter("@noneRankName", ModuleRank.GetNoneRank()?.Name ?? "none"), - new MySqlParameter("@startPoints", Config.RankSettings.StartPoints) - }; - - _ = LoadPlayerCacheAsync(steamID, combinedQuery, parameters); - } - - public async Task LoadPlayerCacheAsync(ulong steamID, string combinedQuery, MySqlParameter[] parameters) - { - try - { - using (MySqlCommand command = new MySqlCommand(combinedQuery)) - { - DataTable dataTable = await Database.Instance.ExecuteReaderAsync(combinedQuery, parameters); - - if (dataTable.Rows.Count > 0) - { - foreach (DataRow row in dataTable.Rows) - { - LoadPlayerRowToCache(steamID, row); - } - } - } - } - catch (Exception ex) - { - Logger.LogError($"A problem occurred while loading single player cache: {ex.Message}"); - } - } - - private void LoadAllPlayersCache() - { - List players = Utilities.GetPlayers().Where(player => player?.IsValid == true && player.PlayerPawn?.IsValid == true && !player.IsBot && !player.IsHLTV && player.SteamID.ToString().Length == 17).ToList(); - - if (players.Count == 0) - return; - - string combinedQuery = $@"SELECT - r.`steam_id`, - r.`points`, - s.`kills`, - s.`shoots`, - s.`firstblood`, - s.`deaths`, - s.`hits_given`, - s.`hits_taken`, - s.`headshots`, - s.`grenades`, - s.`mvp`, - s.`round_win`, - s.`round_lose`, - s.`game_win`, - s.`game_lose`, - s.`assists`, - t.`all`, - t.`ct`, - t.`t`, - t.`spec`, - t.`alive`, - t.`dead` - FROM - `{Config.DatabaseSettings.TablePrefix}k4ranks` AS r - LEFT JOIN - `{Config.DatabaseSettings.TablePrefix}k4stats` AS s ON r.`steam_id` = s.`steam_id` - LEFT JOIN - `{Config.DatabaseSettings.TablePrefix}k4times` AS t ON r.`steam_id` = t.`steam_id` - WHERE - r.`steam_id` IN (" + string.Join(",", players.Select(player => $"'{player.SteamID}'")) + ");"; - - try - { - _ = LoadAllPlayersCacheAsync(combinedQuery); - } - catch (Exception ex) - { - Logger.LogError($"LoadAllPlayersCache > {ex.Message}"); - } - } - - public async Task LoadAllPlayersCacheAsync(string combinedQuery) - { - try - { - using (MySqlCommand command = new MySqlCommand(combinedQuery)) - { - DataTable dataTable = await Database.Instance.ExecuteReaderAsync(command.CommandText); - - if (dataTable.Rows.Count > 0) - { - foreach (DataRow row in dataTable.Rows) - { - ulong steamID = Convert.ToUInt64(row["steam_id"]); - - if (steamID == 0) - continue; - - LoadPlayerRowToCache(steamID, row); - } - } - } - } - catch (Exception ex) - { - Logger.LogError($"A problem occurred while loading all players cache: {ex.Message}"); - } - } - - public void LoadPlayerRowToCache(ulong steamID, DataRow row) - { - /** ? Load Rank to Cache */ - RankData? rankData = null; - - if (Config.GeneralSettings.ModuleRanks) - { - int points = Convert.ToInt32(row["points"]); - - rankData = new RankData - { - Points = points, - Rank = ModuleRank.GetPlayerRank(points), - PlayedRound = false, - RoundPoints = 0, - HideAdminTag = false, - MuteMessages = false - }; - } - - /** ? Load Stat to Cache */ - StatData? statData = null; - - if (Config.GeneralSettings.ModuleStats) - { - Dictionary NewStatFields = new Dictionary(); - - string[] statFieldNames = { "kills", "shoots", "firstblood", "deaths", "hits_given", "hits_taken", "headshots", "grenades", "mvp", "round_win", "round_lose", "game_win", "game_lose", "assists" }; - - foreach (string statField in statFieldNames) - { - NewStatFields[statField] = Convert.ToInt32(row[statField]); - } - - statData = new StatData - { - StatFields = NewStatFields, - }; - } - - /** ? Load Time to Cache */ - TimeData? timeData = null; - - if (Config.GeneralSettings.ModuleTimes) - { - Dictionary TimeFields = new Dictionary(); - - string[] timeFieldNames = { "all", "ct", "t", "spec", "alive", "dead" }; - - foreach (string timeField in timeFieldNames) - { - TimeFields[timeField] = Convert.ToInt32(row[timeField]); - } - - DateTime now = DateTime.UtcNow; - - timeData = new TimeData - { - TimeFields = TimeFields, - Times = new Dictionary - { - { "Connect", now }, - { "Team", now }, - { "Death", now } - } - }; - } - - PlayerCache.Instance.AddOrUpdatePlayer(steamID, new PlayerCacheData - { - rankData = rankData, - statData = statData, - timeData = timeData - }); + return msg; } public bool CommandHelper(CCSPlayerController? player, CommandInfo info, CommandUsage usage, int argCount = 0, string? help = null, string? permission = null) @@ -550,70 +72,14 @@ public bool CommandHelper(CCSPlayerController? player, CommandInfo info, Command return true; } - public async Task PurgeTableRows() + public K4Player? GetK4Player(ulong steamID) { - if (Config.GeneralSettings.TablePurgeDays <= 0) - return; - - await Database.Instance.ExecuteWithTransactionAsync(async (connection, transaction) => - { - MySqlParameter[] parameters = new MySqlParameter[] - { - new MySqlParameter("@days", Config.GeneralSettings.TablePurgeDays) - }; - - string query = $@"DELETE FROM `{this.Config.DatabaseSettings.TablePrefix}k4times` WHERE `lastseen` < NOW() - INTERVAL @days DAY;"; - await Database.Instance.ExecuteNonQueryAsync(query, parameters); - - query = $@"DELETE FROM `{this.Config.DatabaseSettings.TablePrefix}k4stats` WHERE `lastseen` < NOW() - INTERVAL @days DAY;"; - await Database.Instance.ExecuteNonQueryAsync(query, parameters); - - query = $@"DELETE FROM `{this.Config.DatabaseSettings.TablePrefix}k4ranks` WHERE `lastseen` < NOW() - INTERVAL @days DAY;"; - await Database.Instance.ExecuteNonQueryAsync(query, parameters); - - if (Config.GeneralSettings.LevelRanksCompatibility) - { - query = $@"DELETE FROM `{Config.DatabaseSettings.LvLRanksTableName}` WHERE `lastconnect` < UNIX_TIMESTAMP(NOW() - INTERVAL @days DAY);"; - await Database.Instance.ExecuteNonQueryAsync(query, parameters); - } - }); + return K4Players.FirstOrDefault(player => player.SteamID == steamID); } - public string GetPlayerCountryCode(CCSPlayerController player) + public K4Player? GetK4Player(CCSPlayerController playerController) { - string? playerIp = player.IpAddress; - - if (playerIp == null) - return "??"; - - string[] parts = playerIp.Split(':'); - string realIP = parts.Length == 2 ? parts[0] : playerIp; - - string filePath = Path.Combine(ModuleDirectory, "GeoLite2-Country.mmdb"); - if (!File.Exists(filePath)) - { - Logger.LogError($"GeoLite2-Country.mmdb not found in {ModuleDirectory}. Download it from https://github.com/P3TERX/GeoLite.mmdb/releases and place it in the same directory as the plugin."); - return "??"; - } - - using DatabaseReader reader = new DatabaseReader(Path.Combine(ModuleDirectory, "GeoLite2-Country.mmdb")); - { - try - { - MaxMind.GeoIP2.Responses.CountryResponse response = reader.Country(realIP); - return response.Country.IsoCode ?? "??"; - } - catch (AddressNotFoundException) - { - Console.WriteLine($"The address {realIP} is not in the database."); - return "??"; - } - catch (GeoIP2Exception ex) - { - Console.WriteLine($"Error: {ex.Message}"); - return "??"; - } - } + return K4Players.FirstOrDefault(player => player.Controller == playerController); } } } \ No newline at end of file diff --git a/K4-System/src/lang/en.json b/K4-System/src/lang/en.json index 30ea2d9..06ba57d 100644 --- a/K4-System/src/lang/en.json +++ b/K4-System/src/lang/en.json @@ -27,11 +27,11 @@ "k4.ranks.rank.line2": "--- {silver}Next rank: {0}{1}", "k4.ranks.rank.line3": "--- {silver}Points until next rank: {lime}{0}", "k4.ranks.rank.line4": "--- {silver}Place in top list: {lime}{0} out of {1}", - "k4.ranks.resetmyrank": "{lime}{0} {silver}has reset their rank and points.", + "k4.ranks.resetmydata": "{lime}{0} {silver}has reset their data.", "k4.ranks.top.title": "Top {lime}{0} {silver}Players", "k4.ranks.top.line": "{gold}{0}. {1}[{2}] {gold}{3} - {blue}{4} points", "k4.ranks.top.notfound": "{lightred}No players found in the top {0}.", - "k4.ranks.resetrank": "{lime}{0}{silver}'s rank and points have been reset by {lime}{1}{silver}.", + "k4.ranks.resetdata": "{lime}{0}{silver}'s data have been reset by {lime}{1}{silver}.", "k4.ranks.setpoints": "{lime}{0}{silver}'s points have been set to {lime}{1} {silver}by {lime}{2}{silver}.", "k4.ranks.givepoints": "{lime}{0} {silver}has given {lime}{1} {silver}points to {lime}{2}{silver}.", "k4.ranks.removepoints": "{lime}{0} {silver}has taken {lime}{1} {silver}points from {lime}{2}{silver}.", diff --git a/K4-System/src/lang/fr.json b/K4-System/src/lang/fr.json.outdated similarity index 100% rename from K4-System/src/lang/fr.json rename to K4-System/src/lang/fr.json.outdated diff --git a/K4-System/src/lang/ro.json b/K4-System/src/lang/ro.json.outdated similarity index 100% rename from K4-System/src/lang/ro.json rename to K4-System/src/lang/ro.json.outdated diff --git a/README.md b/README.md index c3eef31..234871b 100644 --- a/README.md +++ b/README.md @@ -76,6 +76,7 @@ Your support keeps my creative engine running and allows me to share knowledge w You can get these addons to make K4 and your server gameplay much better: - [**K4-Missions**](https://www.buymeacoffee.com/k4ryuu/e/212956): This masterpiece lets you create your own missions to your server, through a really easy json file (5 line per mission). You don't have to configure big codes or so, just easily add few words and the mission is generated automatically. Normal players have less mission slots than VIPs so it's good to entertain your players and get more rank purchase possibly. The code is really really complex for the basic level of CSS scripts, because the hooks are made "dynamically" to support everything. +- - [**K4-System Matchmaking Ranks**](https://github.com/K4ryuu/K4-System-MMRanks): Display K4-System points as prime points or matchmaking ranks on scoreboard. Probably you have to install a metamod addon in order to reveal all player's rank, but you find that in the issues of that plugin.

(back to top)

@@ -116,15 +117,6 @@ If you need any help or information about settings, installation, api, update or ## Roadmap -- [x] Complete remake with modular dependency injection -- [x] Translations -- [x] Detailed Ranks Lists -- [x] Upload to CSS forum -- [x] Create FAQ -- [x] Permission based ranks module -- [ ] Promote/Demote colored alert messages -- [ ] Weapon based points -- [ ] Plugin auto updater (can be disabled, download stable branch, not dev) -- [ ] Top 10 special tags +- [ ] Nothing planned yet

(back to top)