From 2852623936e10c31a385192ec4b1c43871aa4717 Mon Sep 17 00:00:00 2001 From: originalaidn <45371311+originalaidn@users.noreply.github.com> Date: Fri, 18 Oct 2024 23:00:07 +0200 Subject: [PATCH 1/7] added pins and stattrak pins only from website / database: no command now stattrak enabling only from website / database: no command now --- Commands.cs | 4 +++ Config.cs | 3 +++ Events.cs | 51 +++++++++++++++++++++++++++++++++++ Utility.cs | 9 +++++++ Variables.cs | 1 + WeaponAction.cs | 33 +++++++++++++++++++++++ WeaponInfo.cs | 2 ++ WeaponPaints.cs | 1 + WeaponSynchronization.cs | 58 ++++++++++++++++++++++++++++++++++++++++ 9 files changed, 162 insertions(+) diff --git a/Commands.cs b/Commands.cs index ca1e5c67..47c2a8fc 100644 --- a/Commands.cs +++ b/Commands.cs @@ -37,6 +37,10 @@ namespace WeaponPaints GivePlayerGloves(player); RefreshWeapons(player); + GivePlayerAgent(player); + GivePlayerMusicKit(player); + AddTimer(0.1f, () => GivePlayerPin(player)); + AddTimer(0.15f, () => GivePlayerPin(player)); } if (!string.IsNullOrEmpty(Localizer["wp_command_refresh_done"])) diff --git a/Config.cs b/Config.cs index 752c7c0f..a5ad70a3 100644 --- a/Config.cs +++ b/Config.cs @@ -20,6 +20,9 @@ namespace WeaponPaints [JsonPropertyName("SkinEnabled")] public bool SkinEnabled { get; set; } = true; + [JsonPropertyName("PinsEnabled")] + public bool PinsEnabled { get; set; } = true; + [JsonPropertyName("CommandWpEnabled")] public bool CommandWpEnabled { get; set; } = true; diff --git a/Events.cs b/Events.cs index 0da98ea6..049e154a 100644 --- a/Events.cs +++ b/Events.cs @@ -88,6 +88,10 @@ namespace WeaponPaints { GPlayersMusic.TryRemove(player.Slot, out _); } + if (Config.Additional.PinsEnabled) + { + GPlayersPin.TryRemove(player.Slot, out _); + } _temporaryPlayerWeaponWear.TryRemove(player.Slot, out _); @@ -231,6 +235,52 @@ namespace WeaponPaints return HookResult.Continue; } + private HookResult OnPlayerDeath(EventPlayerDeath @event, GameEventInfo info) + { + CCSPlayerController? player = @event.Attacker; + + if (player is null || !player.IsValid) + return HookResult.Continue; + + if (!GPlayerWeaponsInfo.TryGetValue(player.Slot, out _)) return HookResult.Continue; + + CBasePlayerWeapon? weapon = player.PlayerPawn.Value?.WeaponServices?.ActiveWeapon.Value; + + if (weapon == null) return HookResult.Continue; + + int weaponDefIndex = weapon.AttributeManager.Item.ItemDefinitionIndex; + + if (!GPlayerWeaponsInfo[player.Slot].TryGetValue(weaponDefIndex, out var value) || value.Paint == 0) return HookResult.Continue; + + var weaponInfo = value; + + if (weaponInfo.StatTrak) + { + weaponInfo.StatTrakCount += 1; + + var playerInfo = new PlayerInfo + { + UserId = player.UserId, + Slot = player.Slot, + Index = (int)player.Index, + SteamId = player.SteamID.ToString(), + Name = player.PlayerName, + IpAddress = player.IpAddress?.Split(":")[0] + }; + + CAttributeListSetOrAddAttributeValueByName.Invoke(weapon.AttributeManager.Item.NetworkedDynamicAttributes.Handle, "kill eater", ViewAsFloatKillStreak(weaponInfo.StatTrakCount)); + CAttributeListSetOrAddAttributeValueByName.Invoke(weapon.AttributeManager.Item.NetworkedDynamicAttributes.Handle, "kill eater score type", 0); + + CAttributeListSetOrAddAttributeValueByName.Invoke(weapon.AttributeManager.Item.AttributeList.Handle, "kill eater", ViewAsFloatKillStreak(weaponInfo.StatTrakCount)); + CAttributeListSetOrAddAttributeValueByName.Invoke(weapon.AttributeManager.Item.AttributeList.Handle, "kill eater score type", 0); + + if (WeaponSync != null) + _ = Task.Run(async () => await WeaponSync.SyncStatTrakToDatabase(playerInfo, weaponInfo.StatTrakCount, weaponDefIndex)); + } + + return HookResult.Continue; + } + private void RegisterListeners() { RegisterListener(OnMapStart); @@ -239,6 +289,7 @@ namespace WeaponPaints RegisterEventHandler(OnRoundStart); RegisterEventHandler(OnRoundEnd); RegisterListener(OnEntityCreated); + RegisterEventHandler(OnPlayerDeath); if (Config.Additional.ShowSkinImage) RegisterListener(OnTick); diff --git a/Utility.cs b/Utility.cs index c66b4a6b..0a59459b 100644 --- a/Utility.cs +++ b/Utility.cs @@ -33,6 +33,8 @@ namespace WeaponPaints `weapon_wear` float NOT NULL DEFAULT 0.000001, `weapon_seed` int(16) NOT NULL DEFAULT 0, `weapon_nametag` VARCHAR(128) DEFAULT NULL, + `weapon_stattrak` tinyint(1) NOT NULL, + `weapon_stattrak_count` int(10) NOT NULL, `weapon_sticker_0` VARCHAR(128) NOT NULL DEFAULT '0;0;0;0;0;0;0' COMMENT 'id;schema;x;y;wear;scale;rotation', `weapon_sticker_1` VARCHAR(128) NOT NULL DEFAULT '0;0;0;0;0;0;0' COMMENT 'id;schema;x;y;wear;scale;rotation', `weapon_sticker_2` VARCHAR(128) NOT NULL DEFAULT '0;0;0;0;0;0;0' COMMENT 'id;schema;x;y;wear;scale;rotation', @@ -68,6 +70,13 @@ namespace WeaponPaints UNIQUE (`steamid`) ) ENGINE=InnoDB """, + """ + CREATE TABLE IF NOT EXISTS `wp_player_pins` ( + `steamid` varchar(64) NOT NULL, + `id` int(11) NOT NULL, + UNIQUE (`steamid`) + ) ENGINE=InnoDB + """, ]; foreach (var query in createTableQueries) diff --git a/Variables.cs b/Variables.cs index 27422bf5..7df322a1 100644 --- a/Variables.cs +++ b/Variables.cs @@ -75,6 +75,7 @@ public partial class WeaponPaints internal static readonly ConcurrentDictionary GPlayersKnife = new(); internal static readonly ConcurrentDictionary GPlayersGlove = new(); internal static readonly ConcurrentDictionary GPlayersMusic = new(); + internal static readonly ConcurrentDictionary GPlayersPin = new(); public static readonly ConcurrentDictionary GPlayersAgent = new(); internal static readonly ConcurrentDictionary> GPlayerWeaponsInfo = new(); internal static List SkinsList = []; diff --git a/WeaponAction.cs b/WeaponAction.cs index 369fd69d..d471fe00 100644 --- a/WeaponAction.cs +++ b/WeaponAction.cs @@ -87,6 +87,15 @@ namespace WeaponPaints weapon.FallbackWear = weaponInfo.Wear; CAttributeListSetOrAddAttributeValueByName.Invoke(weapon.AttributeManager.Item.NetworkedDynamicAttributes.Handle, "set item texture prefab", weapon.FallbackPaintKit); + if (weaponInfo.StatTrak) + { + CAttributeListSetOrAddAttributeValueByName.Invoke(weapon.AttributeManager.Item.NetworkedDynamicAttributes.Handle, "kill eater", ViewAsFloatKillStreak(weaponInfo.StatTrakCount)); + CAttributeListSetOrAddAttributeValueByName.Invoke(weapon.AttributeManager.Item.NetworkedDynamicAttributes.Handle, "kill eater score type", 0); + + CAttributeListSetOrAddAttributeValueByName.Invoke(weapon.AttributeManager.Item.AttributeList.Handle, "kill eater", ViewAsFloatKillStreak(weaponInfo.StatTrakCount)); + CAttributeListSetOrAddAttributeValueByName.Invoke(weapon.AttributeManager.Item.AttributeList.Handle, "kill eater score type", 0); + } + fallbackPaintKit = weapon.FallbackPaintKit; if (fallbackPaintKit == 0) @@ -451,6 +460,19 @@ namespace WeaponPaints player.MusicKitID = value; Utilities.SetStateChanged(player, "CCSPlayerController", "m_iMusicKitID"); } + + private static void GivePlayerPin(CCSPlayerController player) + { + if (!GPlayersPin.TryGetValue(player.Slot, out var pin)) return; + + if (player.InventoryServices == null) return; + + for (var index = 0; index < player.InventoryServices.Rank.Length; index++) + { + player.InventoryServices.Rank[index] = index == 5 ? (MedalRank_t)pin : MedalRank_t.MEDAL_RANK_NONE; + Utilities.SetStateChanged(player, "CCSPlayerController", "m_pInventoryServices"); + } + } private void GiveOnItemPickup(CCSPlayerController player) { @@ -514,5 +536,16 @@ namespace WeaponPaints { return BitConverter.Int32BitsToSingle((int)value); } + + public float ViewAsFloatKillStreak(T value) where T : struct + { + byte[] bytes = value switch + { + int intValue => BitConverter.GetBytes(intValue), + uint uintValue => BitConverter.GetBytes(uintValue), + _ => throw new ArgumentException("Unsupported type") + }; + return BitConverter.ToSingle(bytes, 0); + } } } \ No newline at end of file diff --git a/WeaponInfo.cs b/WeaponInfo.cs index ffdabfd7..7f89ebb3 100644 --- a/WeaponInfo.cs +++ b/WeaponInfo.cs @@ -6,6 +6,8 @@ public int Seed { get; set; } = 0; public float Wear { get; set; } = 0f; public string Nametag { get; set; } = ""; + public bool StatTrak { get; set; } = false; + public int StatTrakCount { get; set; } = 0; public KeyChainInfo? KeyChain { get; set; } public List Stickers { get; set; } = new List(); } diff --git a/WeaponPaints.cs b/WeaponPaints.cs index 544bdf3a..f9e75230 100644 --- a/WeaponPaints.cs +++ b/WeaponPaints.cs @@ -36,6 +36,7 @@ public partial class WeaponPaints : BasePlugin, IPluginConfig(query, new { steamid = player.SteamId }); + + if (pinData != null) + { + WeaponPaints.GPlayersPin[player.Slot] = pinData.Value; + } + } + catch (Exception ex) + { + Utility.Log($"An error occurred in GetPinsFromDatabase: {ex.Message}"); + } + } + internal async Task SyncKnifeToDatabase(PlayerInfo player, string knife) { if (!_config.Additional.KnifeEnabled || string.IsNullOrEmpty(player.SteamId) || string.IsNullOrEmpty(knife)) return; @@ -349,5 +372,40 @@ namespace WeaponPaints Utility.Log($"Error syncing music kit to database: {e.Message}"); } } + + internal async Task SyncStatTrakToDatabase(PlayerInfo player, int StatTrakCount, int defindex) + { + if (string.IsNullOrEmpty(player.SteamId) || !WeaponPaints.GPlayerWeaponsInfo.TryGetValue(player.Slot, out var weaponsInfo)) + return; + + try + { + await using var connection = await _database.GetConnectionAsync(); + + const string queryCheckExistence = "SELECT COUNT(*) FROM `wp_player_skins` WHERE `steamid` = @steamid AND `weapon_defindex` = @weaponDefIndex"; + + var existingRecordCount = await connection.ExecuteScalarAsync(queryCheckExistence, new { steamid = player.SteamId, weaponDefIndex = defindex }); + + string query = string.Empty; + object? parameters = null; + + if (existingRecordCount > 0) + { + query = "UPDATE `wp_player_skins` SET `weapon_stattrak_count` = @StatTrakCount WHERE `steamid` = @steamid AND `weapon_defindex` = @weaponDefIndex"; + parameters = new { steamid = player.SteamId, weaponDefIndex = defindex, StatTrakCount }; + } + else + { + query = "INSERT INTO `wp_player_skins` (`steamid`, `weapon_defindex`, `weapon_stattrak_count`) VALUES (@steamid, @weaponDefIndex, @StatTrakCount)"; + parameters = new { steamid = player.SteamId, weaponDefIndex = defindex, StatTrakCount }; + } + + await connection.ExecuteAsync(query, parameters); + } + catch (Exception e) + { + Utility.Log($"Error syncing weapon paints to database: {e.Message}"); + } + } } } \ No newline at end of file From 8b4d9dc676aaf754d7ae835e6933a1c05cc7ee7c Mon Sep 17 00:00:00 2001 From: originalaidn <45371311+originalaidn@users.noreply.github.com> Date: Fri, 18 Oct 2024 23:02:18 +0200 Subject: [PATCH 2/7] Update Commands.cs --- Commands.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/Commands.cs b/Commands.cs index 47c2a8fc..fcf7ebed 100644 --- a/Commands.cs +++ b/Commands.cs @@ -39,7 +39,6 @@ namespace WeaponPaints RefreshWeapons(player); GivePlayerAgent(player); GivePlayerMusicKit(player); - AddTimer(0.1f, () => GivePlayerPin(player)); AddTimer(0.15f, () => GivePlayerPin(player)); } From 179eb41e47bd68d19a5e16ff58fe451009812ead Mon Sep 17 00:00:00 2001 From: originalaidn <45371311+originalaidn@users.noreply.github.com> Date: Fri, 18 Oct 2024 23:12:47 +0200 Subject: [PATCH 3/7] removed ViewAsFloatKillStreak & added killstreak sync to disconnect --- Events.cs | 38 +++++++++++++++++++++++++++++++++----- WeaponAction.cs | 15 ++------------- 2 files changed, 35 insertions(+), 18 deletions(-) diff --git a/Events.cs b/Events.cs index 049e154a..0b090149 100644 --- a/Events.cs +++ b/Events.cs @@ -97,6 +97,37 @@ namespace WeaponPaints CommandsCooldown.Remove(player.Slot); + var playerInfo = new PlayerInfo + { + UserId = player.UserId, + Slot = player.Slot, + Index = (int)player.Index, + SteamId = player.SteamID.ToString(), + Name = player.PlayerName, + IpAddress = player.IpAddress?.Split(":")[0] + }; + + if (!GPlayerWeaponsInfo.TryGetValue(player.Slot, out var weaponInfos)) + return HookResult.Continue; + + foreach (var weapon in weaponInfos) + { + var weaponDefIndex = weapon.Key; + var weaponInfo = weapon.Value; + + if (weaponInfo.Paint == 0) + continue; + + if (weaponInfo.StatTrak) + { + if (WeaponSync != null) + { + + _ = Task.Run(async () => await WeaponSync.SyncStatTrakToDatabase(playerInfo, weaponInfo.StatTrakCount, weaponDefIndex)); + } + } + } + return HookResult.Continue; } @@ -268,14 +299,11 @@ namespace WeaponPaints IpAddress = player.IpAddress?.Split(":")[0] }; - CAttributeListSetOrAddAttributeValueByName.Invoke(weapon.AttributeManager.Item.NetworkedDynamicAttributes.Handle, "kill eater", ViewAsFloatKillStreak(weaponInfo.StatTrakCount)); + CAttributeListSetOrAddAttributeValueByName.Invoke(weapon.AttributeManager.Item.NetworkedDynamicAttributes.Handle, "kill eater", ViewAsFloat((uint)weaponInfo.StatTrakCount)); CAttributeListSetOrAddAttributeValueByName.Invoke(weapon.AttributeManager.Item.NetworkedDynamicAttributes.Handle, "kill eater score type", 0); - CAttributeListSetOrAddAttributeValueByName.Invoke(weapon.AttributeManager.Item.AttributeList.Handle, "kill eater", ViewAsFloatKillStreak(weaponInfo.StatTrakCount)); + CAttributeListSetOrAddAttributeValueByName.Invoke(weapon.AttributeManager.Item.AttributeList.Handle, "kill eater", ViewAsFloat((uint)weaponInfo.StatTrakCount)); CAttributeListSetOrAddAttributeValueByName.Invoke(weapon.AttributeManager.Item.AttributeList.Handle, "kill eater score type", 0); - - if (WeaponSync != null) - _ = Task.Run(async () => await WeaponSync.SyncStatTrakToDatabase(playerInfo, weaponInfo.StatTrakCount, weaponDefIndex)); } return HookResult.Continue; diff --git a/WeaponAction.cs b/WeaponAction.cs index d471fe00..5f13c1b2 100644 --- a/WeaponAction.cs +++ b/WeaponAction.cs @@ -89,10 +89,10 @@ namespace WeaponPaints if (weaponInfo.StatTrak) { - CAttributeListSetOrAddAttributeValueByName.Invoke(weapon.AttributeManager.Item.NetworkedDynamicAttributes.Handle, "kill eater", ViewAsFloatKillStreak(weaponInfo.StatTrakCount)); + CAttributeListSetOrAddAttributeValueByName.Invoke(weapon.AttributeManager.Item.NetworkedDynamicAttributes.Handle, "kill eater", ViewAsFloat((uint)weaponInfo.StatTrakCount)); CAttributeListSetOrAddAttributeValueByName.Invoke(weapon.AttributeManager.Item.NetworkedDynamicAttributes.Handle, "kill eater score type", 0); - CAttributeListSetOrAddAttributeValueByName.Invoke(weapon.AttributeManager.Item.AttributeList.Handle, "kill eater", ViewAsFloatKillStreak(weaponInfo.StatTrakCount)); + CAttributeListSetOrAddAttributeValueByName.Invoke(weapon.AttributeManager.Item.AttributeList.Handle, "kill eater", ViewAsFloat((uint)weaponInfo.StatTrakCount)); CAttributeListSetOrAddAttributeValueByName.Invoke(weapon.AttributeManager.Item.AttributeList.Handle, "kill eater score type", 0); } @@ -536,16 +536,5 @@ namespace WeaponPaints { return BitConverter.Int32BitsToSingle((int)value); } - - public float ViewAsFloatKillStreak(T value) where T : struct - { - byte[] bytes = value switch - { - int intValue => BitConverter.GetBytes(intValue), - uint uintValue => BitConverter.GetBytes(uintValue), - _ => throw new ArgumentException("Unsupported type") - }; - return BitConverter.ToSingle(bytes, 0); - } } } \ No newline at end of file From a9fb5b241d89129d6d227508540e31930000215f Mon Sep 17 00:00:00 2001 From: originalaidn <45371311+originalaidn@users.noreply.github.com> Date: Fri, 18 Oct 2024 23:13:51 +0200 Subject: [PATCH 4/7] tab --- Events.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Events.cs b/Events.cs index 0b090149..b7fe7e5f 100644 --- a/Events.cs +++ b/Events.cs @@ -108,7 +108,7 @@ namespace WeaponPaints }; if (!GPlayerWeaponsInfo.TryGetValue(player.Slot, out var weaponInfos)) - return HookResult.Continue; + return HookResult.Continue; foreach (var weapon in weaponInfos) { From 7172dc344f5a8b948d98d43add6282437e981451 Mon Sep 17 00:00:00 2001 From: originalaidn <45371311+originalaidn@users.noreply.github.com> Date: Fri, 18 Oct 2024 23:15:56 +0200 Subject: [PATCH 5/7] not needed this check --- Events.cs | 3 --- 1 file changed, 3 deletions(-) diff --git a/Events.cs b/Events.cs index b7fe7e5f..d5132ed6 100644 --- a/Events.cs +++ b/Events.cs @@ -115,9 +115,6 @@ namespace WeaponPaints var weaponDefIndex = weapon.Key; var weaponInfo = weapon.Value; - if (weaponInfo.Paint == 0) - continue; - if (weaponInfo.StatTrak) { if (WeaponSync != null) From c43f4bde80a204b0dc6716530b2f4769c1637533 Mon Sep 17 00:00:00 2001 From: originalaidn <45371311+originalaidn@users.noreply.github.com> Date: Fri, 18 Oct 2024 23:25:37 +0200 Subject: [PATCH 6/7] using transaction to sync killstreaks --- Events.cs | 64 ++++++++++++++++++++++------------------ WeaponSynchronization.cs | 37 ++++++++++++----------- 2 files changed, 56 insertions(+), 45 deletions(-) diff --git a/Events.cs b/Events.cs index d5132ed6..97513753 100644 --- a/Events.cs +++ b/Events.cs @@ -5,6 +5,7 @@ using CounterStrikeSharp.API.Modules.Entities; using CounterStrikeSharp.API.Modules.Memory; using CounterStrikeSharp.API.Modules.Memory.DynamicFunctions; using System.Runtime.InteropServices; +using System.Collections.Concurrent; namespace WeaponPaints { @@ -68,6 +69,21 @@ namespace WeaponPaints if (player is null || !player.IsValid || player.IsBot) return HookResult.Continue; + var playerInfo = new PlayerInfo + { + UserId = player.UserId, + Slot = player.Slot, + Index = (int)player.Index, + SteamId = player.SteamID.ToString(), + Name = player.PlayerName, + IpAddress = player.IpAddress?.Split(":")[0] + }; + + if (!GPlayerWeaponsInfo.TryGetValue(player.Slot, out var weaponInfos)) + return HookResult.Continue; + + _ = Task.Run(async () => await SyncStatTrakForPlayer(playerInfo, weaponInfos)); + if (Config.Additional.SkinEnabled) { GPlayerWeaponsInfo.TryRemove(player.Slot, out _); @@ -97,36 +113,28 @@ namespace WeaponPaints CommandsCooldown.Remove(player.Slot); - var playerInfo = new PlayerInfo - { - UserId = player.UserId, - Slot = player.Slot, - Index = (int)player.Index, - SteamId = player.SteamID.ToString(), - Name = player.PlayerName, - IpAddress = player.IpAddress?.Split(":")[0] - }; - - if (!GPlayerWeaponsInfo.TryGetValue(player.Slot, out var weaponInfos)) - return HookResult.Continue; - - foreach (var weapon in weaponInfos) - { - var weaponDefIndex = weapon.Key; - var weaponInfo = weapon.Value; - - if (weaponInfo.StatTrak) - { - if (WeaponSync != null) - { - - _ = Task.Run(async () => await WeaponSync.SyncStatTrakToDatabase(playerInfo, weaponInfo.StatTrakCount, weaponDefIndex)); - } - } - } - return HookResult.Continue; } + + public async Task SyncStatTrakForPlayer(PlayerInfo playerInfo, ConcurrentDictionary weaponInfos) + { + if (WeaponSync == null || weaponInfos == null || weaponInfos.Count == 0) return; + + var statTrakWeapons = weaponInfos + .Where(w => w.Value.StatTrak) + .ToDictionary(w => w.Key, w => w.Value.StatTrakCount); + + if (statTrakWeapons.Count == 0) return; + + try + { + await WeaponSync.SyncStatTrakToDatabase(playerInfo, statTrakWeapons); + } + catch (Exception ex) + { + Utility.Log($"Error syncing StatTrak for player {playerInfo.SteamId}: {ex.Message}"); + } + } private void OnMapStart(string mapName) { diff --git a/WeaponSynchronization.cs b/WeaponSynchronization.cs index b72b058a..3603018c 100644 --- a/WeaponSynchronization.cs +++ b/WeaponSynchronization.cs @@ -373,34 +373,37 @@ namespace WeaponPaints } } - internal async Task SyncStatTrakToDatabase(PlayerInfo player, int StatTrakCount, int defindex) + internal async Task SyncStatTrakToDatabase(PlayerInfo player, Dictionary weaponStatTrakCounts) { - if (string.IsNullOrEmpty(player.SteamId) || !WeaponPaints.GPlayerWeaponsInfo.TryGetValue(player.Slot, out var weaponsInfo)) + if (string.IsNullOrEmpty(player.SteamId) || weaponStatTrakCounts == null || weaponStatTrakCounts.Count == 0) return; try { await using var connection = await _database.GetConnectionAsync(); + await using var transaction = await connection.BeginTransactionAsync(); - const string queryCheckExistence = "SELECT COUNT(*) FROM `wp_player_skins` WHERE `steamid` = @steamid AND `weapon_defindex` = @weaponDefIndex"; + foreach (var weapon in weaponStatTrakCounts) + { + int defindex = weapon.Key; + int statTrakCount = weapon.Value; - var existingRecordCount = await connection.ExecuteScalarAsync(queryCheckExistence, new { steamid = player.SteamId, weaponDefIndex = defindex }); + const string query = @" + INSERT INTO `wp_player_skins` (`steamid`, `weapon_defindex`, `weapon_stattrak_count`) + VALUES (@steamid, @weaponDefIndex, @StatTrakCount) + ON DUPLICATE KEY UPDATE `weapon_stattrak_count` = @StatTrakCount"; - string query = string.Empty; - object? parameters = null; - - if (existingRecordCount > 0) + var parameters = new { - query = "UPDATE `wp_player_skins` SET `weapon_stattrak_count` = @StatTrakCount WHERE `steamid` = @steamid AND `weapon_defindex` = @weaponDefIndex"; - parameters = new { steamid = player.SteamId, weaponDefIndex = defindex, StatTrakCount }; - } - else - { - query = "INSERT INTO `wp_player_skins` (`steamid`, `weapon_defindex`, `weapon_stattrak_count`) VALUES (@steamid, @weaponDefIndex, @StatTrakCount)"; - parameters = new { steamid = player.SteamId, weaponDefIndex = defindex, StatTrakCount }; - } + steamid = player.SteamId, + weaponDefIndex = defindex, + StatTrakCount = statTrakCount + }; - await connection.ExecuteAsync(query, parameters); + await connection.ExecuteAsync(query, parameters, transaction); + } + + await transaction.CommitAsync(); } catch (Exception e) { From 2bc53cd3c08df552af76c263ec3d432fc9635db9 Mon Sep 17 00:00:00 2001 From: Dawid Bepierszcz <41084667+daffyyyy@users.noreply.github.com> Date: Fri, 18 Oct 2024 23:40:44 +0200 Subject: [PATCH 7/7] 2.7a - Better syncing stattrak --- Config.cs | 4 +-- Events.cs | 62 +++++++++------------------------------- VERSION | 2 +- WeaponAction.cs | 17 +---------- WeaponInfo.cs | 8 +++--- WeaponPaints.cs | 4 ++- WeaponSynchronization.cs | 19 +++++++----- 7 files changed, 37 insertions(+), 79 deletions(-) diff --git a/Config.cs b/Config.cs index a5ad70a3..be606645 100644 --- a/Config.cs +++ b/Config.cs @@ -65,7 +65,7 @@ namespace WeaponPaints public class WeaponPaintsConfig : BasePluginConfig { - [JsonPropertyName("ConfigVersion")] public override int Version { get; set; } = 7; + [JsonPropertyName("ConfigVersion")] public override int Version { get; set; } = 8; [JsonPropertyName("SkinsLanguage")] public string SkinsLanguage { get; set; } = "en"; @@ -91,7 +91,7 @@ namespace WeaponPaints [JsonPropertyName("Website")] public string Website { get; set; } = "example.com/skins"; - [JsonPropertyName("Additionalss")] + [JsonPropertyName("Additional")] public Additional Additional { get; set; } = new(); } } \ No newline at end of file diff --git a/Events.cs b/Events.cs index 97513753..1570eaa0 100644 --- a/Events.cs +++ b/Events.cs @@ -4,8 +4,6 @@ using CounterStrikeSharp.API.Core.Attributes.Registration; using CounterStrikeSharp.API.Modules.Entities; using CounterStrikeSharp.API.Modules.Memory; using CounterStrikeSharp.API.Modules.Memory.DynamicFunctions; -using System.Runtime.InteropServices; -using System.Collections.Concurrent; namespace WeaponPaints { @@ -81,8 +79,9 @@ namespace WeaponPaints if (!GPlayerWeaponsInfo.TryGetValue(player.Slot, out var weaponInfos)) return HookResult.Continue; - - _ = Task.Run(async () => await SyncStatTrakForPlayer(playerInfo, weaponInfos)); + + if (WeaponSync != null) + _ = Task.Run(async () => await WeaponSync.SyncStatTrakToDatabase(playerInfo, weaponInfos)); if (Config.Additional.SkinEnabled) { @@ -110,31 +109,10 @@ namespace WeaponPaints } _temporaryPlayerWeaponWear.TryRemove(player.Slot, out _); - CommandsCooldown.Remove(player.Slot); return HookResult.Continue; } - - public async Task SyncStatTrakForPlayer(PlayerInfo playerInfo, ConcurrentDictionary weaponInfos) - { - if (WeaponSync == null || weaponInfos == null || weaponInfos.Count == 0) return; - - var statTrakWeapons = weaponInfos - .Where(w => w.Value.StatTrak) - .ToDictionary(w => w.Key, w => w.Value.StatTrakCount); - - if (statTrakWeapons.Count == 0) return; - - try - { - await WeaponSync.SyncStatTrakToDatabase(playerInfo, statTrakWeapons); - } - catch (Exception ex) - { - Utility.Log($"Error syncing StatTrak for player {playerInfo.SteamId}: {ex.Message}"); - } - } private void OnMapStart(string mapName) { @@ -286,30 +264,18 @@ namespace WeaponPaints int weaponDefIndex = weapon.AttributeManager.Item.ItemDefinitionIndex; - if (!GPlayerWeaponsInfo[player.Slot].TryGetValue(weaponDefIndex, out var value) || value.Paint == 0) return HookResult.Continue; - - var weaponInfo = value; - - if (weaponInfo.StatTrak) - { - weaponInfo.StatTrakCount += 1; - - var playerInfo = new PlayerInfo - { - UserId = player.UserId, - Slot = player.Slot, - Index = (int)player.Index, - SteamId = player.SteamID.ToString(), - Name = player.PlayerName, - IpAddress = player.IpAddress?.Split(":")[0] - }; - - CAttributeListSetOrAddAttributeValueByName.Invoke(weapon.AttributeManager.Item.NetworkedDynamicAttributes.Handle, "kill eater", ViewAsFloat((uint)weaponInfo.StatTrakCount)); - CAttributeListSetOrAddAttributeValueByName.Invoke(weapon.AttributeManager.Item.NetworkedDynamicAttributes.Handle, "kill eater score type", 0); + if (!GPlayerWeaponsInfo[player.Slot].TryGetValue(weaponDefIndex, out var weaponInfo) || weaponInfo.Paint == 0) + return HookResult.Continue; + + if (!weaponInfo.StatTrak) return HookResult.Continue; + + weaponInfo.StatTrakCount += 1; - CAttributeListSetOrAddAttributeValueByName.Invoke(weapon.AttributeManager.Item.AttributeList.Handle, "kill eater", ViewAsFloat((uint)weaponInfo.StatTrakCount)); - CAttributeListSetOrAddAttributeValueByName.Invoke(weapon.AttributeManager.Item.AttributeList.Handle, "kill eater score type", 0); - } + CAttributeListSetOrAddAttributeValueByName.Invoke(weapon.AttributeManager.Item.NetworkedDynamicAttributes.Handle, "kill eater", ViewAsFloat((uint)weaponInfo.StatTrakCount)); + CAttributeListSetOrAddAttributeValueByName.Invoke(weapon.AttributeManager.Item.NetworkedDynamicAttributes.Handle, "kill eater score type", 0); + + CAttributeListSetOrAddAttributeValueByName.Invoke(weapon.AttributeManager.Item.AttributeList.Handle, "kill eater", ViewAsFloat((uint)weaponInfo.StatTrakCount)); + CAttributeListSetOrAddAttributeValueByName.Invoke(weapon.AttributeManager.Item.AttributeList.Handle, "kill eater score type", 0); return HookResult.Continue; } diff --git a/VERSION b/VERSION index 4b887540..4818b1b8 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -2.6a \ No newline at end of file +2.7a \ No newline at end of file diff --git a/WeaponAction.cs b/WeaponAction.cs index 5f13c1b2..6724cd82 100644 --- a/WeaponAction.cs +++ b/WeaponAction.cs @@ -6,7 +6,6 @@ using CounterStrikeSharp.API.Modules.Timers; using CounterStrikeSharp.API.Modules.Utils; using Microsoft.Extensions.Logging; using System.Collections.Concurrent; -using System.Linq.Expressions; using System.Runtime.InteropServices; namespace WeaponPaints @@ -518,21 +517,7 @@ namespace WeaponPaints return viewModel.Value == null ? null : viewModel.Value; } - public static unsafe T[] GetFixedArray(nint pointer, string @class, string member, int length) where T : CHandle - { - var ptr = pointer + Schema.GetSchemaOffset(@class, member); - var references = MemoryMarshal.CreateSpan(ref ptr, length); - var values = new T[length]; - - for (var i = 0; i < length; i++) - { - values[i] = (T)Activator.CreateInstance(typeof(T), references[i])!; - } - - return values; - } - - private float ViewAsFloat(uint value) + private static float ViewAsFloat(uint value) { return BitConverter.Int32BitsToSingle((int)value); } diff --git a/WeaponInfo.cs b/WeaponInfo.cs index 7f89ebb3..1bd66f6a 100644 --- a/WeaponInfo.cs +++ b/WeaponInfo.cs @@ -3,13 +3,13 @@ public class WeaponInfo { public int Paint { get; set; } - public int Seed { get; set; } = 0; - public float Wear { get; set; } = 0f; + public int Seed { get; set; } + public float Wear { get; set; } public string Nametag { get; set; } = ""; public bool StatTrak { get; set; } = false; - public int StatTrakCount { get; set; } = 0; + public int StatTrakCount { get; set; } public KeyChainInfo? KeyChain { get; set; } - public List Stickers { get; set; } = new List(); + public List Stickers { get; set; } = new(); } public class StickerInfo diff --git a/WeaponPaints.cs b/WeaponPaints.cs index f9e75230..9bb28247 100644 --- a/WeaponPaints.cs +++ b/WeaponPaints.cs @@ -16,7 +16,7 @@ public partial class WeaponPaints : BasePlugin, IPluginConfig "Nereziel & daffyy"; public override string ModuleDescription => "Skin, gloves, agents and knife selector, standalone and web-based"; public override string ModuleName => "WeaponPaints"; - public override string ModuleVersion => "2.6a"; + public override string ModuleVersion => "2.7a"; public override void Load(bool hotReload) { @@ -53,6 +53,8 @@ public partial class WeaponPaints : BasePlugin, IPluginConfig OnAllPluginsLoaded(hotReload)); } Utility.LoadSkinsFromFile(ModuleDirectory + $"/data/skins_{_config.SkinsLanguage}.json", Logger); diff --git a/WeaponSynchronization.cs b/WeaponSynchronization.cs index 3603018c..801bdc8e 100644 --- a/WeaponSynchronization.cs +++ b/WeaponSynchronization.cs @@ -373,9 +373,17 @@ namespace WeaponPaints } } - internal async Task SyncStatTrakToDatabase(PlayerInfo player, Dictionary weaponStatTrakCounts) + internal async Task SyncStatTrakToDatabase(PlayerInfo player, ConcurrentDictionary weaponInfos) { - if (string.IsNullOrEmpty(player.SteamId) || weaponStatTrakCounts == null || weaponStatTrakCounts.Count == 0) + if (WeaponPaints.WeaponSync == null || weaponInfos.IsEmpty) return; + + var statTrakWeapons = weaponInfos + .Where(w => w.Value is { StatTrak: true, StatTrakCount: > 0 }) + .ToDictionary(w => w.Key, w => w.Value.StatTrakCount); + + if (statTrakWeapons.Count == 0) return; + + if (string.IsNullOrEmpty(player.SteamId)) return; try @@ -383,11 +391,8 @@ namespace WeaponPaints await using var connection = await _database.GetConnectionAsync(); await using var transaction = await connection.BeginTransactionAsync(); - foreach (var weapon in weaponStatTrakCounts) + foreach (var (defindex, statTrakCount) in statTrakWeapons) { - int defindex = weapon.Key; - int statTrakCount = weapon.Value; - const string query = @" INSERT INTO `wp_player_skins` (`steamid`, `weapon_defindex`, `weapon_stattrak_count`) VALUES (@steamid, @weaponDefIndex, @StatTrakCount) @@ -407,7 +412,7 @@ namespace WeaponPaints } catch (Exception e) { - Utility.Log($"Error syncing weapon paints to database: {e.Message}"); + Utility.Log($"Error syncing stattrak to database: {e.Message}"); } } }