diff --git a/Commands.cs b/Commands.cs new file mode 100644 index 00000000..9e8bc041 --- /dev/null +++ b/Commands.cs @@ -0,0 +1,287 @@ +using CounterStrikeSharp.API.Core; +using CounterStrikeSharp.API.Modules.Commands; +using CounterStrikeSharp.API.Modules.Entities; +using CounterStrikeSharp.API.Modules.Menu; + +namespace WeaponPaints +{ + public partial class WeaponPaints + { + private void RegisterCommands() + { + AddCommand($"css_{Config.Additional.CommandSkin}", "Skins info", (player, info) => + { + if (!Utility.IsPlayerValid(player)) return; + OnCommandWS(player, info); + }); + AddCommand($"css_{Config.Additional.CommandRefresh}", "Skins refresh", (player, info) => + { + if (!Utility.IsPlayerValid(player)) return; + OnCommandRefresh(player, info); + }); + if (Config.Additional.CommandKillEnabled) + { + AddCommand($"css_{Config.Additional.CommandKill}", "kill yourself", (player, info) => + { + if (!Utility.IsPlayerValid(player) || !player!.PlayerPawn.IsValid) return; + + player.PlayerPawn.Value.CommitSuicide(true, false); + }); + } + } + private void SetupKnifeMenu() + { + if (!Config.Additional.KnifeEnabled) return; + + var knivesOnly = weaponList + .Where(pair => pair.Key.StartsWith("weapon_knife") || pair.Key.StartsWith("weapon_bayonet")) + .ToDictionary(pair => pair.Key, pair => pair.Value); + + var giveItemMenu = new ChatMenu(Utility.ReplaceTags($" {Config.Messages.KnifeMenuTitle}")); + var handleGive = (CCSPlayerController? player, ChatMenuOption option) => + { + if (Utility.IsPlayerValid(player)) + { + var knifeName = option.Text; + var knifeKey = knivesOnly.FirstOrDefault(x => x.Value == knifeName).Key; + if (!string.IsNullOrEmpty(knifeKey)) + { + string temp = ""; + + if (!string.IsNullOrEmpty(Config.Messages.ChosenKnifeMenu)) + { + temp = $" {Config.Prefix} {Config.Messages.ChosenKnifeMenu}".Replace("{KNIFE}", knifeName); + player!.PrintToChat(Utility.ReplaceTags(temp)); + } + + if (!string.IsNullOrEmpty(Config.Messages.ChosenKnifeMenuKill) && Config.Additional.CommandKillEnabled) + { + temp = $" {Config.Prefix} {Config.Messages.ChosenKnifeMenuKill}"; + player!.PrintToChat(Utility.ReplaceTags(temp)); + } + + g_playersKnife[(int)player!.EntityIndex!.Value.Value] = knifeKey; + + if (player!.PawnIsAlive) + { + g_changedKnife.Add((int)player.EntityIndex!.Value.Value); + if (PlayerHasKnife(player)) + { + RefreshPlayerKnife(player); + } + + /* + AddTimer(1.0f, () => GiveKnifeToPlayer(player)); + */ + } + if (weaponSync != null) + Task.Run(() => weaponSync.SyncKnifeToDatabase((int)player.EntityIndex!.Value.Value, knifeKey)); + } + } + }; + foreach (var knifePair in knivesOnly) + { + giveItemMenu.AddMenuOption(knifePair.Value, handleGive); + } + AddCommand($"css_{Config.Additional.CommandKnife}", "Knife Menu", (player, info) => + { + if (!Utility.IsPlayerValid(player)) return; + int playerIndex = (int)player!.EntityIndex!.Value.Value; + + if (commandCooldown != null && DateTime.UtcNow >= commandCooldown[playerIndex].AddSeconds(Config.CmdRefreshCooldownSeconds) && playerIndex > 0 && playerIndex < commandCooldown.Length) + { + commandCooldown[playerIndex] = DateTime.UtcNow; + ChatMenus.OpenMenu(player, giveItemMenu); + return; + } + if (!string.IsNullOrEmpty(Config.Messages.CooldownRefreshCommand)) + { + string temp = $" {Config.Prefix} {Config.Messages.CooldownRefreshCommand}"; + player.PrintToChat(Utility.ReplaceTags(temp)); + } + }); + } + + private void SetupSkinsMenu() + { + var classNamesByWeapon = weaponList.ToDictionary(kvp => kvp.Value, kvp => kvp.Key); + var weaponSelectionMenu = new ChatMenu(Utility.ReplaceTags($" {Config.Messages.WeaponMenuTitle}")); + + // Function to handle skin selection for a specific weapon + var handleWeaponSelection = (CCSPlayerController? player, ChatMenuOption option) => + { + if (!Utility.IsPlayerValid(player)) return; + + int playerIndex = (int)player!.EntityIndex!.Value.Value; + string selectedWeapon = option.Text; + if (classNamesByWeapon.TryGetValue(selectedWeapon, out string? selectedWeaponClassname)) + { + if (selectedWeaponClassname == null) return; + var skinsForSelectedWeapon = skinsList?.Where(skin => + skin != null && + skin.TryGetValue("weapon_name", out var weaponName) && + weaponName?.ToString() == selectedWeaponClassname + )?.ToList(); + + var skinSubMenu = new ChatMenu(Utility.ReplaceTags($" {Config.Messages.SkinMenuTitle}").Replace("{WEAPON}", selectedWeapon)); + + // Function to handle skin selection for the chosen weapon + var handleSkinSelection = (CCSPlayerController? p, ChatMenuOption opt) => + { + if (p == null || !p.IsValid) return; + + var steamId = new SteamID(player.SteamID); + var firstSkin = skinsList?.FirstOrDefault(skin => + { + if (skin != null && skin.TryGetValue("weapon_name", out var weaponName)) + { + return weaponName?.ToString() == selectedWeaponClassname; + } + return false; + }); + string selectedSkin = opt.Text; + string selectedPaintID = selectedSkin.Split('(')[1].Trim(')').Trim(); + + if (firstSkin != null && + firstSkin.TryGetValue("weapon_defindex", out var weaponDefIndexObj) && + weaponDefIndexObj != null && + int.TryParse(weaponDefIndexObj.ToString(), out var weaponDefIndex) && + int.TryParse(selectedPaintID, out var paintID)) + { + string temp = $" {Config.Prefix} {Config.Messages.ChosenSkinMenu}".Replace("{SKIN}", selectedSkin); + p.PrintToChat(Utility.ReplaceTags(temp)); + + if (!gPlayerWeaponsInfo[playerIndex].ContainsKey(weaponDefIndex)) + { + gPlayerWeaponsInfo[playerIndex][weaponDefIndex] = new WeaponInfo(); + } + + gPlayerWeaponsInfo[playerIndex][weaponDefIndex].Paint = paintID; + gPlayerWeaponsInfo[playerIndex][weaponDefIndex].Wear = 0.0f; + gPlayerWeaponsInfo[playerIndex][weaponDefIndex].Seed = 0; + + if (weaponSync == null) return; + Task.Run(async () => + { + await weaponSync.SyncWeaponPaintsToDatabase(player); + }); + } + }; + + // Add skin options to the submenu for the selected weapon + if (skinsForSelectedWeapon != null) + { + foreach (var skin in skinsForSelectedWeapon.Where(s => s != null)) + { + if (skin.TryGetValue("paint_name", out var paintNameObj) && skin.TryGetValue("paint", out var paintObj)) + { + var paintName = paintNameObj?.ToString(); + var paint = paintObj?.ToString(); + + if (!string.IsNullOrEmpty(paintName) && !string.IsNullOrEmpty(paint)) + { + skinSubMenu.AddMenuOption($"{paintName} ({paint})", handleSkinSelection); + } + } + } + } + + // Open the submenu for skin selection of the chosen weapon + ChatMenus.OpenMenu(player, skinSubMenu); + } + }; + + // Add weapon options to the weapon selection menu + foreach (var weaponClass in weaponList.Keys) + { + string weaponName = weaponList[weaponClass]; + weaponSelectionMenu.AddMenuOption(weaponName, handleWeaponSelection); + } + // Command to open the weapon selection menu for players + AddCommand($"css_{Config.Additional.CommandSkinSelection}", "Skins selection menu", (player, info) => + { + if (!Utility.IsPlayerValid(player)) return; + int playerIndex = (int)player!.EntityIndex!.Value.Value; + + if (commandCooldown != null && DateTime.UtcNow >= commandCooldown[playerIndex].AddSeconds(Config.CmdRefreshCooldownSeconds) && playerIndex > 0 && playerIndex < commandCooldown.Length) + { + commandCooldown[playerIndex] = DateTime.UtcNow; + ChatMenus.OpenMenu(player, weaponSelectionMenu); + return; + } + if (!string.IsNullOrEmpty(Config.Messages.CooldownRefreshCommand)) + { + string temp = $"{Config.Prefix} {Config.Messages.CooldownRefreshCommand}"; + player.PrintToChat(Utility.ReplaceTags(temp)); + } + + }); + } + + private void OnCommandRefresh(CCSPlayerController? player, CommandInfo command) + { + if (!Config.Additional.CommandWpEnabled || !Config.Additional.SkinEnabled) return; + if (!Utility.IsPlayerValid(player)) return; + string temp = ""; + int playerIndex = (int)player!.EntityIndex!.Value.Value; + if (commandCooldown != null && DateTime.UtcNow >= commandCooldown[playerIndex].AddSeconds(Config.CmdRefreshCooldownSeconds)) + { + commandCooldown[playerIndex] = DateTime.UtcNow; + if (weaponSync != null) + Task.Run(async () => await weaponSync.GetWeaponPaintsFromDatabase(playerIndex)); + if (Config.Additional.KnifeEnabled) + { + if (PlayerHasKnife(player)) + RefreshPlayerKnife(player); + /* + AddTimer(1.0f, () => + { + GiveKnifeToPlayer(player); + }); + */ + if (weaponSync != null) + Task.Run(async () => await weaponSync.GetKnifeFromDatabase(playerIndex)); + /* + RemoveKnifeFromPlayer(player); + AddTimer(0.2f, () => GiveKnifeToPlayer(player)); + */ + } + if (!string.IsNullOrEmpty(Config.Messages.SuccessRefreshCommand)) + { + temp = $" {Config.Prefix} {Config.Messages.SuccessRefreshCommand}"; + player.PrintToChat(Utility.ReplaceTags(temp)); + } + return; + } + if (!string.IsNullOrEmpty(Config.Messages.CooldownRefreshCommand)) + { + temp = $" {Config.Prefix} {Config.Messages.CooldownRefreshCommand}"; + player.PrintToChat(Utility.ReplaceTags(temp)); + } + } + private void OnCommandWS(CCSPlayerController? player, CommandInfo command) + { + if (!Config.Additional.SkinEnabled) return; + if (!Utility.IsPlayerValid(player)) return; + + string temp = ""; + + if (!string.IsNullOrEmpty(Config.Messages.WebsiteMessageCommand)) + { + temp = $" {Config.Prefix} {Config.Messages.WebsiteMessageCommand}"; + player!.PrintToChat(Utility.ReplaceTags(temp)); + } + if (!string.IsNullOrEmpty(Config.Messages.SynchronizeMessageCommand)) + { + temp = $" {Config.Prefix} {Config.Messages.SynchronizeMessageCommand}"; + player!.PrintToChat(Utility.ReplaceTags(temp)); + } + if (!Config.Additional.KnifeEnabled) return; + if (!string.IsNullOrEmpty(Config.Messages.KnifeMessageCommand)) + { + temp = $" {Config.Prefix} {Config.Messages.KnifeMessageCommand}"; + player!.PrintToChat(Utility.ReplaceTags(temp)); + } + } + } +} diff --git a/Events.cs b/Events.cs new file mode 100644 index 00000000..f94afe5c --- /dev/null +++ b/Events.cs @@ -0,0 +1,183 @@ +using CounterStrikeSharp.API; +using CounterStrikeSharp.API.Core; +using CounterStrikeSharp.API.Modules.Entities; +using CounterStrikeSharp.API.Core.Attributes; +using CounterStrikeSharp.API.Core.Attributes.Registration; +using CounterStrikeSharp.API.Modules.Utils; +using CounterStrikeSharp.API.Modules.Admin; +using CounterStrikeSharp.API.Modules.Commands; +using CounterStrikeSharp.API.Modules.Cvars; +using CounterStrikeSharp.API.Modules.Memory; +using CounterStrikeSharp.API.Modules.Memory.DynamicFunctions; + +namespace WeaponPaints +{ + public partial class WeaponPaints + { + private void RegisterEvents() + { + RegisterListener(OnEntitySpawned); + RegisterEventHandler(OnEventItemPurchasePost); + RegisterListener(OnClientAuthorized); + RegisterListener(OnClientDisconnect); + RegisterListener(OnMapStart); + RegisterEventHandler(OnPlayerSpawn); + RegisterEventHandler(OnRoundStart, HookMode.Pre); + RegisterEventHandler(OnItemPickup); + + } + private void OnMapStart(string mapName) + { + if (!Config.Additional.KnifeEnabled) return; + // TODO + // needed for now + AddTimer(2.0f, () => + { + NativeAPI.IssueServerCommand("mp_t_default_melee \"\""); + NativeAPI.IssueServerCommand("mp_ct_default_melee \"\""); + }); + } + private void OnClientAuthorized(int playerSlot, SteamID steamID) + { + int playerIndex = playerSlot + 1; + Task.Run(async () => + { + if (Config.Additional.KnifeEnabled && weaponSync != null) + await weaponSync.GetKnifeFromDatabase(playerIndex); + if (Config.Additional.SkinEnabled && weaponSync != null) + await weaponSync.GetWeaponPaintsFromDatabase(playerIndex); + }); + } + private void OnClientDisconnect(int playerSlot) + { + CCSPlayerController player = Utilities.GetPlayerFromSlot(playerSlot); + + if (player == null || !player.IsValid || player.IsHLTV) return; + + if (Config.Additional.KnifeEnabled) + g_playersKnife.Remove((int)player.EntityIndex!.Value.Value); + if (Config.Additional.SkinEnabled) + gPlayerWeaponsInfo.Remove((int)player.EntityIndex!.Value.Value); + } + + private HookResult OnPlayerSpawn(EventPlayerSpawn @event, GameEventInfo info) + { + CCSPlayerController? player = @event.Userid; + if (player == null || !player.IsValid || !player.PlayerPawn.IsValid) + { + return HookResult.Continue; + } + + if (Config.Additional.KnifeEnabled) + { + g_knifePickupCount[(int)player.EntityIndex!.Value.Value] = 0; + if (!PlayerHasKnife(player)) + GiveKnifeToPlayer(player); + } + + if (Config.Additional.SkinVisibilityFix) + { + AddTimer(0.3f, () => RefreshSkins(player)); + } + + return HookResult.Continue; + } + private HookResult OnRoundStart(EventRoundStart @event, GameEventInfo info) + { + NativeAPI.IssueServerCommand("mp_t_default_melee \"\""); + NativeAPI.IssueServerCommand("mp_ct_default_melee \"\""); + + return HookResult.Continue; + } + private HookResult OnItemPickup(EventItemPickup @event, GameEventInfo info) + { + if (@event.Defindex == 42 || @event.Defindex == 59) + { + CCSPlayerController? player = @event.Userid; + if (!Utility.IsPlayerValid(player) || !player.PawnIsAlive || g_knifePickupCount[(int)player.EntityIndex!.Value.Value] >= 1) return HookResult.Continue; + + if (g_playersKnife.ContainsKey((int)player.EntityIndex!.Value.Value) + && + g_playersKnife[(int)player.EntityIndex!.Value.Value] != "weapon_knife") + { + g_knifePickupCount[(int)player.EntityIndex!.Value.Value]++; + RefreshPlayerKnife(player); + /* + if (!PlayerHasKnife(player)) + GiveKnifeToPlayer(player); + */ + if (Config.Additional.SkinVisibilityFix) + { + AddTimer(0.25f, () => RefreshSkins(player)); + } + } + } + return HookResult.Continue; + } + + private void OnEntitySpawned(CEntityInstance entity) + { + if (!Config.Additional.SkinEnabled) return; + var designerName = entity.DesignerName; + if (!weaponList.ContainsKey(designerName)) return; + bool isKnife = false; + var weapon = new CBasePlayerWeapon(entity.Handle); + + if (designerName.Contains("knife") || designerName.Contains("bayonet")) + { + isKnife = true; + } + Server.NextFrame(() => + { + try + { + if (!weapon.IsValid) return; + if (weapon.OwnerEntity.Value == null) return; + if (!weapon.OwnerEntity.Value.EntityIndex.HasValue) + { + for (int i = 1; i <= Server.MaxPlayers; i++) + { + CCSPlayerController? ghostPlayer = Utilities.GetPlayerFromIndex(i); + if (!Utility.IsPlayerValid(ghostPlayer)) continue; + if (g_changedKnife.Contains((int)ghostPlayer.EntityIndex!.Value.Value)) + { + ChangeWeaponAttributes(weapon, ghostPlayer, isKnife); + g_changedKnife.Remove((int)ghostPlayer.EntityIndex!.Value.Value); + break; + } + } + return; + } + + if (!weapon.OwnerEntity.Value.EntityIndex.HasValue) return; + int weaponOwner = (int)weapon.OwnerEntity.Value.EntityIndex.Value.Value; + var pawn = new CBasePlayerPawn(NativeAPI.GetEntityFromIndex(weaponOwner)); + if (!pawn.IsValid) return; + var playerIndex = (int)pawn.Controller.Value.EntityIndex!.Value.Value; + var player = Utilities.GetPlayerFromIndex(playerIndex); + if (!Utility.IsPlayerValid(player)) return; + + // TODO: Remove knife crashes here, needs another solution + /*if (isKnife && g_playersKnife[(int)player.EntityIndex!.Value.Value] != "weapon_knife" && (weapon.AttributeManager.Item.ItemDefinitionIndex == 42 || weapon.AttributeManager.Item.ItemDefinitionIndex == 59)) + { + RemoveKnifeFromPlayer(player); + return; + }*/ + ChangeWeaponAttributes(weapon, player, isKnife); + } + catch (Exception) { } + }); + } + private HookResult OnEventItemPurchasePost(EventItemPurchase @event, GameEventInfo info) + { + CCSPlayerController? player = @event.Userid; + + if (player == null || !player.IsValid) return HookResult.Continue; + + if (Config.Additional.SkinVisibilityFix) + AddTimer(0.2f, () => RefreshSkins(player)); + + return HookResult.Continue; + } + } +} diff --git a/Utility.cs b/Utility.cs index 820acca6..a59a6047 100644 --- a/Utility.cs +++ b/Utility.cs @@ -1,20 +1,102 @@ using CounterStrikeSharp.API.Core; using CounterStrikeSharp.API.Modules.Entities; using CounterStrikeSharp.API.Modules.Utils; +using Dapper; +using MySqlConnector; +using Newtonsoft.Json.Linq; +using Newtonsoft.Json; using System.Reflection; namespace WeaponPaints { - public static class Utility + internal static class Utility { - public static WeaponPaintsConfig? Config { get; set; } + internal static WeaponPaintsConfig? Config { get; set; } - public static bool IsPlayerValid(CCSPlayerController? player) + internal static bool IsPlayerValid(CCSPlayerController? player) { return (player != null && player.IsValid && !player.IsBot && !player.IsHLTV); } - public static string ReplaceTags(string message) + internal static string BuildDatabaseConnectionString() + { + if (Config == null) return String.Empty; + var builder = new MySqlConnectionStringBuilder + { + Server = Config.DatabaseHost, + UserID = Config.DatabaseUser, + Password = Config.DatabasePassword, + Database = Config.DatabaseName, + Port = (uint)Config.DatabasePort, + }; + + return builder.ConnectionString; + } + + internal static void TestDatabaseConnection() + { + try + { + using var connection = new MySqlConnection(BuildDatabaseConnectionString()); + connection.Open(); + + if (connection.State != System.Data.ConnectionState.Open) + { + throw new Exception("[WeaponPaints] Unable connect to database!"); + } + } + catch (Exception ex) + { + throw new Exception("[WeaponPaints] Unknown mysql exception! " + ex.Message); + } + CheckDatabaseTables(); + } + + internal static async void CheckDatabaseTables() + { + try + { + using var connection = new MySqlConnection(BuildDatabaseConnectionString()); + await connection.OpenAsync(); + + using var transaction = await connection.BeginTransactionAsync(); + + try + { + string createTable1 = "CREATE TABLE IF NOT EXISTS `wp_player_skins` (`steamid` varchar(64) NOT NULL, `weapon_defindex` int(6) NOT NULL, `weapon_paint_id` int(6) NOT NULL, `weapon_wear` float NOT NULL DEFAULT 0.0001, `weapon_seed` int(16) NOT NULL DEFAULT 0) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_unicode_ci"; + string createTable2 = "CREATE TABLE IF NOT EXISTS `wp_player_knife` (`steamid` varchar(64) NOT NULL, `knife` varchar(64) NOT NULL, UNIQUE (`steamid`)) ENGINE = InnoDB"; + + await connection.ExecuteAsync(createTable1, transaction: transaction); + await connection.ExecuteAsync(createTable2, transaction: transaction); + + await transaction.CommitAsync(); + } + catch (Exception) + { + await transaction.RollbackAsync(); + throw new Exception("[WeaponPaints] Unable to create tables!"); + } + } + catch (Exception ex) + { + throw new Exception("[WeaponPaints] Unknown mysql exception! " + ex.Message); + } + } + internal static void LoadSkinsFromFile(string filePath) + { + if (File.Exists(filePath)) + { + string json = File.ReadAllText(filePath); + var deserializedSkins = JsonConvert.DeserializeObject>(json); + WeaponPaints.skinsList = deserializedSkins ?? new List(); + } + else + { + throw new FileNotFoundException("File not found.", filePath); + } + } + + internal static string ReplaceTags(string message) { if (message.Contains('{')) { @@ -37,14 +119,14 @@ namespace WeaponPaints return message; } - public static void Log(string message) + internal static void Log(string message) { Console.BackgroundColor = ConsoleColor.DarkGray; Console.ForegroundColor = ConsoleColor.Cyan; Console.WriteLine("[WeaponPaints] " + message); Console.ResetColor(); } - public static void ShowAd(string moduleVersion) + internal static void ShowAd(string moduleVersion) { Console.WriteLine(" "); Console.WriteLine(" _ _ _______ _______ _______ _______ __ _ _______ _______ ___ __ _ _______ _______ "); diff --git a/WeaponAction.cs b/WeaponAction.cs new file mode 100644 index 00000000..9e8d52a6 --- /dev/null +++ b/WeaponAction.cs @@ -0,0 +1,222 @@ +using CounterStrikeSharp.API; +using CounterStrikeSharp.API.Core; +using CounterStrikeSharp.API.Modules.Memory; +using CounterStrikeSharp.API.Modules.Utils; + +namespace WeaponPaints +{ + public partial class WeaponPaints + { + internal static void ChangeWeaponAttributes(CBasePlayerWeapon? weapon, CCSPlayerController? player, bool isKnife = false) + { + if (weapon == null || !weapon.IsValid || !Utility.IsPlayerValid(player)) return; + + int playerIndex = (int)player!.EntityIndex!.Value.Value; + + if (!gPlayerWeaponsInfo.ContainsKey(playerIndex)) return; + + if (isKnife && !g_playersKnife.ContainsKey(playerIndex) || isKnife && g_playersKnife[playerIndex] == "weapon_knife") return; + + int weaponDefIndex = weapon.AttributeManager.Item.ItemDefinitionIndex; + + if (_config.Additional.GiveRandomSkin && + !gPlayerWeaponsInfo[playerIndex].ContainsKey(weaponDefIndex)) + { + // Random skins + weapon.AttributeManager.Item.ItemID = 16384; + weapon.AttributeManager.Item.ItemIDLow = 16384 & 0xFFFFFFFF; + weapon.AttributeManager.Item.ItemIDHigh = weapon.AttributeManager.Item.ItemIDLow >> 32; + weapon.FallbackPaintKit = GetRandomPaint(weaponDefIndex); + weapon.FallbackSeed = 0; + weapon.FallbackWear = 0.0f; + if (!isKnife && weapon.CBodyComponent != null && weapon.CBodyComponent.SceneNode != null) + { + var skeleton = GetSkeletonInstance(weapon.CBodyComponent.SceneNode); + skeleton.ModelState.MeshGroupMask = 2; + } + return; + } + + if (!gPlayerWeaponsInfo[playerIndex].ContainsKey(weaponDefIndex)) return; + WeaponInfo weaponInfo = gPlayerWeaponsInfo[playerIndex][weaponDefIndex]; + //Log($"Apply on {weapon.DesignerName}({weapon.AttributeManager.Item.ItemDefinitionIndex}) paint {gPlayerWeaponPaints[steamId.SteamId64][weapon.AttributeManager.Item.ItemDefinitionIndex]} seed {gPlayerWeaponSeed[steamId.SteamId64][weapon.AttributeManager.Item.ItemDefinitionIndex]} wear {gPlayerWeaponWear[steamId.SteamId64][weapon.AttributeManager.Item.ItemDefinitionIndex]}"); + weapon.AttributeManager.Item.ItemID = 16384; + weapon.AttributeManager.Item.ItemIDLow = 16384 & 0xFFFFFFFF; + weapon.AttributeManager.Item.ItemIDHigh = weapon.AttributeManager.Item.ItemIDLow >> 32; + weapon.FallbackPaintKit = weaponInfo.Paint; + weapon.FallbackSeed = weaponInfo.Seed; + weapon.FallbackWear = weaponInfo.Wear; + if (!isKnife && weapon.CBodyComponent != null && weapon.CBodyComponent.SceneNode != null) + { + var skeleton = GetSkeletonInstance(weapon.CBodyComponent.SceneNode); + skeleton.ModelState.MeshGroupMask = 2; + } + } + internal static void GiveKnifeToPlayer(CCSPlayerController? player) + { + if (!_config.Additional.KnifeEnabled || player == null || !player.EntityIndex.HasValue || !player.IsValid) return; + if (g_playersKnife.TryGetValue((int)player.EntityIndex.Value.Value, out var knife)) + { + player.GiveNamedItem(knife); + } + else if (_config.Additional.GiveRandomKnife) + { + var knifeTypes = weaponList.Where(pair => pair.Key.StartsWith("weapon_knife") || pair.Key.StartsWith("weapon_bayonet")).ToDictionary(pair => pair.Key, pair => pair.Value); + + Random random = new(); + int index = random.Next(knifeTypes.Count); + var randomKnifeClass = knifeTypes.Keys.ElementAt(index); + + player.GiveNamedItem(randomKnifeClass); + } + else + { + var defaultKnife = (CsTeam)player.TeamNum == CsTeam.Terrorist ? "weapon_knife_t" : "weapon_knife"; + player.GiveNamedItem(defaultKnife); + } + } + internal void RemovePlayerKnife(CCSPlayerController? player) + { + if (player == null || !player.IsValid || !player.PawnIsAlive) return; + if (player.PlayerPawn.Value.WeaponServices == null || player.PlayerPawn.Value.ItemServices == null) return; + + var weapons = player.PlayerPawn.Value.WeaponServices.MyWeapons; + if (weapons != null && weapons.Count > 0) + { + CCSPlayer_ItemServices service = new CCSPlayer_ItemServices(player.PlayerPawn.Value.ItemServices.Handle); + //var dropWeapon = VirtualFunction.CreateVoid(service.Handle, GameData.GetOffset("CCSPlayer_ItemServices_DropActivePlayerWeapon")); + + foreach (var weapon in weapons) + { + if (weapon != null && weapon.IsValid && weapon.Value.IsValid) + { + //if (weapon.Value.AttributeManager.Item.ItemDefinitionIndex == 42 || weapon.Value.AttributeManager.Item.ItemDefinitionIndex == 59) + if (weapon.Value.DesignerName.Contains("knife") || weapon.Value.DesignerName.Contains("bayonet")) + { + if (!weapon.Value.EntityIndex.HasValue) return; + int weaponEntityIndex = (int)weapon.Value.EntityIndex!.Value.Value; + NativeAPI.IssueClientCommand((int)player.EntityIndex!.Value.Value - 1, "slot3"); + AddTimer(0.35f, () => service.DropActivePlayerWeapon(weapon.Value)); + + AddTimer(1.0f, () => + { + CEntityInstance? knife = Utilities.GetEntityFromIndex(weaponEntityIndex); + if (knife != null && knife.IsValid) + { + knife.Remove(); + } + }); + + break; + } + } + } + } + } + internal void RefreshPlayerKnife(CCSPlayerController? player) + { + if (player == null || !player.IsValid || !player.PawnIsAlive) return; + if (player.PlayerPawn.Value.WeaponServices == null || player.PlayerPawn.Value.ItemServices == null) return; + + var weapons = player.PlayerPawn.Value.WeaponServices.MyWeapons; + if (weapons != null && weapons.Count > 0) + { + CCSPlayer_ItemServices service = new CCSPlayer_ItemServices(player.PlayerPawn.Value.ItemServices.Handle); + //var dropWeapon = VirtualFunction.CreateVoid(service.Handle, GameData.GetOffset("CCSPlayer_ItemServices_DropActivePlayerWeapon")); + + foreach (var weapon in weapons) + { + if (weapon != null && weapon.IsValid && weapon.Value.IsValid) + { + //if (weapon.Value.AttributeManager.Item.ItemDefinitionIndex == 42 || weapon.Value.AttributeManager.Item.ItemDefinitionIndex == 59) + if (weapon.Value.DesignerName.Contains("knife") || weapon.Value.DesignerName.Contains("bayonet")) + { + if (!weapon.Value.EntityIndex.HasValue) return; + int weaponEntityIndex = (int)weapon.Value.EntityIndex!.Value.Value; + NativeAPI.IssueClientCommand((int)player.EntityIndex!.Value.Value - 1, "slot3"); + AddTimer(0.35f, () => service.DropActivePlayerWeapon(weapon.Value)); + + AddTimer(1.0f, () => + { + CEntityInstance? knife = Utilities.GetEntityFromIndex(weaponEntityIndex); + if (knife != null && knife.IsValid) + { + knife.Remove(); + + if (player.PawnIsAlive) + GiveKnifeToPlayer(player); + } + }); + + break; + } + } + } + } + } + + internal void RefreshSkins(CCSPlayerController? player) + { + if (!Utility.IsPlayerValid(player) || !player!.PawnIsAlive) return; + + AddTimer(0.18f, () => NativeAPI.IssueClientCommand((int)player.EntityIndex!.Value.Value - 1, "slot3")); + AddTimer(0.25f, () => NativeAPI.IssueClientCommand((int)player.EntityIndex!.Value.Value - 1, "slot2")); + AddTimer(0.38f, () => NativeAPI.IssueClientCommand((int)player.EntityIndex!.Value.Value - 1, "slot1")); + } + internal static bool PlayerHasKnife(CCSPlayerController? player) + { + if (!WeaponPaints._config.Additional.KnifeEnabled) return false; + + if (player == null || !player.IsValid) + { + return false; + } + + var weapons = player.PlayerPawn.Value.WeaponServices!.MyWeapons; + if (weapons == null || weapons.Count <= 0) return false; + foreach (var weapon in weapons) + { + if (weapon != null && weapon.IsValid && weapon.Value.IsValid) + { + if (weapon.Value.DesignerName.Contains("knife") || weapon.Value.DesignerName.Contains("bayonet")) + { + return true; + } + } + } + return false; + } + + private static CSkeletonInstance GetSkeletonInstance(CGameSceneNode node) + { + Func GetSkeletonInstance = VirtualFunction.Create(node.Handle, 8); + return new CSkeletonInstance(GetSkeletonInstance(node.Handle)); + } + + private static int GetRandomPaint(int defindex) + { + Random rnd = new Random(); + + if (WeaponPaints.skinsList != null) + { + // Filter weapons by the provided defindex + var filteredWeapons = WeaponPaints.skinsList.FindAll(w => w["weapon_defindex"]?.ToString() == defindex.ToString()); + + if (filteredWeapons.Count > 0) + { + var randomWeapon = filteredWeapons[rnd.Next(filteredWeapons.Count)]; + if (int.TryParse(randomWeapon["paint"]?.ToString(), out int paintValue)) + { + return paintValue; + } + else + { + return 0; + } + + } + } + return 0; + } + } +} diff --git a/WeaponInfo.cs b/WeaponInfo.cs new file mode 100644 index 00000000..310c6e87 --- /dev/null +++ b/WeaponInfo.cs @@ -0,0 +1,15 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace WeaponPaints +{ + public class WeaponInfo + { + public int Paint { get; set; } + public int Seed { get; set; } + public float Wear { get; set; } + } +} diff --git a/WeaponPaints.cs b/WeaponPaints.cs index e713f6f2..82605b14 100644 --- a/WeaponPaints.cs +++ b/WeaponPaints.cs @@ -1,44 +1,43 @@ using CounterStrikeSharp.API; using CounterStrikeSharp.API.Core; using CounterStrikeSharp.API.Core.Attributes; -using CounterStrikeSharp.API.Modules.Commands; -using CounterStrikeSharp.API.Modules.Entities; using CounterStrikeSharp.API.Modules.Memory; -using CounterStrikeSharp.API.Modules.Menu; using CounterStrikeSharp.API.Modules.Utils; -using MySqlConnector; -using Dapper; -using System.Runtime.ExceptionServices; -using System.Reflection; using CounterStrikeSharp.API.Modules.Cvars; using Newtonsoft.Json; using Newtonsoft.Json.Linq; namespace WeaponPaints; [MinimumApiVersion(61)] -public class WeaponPaints : BasePlugin, IPluginConfig +public partial class WeaponPaints : BasePlugin, IPluginConfig { public override string ModuleName => "WeaponPaints"; public override string ModuleDescription => "Skin and knife selector, standalone and web-based"; public override string ModuleAuthor => "Nereziel & daffyy"; public override string ModuleVersion => "1.2a"; public WeaponPaintsConfig Config { get; set; } = new(); + internal static WeaponPaintsConfig _config = new WeaponPaintsConfig(); - private string DatabaseConnectionString = string.Empty; - private Uri GlobalShareApi = new Uri("https://weaponpaints.fun/api.php"); + internal static WeaponSynchronization? weaponSync; - public bool IsMatchZy = false; - public int GlobalShareServerId = 0; - - private DateTime[] commandCooldown = new DateTime[Server.MaxPlayers]; + /* private Dictionary> gPlayerWeaponPaints = new(); private Dictionary> gPlayerWeaponSeed = new(); private Dictionary> gPlayerWeaponWear = new(); - private Dictionary g_playersKnife = new(); - private List g_changedKnife = new(); + */ + private string DatabaseConnectionString = string.Empty; - private static List skinsList = new List(); - private static readonly Dictionary weaponList = new() + internal Uri GlobalShareApi = new Uri("https://weaponpaints.fun/api.php"); + internal int GlobalShareServerId = 0; + + private DateTime[] commandCooldown = new DateTime[Server.MaxPlayers]; + internal static Dictionary> gPlayerWeaponsInfo = new Dictionary>(); + internal static Dictionary g_knifePickupCount = new Dictionary(); + internal static Dictionary g_playersKnife = new(); + internal static List g_changedKnife = new(); + + internal static List skinsList = new List(); + internal static readonly Dictionary weaponList = new() { {"weapon_deagle", "Desert Eagle"}, {"weapon_elite", "Dual Berettas"}, @@ -100,18 +99,15 @@ public class WeaponPaints : BasePlugin, IPluginConfig { if (!Config.GlobalShare) { - BuildDatabaseConnectionString(); - TestDatabaseConnection(); + DatabaseConnectionString = Utility.BuildDatabaseConnectionString(); + Utility.TestDatabaseConnection(); } - RegisterListener(OnEntitySpawned); - RegisterEventHandler(OnEventItemPurchasePost); - RegisterListener(OnClientPutInServer); - RegisterListener(OnClientDisconnect); - RegisterListener(OnMapStart); - RegisterEventHandler(OnPlayerSpawn); - RegisterEventHandler(OnRoundStart, HookMode.Pre); - RegisterEventHandler(OnItemPickup, HookMode.Pre); - RegisterEventHandler(OnItemRemove); + + if (Config.GlobalShare) + GlobalShareConnect(); + + + weaponSync = new WeaponSynchronization(DatabaseConnectionString, Config, GlobalShareApi, GlobalShareServerId); if (hotReload) { @@ -120,11 +116,11 @@ public class WeaponPaints : BasePlugin, IPluginConfig { for (int i = 1; i <= Server.MaxPlayers; i++) { - if (Config.Additional.SkinEnabled) - await GetWeaponPaintsFromDatabase(i); + if (Config.Additional.SkinEnabled && weaponSync != null) + await weaponSync.GetWeaponPaintsFromDatabase(i); - if (Config.Additional.KnifeEnabled) - await GetKnifeFromDatabase(i); + if (Config.Additional.KnifeEnabled && weaponSync != null) + await weaponSync.GetKnifeFromDatabase(i); } }); } @@ -134,15 +130,10 @@ public class WeaponPaints : BasePlugin, IPluginConfig if (Config.Additional.SkinEnabled) SetupSkinsMenu(); + RegisterEvents(); RegisterCommands(); - LoadSkinsFromFile(ModuleDirectory + "/skins.json"); - } - - private HookResult OnItemRemove(EventItemRemove @event, GameEventInfo info) - { - Console.WriteLine(@event.Defindex); - return HookResult.Continue; + Utility.LoadSkinsFromFile(ModuleDirectory + "/skins.json"); } public void OnConfigParsed(WeaponPaintsConfig config) @@ -156,72 +147,16 @@ public class WeaponPaints : BasePlugin, IPluginConfig } Config = config; + _config = config; Utility.Config = config; Utility.ShowAd(ModuleVersion); } - private void BuildDatabaseConnectionString() - { - var builder = new MySqlConnectionStringBuilder - { - Server = Config.DatabaseHost, - UserID = Config.DatabaseUser, - Password = Config.DatabasePassword, - Database = Config.DatabaseName, - Port = (uint)Config.DatabasePort, - }; - DatabaseConnectionString = builder.ConnectionString; + public static WeaponPaintsConfig GetWeaponPaintsConfig() + { + return _config; } - private void TestDatabaseConnection() - { - try - { - using var connection = new MySqlConnection(DatabaseConnectionString); - connection.Open(); - - if (connection.State != System.Data.ConnectionState.Open) - { - throw new Exception("[WeaponPaints] Unable connect to database!"); - } - } - catch (Exception ex) - { - throw new Exception("[WeaponPaints] Unknown mysql exception! " + ex.Message); - } - CheckDatabaseTables(); - } - - async private void CheckDatabaseTables() - { - try - { - using var connection = new MySqlConnection(DatabaseConnectionString); - await connection.OpenAsync(); - - using var transaction = await connection.BeginTransactionAsync(); - - try - { - string createTable1 = "CREATE TABLE IF NOT EXISTS `wp_player_skins` (`steamid` varchar(64) NOT NULL, `weapon_defindex` int(6) NOT NULL, `weapon_paint_id` int(6) NOT NULL, `weapon_wear` float NOT NULL DEFAULT 0.0001, `weapon_seed` int(16) NOT NULL DEFAULT 0) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_unicode_ci"; - string createTable2 = "CREATE TABLE IF NOT EXISTS `wp_player_knife` (`steamid` varchar(64) NOT NULL, `knife` varchar(64) NOT NULL, UNIQUE (`steamid`)) ENGINE = InnoDB"; - - await connection.ExecuteAsync(createTable1, transaction: transaction); - await connection.ExecuteAsync(createTable2, transaction: transaction); - - await transaction.CommitAsync(); - } - catch (Exception) - { - await transaction.RollbackAsync(); - throw new Exception("[WeaponPaints] Unable to create tables!"); - } - } - catch (Exception ex) - { - throw new Exception("[WeaponPaints] Unknown mysql exception! " + ex.Message); - } - } // TODO: fix for map which change mp_t_default_melee /*private HookResult OnRoundPreStart(EventRoundPrestart @event, GameEventInfo info) { @@ -234,52 +169,6 @@ public class WeaponPaints : BasePlugin, IPluginConfig { base.Unload(hotReload); } - private void RegisterCommands() - { - AddCommand($"css_{Config.Additional.CommandSkin}", "Skins info", (player, info) => - { - if (!Utility.IsPlayerValid(player)) return; - OnCommandWS(player, info); - }); - AddCommand($"css_{Config.Additional.CommandRefresh}", "Skins refresh", (player, info) => - { - if (!Utility.IsPlayerValid(player)) return; - OnCommandRefresh(player, info); - }); - if (Config.Additional.CommandKillEnabled) - { - AddCommand($"css_{Config.Additional.CommandKill}", "kill yourself", (player, info) => - { - if (!Utility.IsPlayerValid(player) || !player!.PlayerPawn.IsValid) return; - - player.PlayerPawn.Value.CommitSuicide(true, false); - }); - } - } - private void IncompatibilityCheck() - { - // MatchZy - if (Directory.Exists(Path.GetDirectoryName(ModuleDirectory) + "/MatchZy")) - { - Console.WriteLine("[WeaponPaints] Incompatibility found: MatchZy"); - IsMatchZy = true; - } - } - - private void OnMapStart(string mapName) - { - if (!Config.Additional.KnifeEnabled) return; - // TODO - // needed for now - AddTimer(2.0f, () => - { - NativeAPI.IssueServerCommand("mp_t_default_melee \"\""); - NativeAPI.IssueServerCommand("mp_ct_default_melee \"\""); - IncompatibilityCheck(); - }); - if (Config.GlobalShare) - GlobalShareConnect(); - } private void GlobalShareConnect() { @@ -314,832 +203,4 @@ public class WeaponPaints : BasePlugin, IPluginConfig } Console.WriteLine("[WeaponPaints] GlobalShare ONLINE"); } - - private void OnClientPutInServer(int playerSlot) - { - int playerIndex = playerSlot + 1; - Task.Run(async () => - { - if (Config.Additional.KnifeEnabled) - await GetKnifeFromDatabase(playerIndex); - if (Config.Additional.SkinEnabled) - await GetWeaponPaintsFromDatabase(playerIndex); - }); - } - private void OnClientDisconnect(int playerSlot) - { - CCSPlayerController player = Utilities.GetPlayerFromSlot(playerSlot); - if (!Utility.IsPlayerValid(player)) return; - if (Config.Additional.KnifeEnabled) - g_playersKnife.Remove((int)player.EntityIndex!.Value.Value); - if (Config.Additional.SkinEnabled) - gPlayerWeaponPaints.Remove((int)player.EntityIndex!.Value.Value); - } - - private HookResult OnPlayerSpawn(EventPlayerSpawn @event, GameEventInfo info) - { - CCSPlayerController? player = @event.Userid; - if (player == null || !player.IsValid || !player.PlayerPawn.IsValid) - { - return HookResult.Continue; - } - - if (Config.Additional.KnifeEnabled) - { - if (!PlayerHasKnife(player)) - GiveKnifeToPlayer(player); - } - - if (Config.Additional.SkinVisibilityFix) - { - AddTimer(0.3f, () => RefreshSkins(player)); - } - - return HookResult.Continue; - } - private HookResult OnRoundStart(EventRoundStart @event, GameEventInfo info) - { - /* - if (!IsMatchZy) return HookResult.Continue; - */ - - NativeAPI.IssueServerCommand("mp_t_default_melee \"\""); - NativeAPI.IssueServerCommand("mp_ct_default_melee \"\""); - - return HookResult.Continue; - } - private HookResult OnItemPickup(EventItemPickup @event, GameEventInfo info) - { - if (@event.Defindex == 42 || @event.Defindex == 59) - { - CCSPlayerController? player = @event.Userid; - if (!Utility.IsPlayerValid(player) || !player.PawnIsAlive) return HookResult.Continue; - - if (g_playersKnife.ContainsKey((int)player.EntityIndex!.Value.Value) - && - g_playersKnife[(int)player.EntityIndex!.Value.Value] != "weapon_knife") - { - if (PlayerHasKnife(player)) - RemoveKnifeFromPlayer(player); - - AddTimer(0.3f, () => - { - if (!PlayerHasKnife(player)) - GiveKnifeToPlayer(player); - }); - - if (Config.Additional.SkinVisibilityFix) - { - AddTimer(0.25f, () => RefreshSkins(player)); - } - } - } - return HookResult.Continue; - } - - private void OnEntitySpawned(CEntityInstance entity) - { - if (!Config.Additional.SkinEnabled) return; - var designerName = entity.DesignerName; - if (!weaponList.ContainsKey(designerName)) return; - bool isKnife = false; - var weapon = new CBasePlayerWeapon(entity.Handle); - - if (designerName.Contains("knife") || designerName.Contains("bayonet")) - { - isKnife = true; - } - Server.NextFrame(() => - { - try - { - if (!weapon.IsValid) return; - if (weapon.OwnerEntity.Value == null) return; - if (!weapon.OwnerEntity.Value.EntityIndex.HasValue) - { - for (int i = 1; i <= Server.MaxPlayers; i++) - { - CCSPlayerController? ghostPlayer = Utilities.GetPlayerFromIndex(i); - if (!Utility.IsPlayerValid(ghostPlayer)) continue; - if (g_changedKnife.Contains((int)ghostPlayer.EntityIndex!.Value.Value)) - { - ChangeWeaponAttributes(weapon, ghostPlayer, isKnife); - g_changedKnife.Remove((int)ghostPlayer.EntityIndex!.Value.Value); - break; - } - } - return; - } - - if (!weapon.OwnerEntity.Value.EntityIndex.HasValue) return; - int weaponOwner = (int)weapon.OwnerEntity.Value.EntityIndex.Value.Value; - var pawn = new CBasePlayerPawn(NativeAPI.GetEntityFromIndex(weaponOwner)); - if (!pawn.IsValid) return; - var playerIndex = (int)pawn.Controller.Value.EntityIndex!.Value.Value; - var player = Utilities.GetPlayerFromIndex(playerIndex); - if (!Utility.IsPlayerValid(player)) return; - - // TODO: Remove knife crashes here, needs another solution - /*if (isKnife && g_playersKnife[(int)player.EntityIndex!.Value.Value] != "weapon_knife" && (weapon.AttributeManager.Item.ItemDefinitionIndex == 42 || weapon.AttributeManager.Item.ItemDefinitionIndex == 59)) - { - RemoveKnifeFromPlayer(player); - return; - }*/ - ChangeWeaponAttributes(weapon, player, isKnife); - } - catch (Exception) { } - }); - } - private void ChangeWeaponAttributes(CBasePlayerWeapon? weapon, CCSPlayerController? player, bool isKnife = false) - { - if (weapon == null || !weapon.IsValid || !Utility.IsPlayerValid(player)) return; - - int playerIndex = (int)player!.EntityIndex!.Value.Value; - if (!gPlayerWeaponPaints.ContainsKey(playerIndex)) return; - - if (isKnife && !g_playersKnife.ContainsKey(playerIndex) || isKnife && g_playersKnife[playerIndex] == "weapon_knife") return; - - if (Config.Additional.GiveRandomSkin && - !gPlayerWeaponPaints[playerIndex].ContainsKey(weapon.AttributeManager.Item.ItemDefinitionIndex)) - { - // Random skins - weapon.AttributeManager.Item.ItemID = 16384; - weapon.AttributeManager.Item.ItemIDLow = 16384 & 0xFFFFFFFF; - weapon.AttributeManager.Item.ItemIDHigh = weapon.AttributeManager.Item.ItemIDLow >> 32; - weapon.FallbackPaintKit = GetRandomPaint(weapon.AttributeManager.Item.ItemDefinitionIndex); - weapon.FallbackSeed = 0; - weapon.FallbackWear = 0.0f; - if (!isKnife && weapon.CBodyComponent != null && weapon.CBodyComponent.SceneNode != null) - { - var skeleton = GetSkeletonInstance(weapon.CBodyComponent.SceneNode); - skeleton.ModelState.MeshGroupMask = 2; - } - return; - } - - if (!gPlayerWeaponPaints[playerIndex].ContainsKey(weapon.AttributeManager.Item.ItemDefinitionIndex)) return; - //Log($"Apply on {weapon.DesignerName}({weapon.AttributeManager.Item.ItemDefinitionIndex}) paint {gPlayerWeaponPaints[steamId.SteamId64][weapon.AttributeManager.Item.ItemDefinitionIndex]} seed {gPlayerWeaponSeed[steamId.SteamId64][weapon.AttributeManager.Item.ItemDefinitionIndex]} wear {gPlayerWeaponWear[steamId.SteamId64][weapon.AttributeManager.Item.ItemDefinitionIndex]}"); - weapon.AttributeManager.Item.ItemID = 16384; - weapon.AttributeManager.Item.ItemIDLow = 16384 & 0xFFFFFFFF; - weapon.AttributeManager.Item.ItemIDHigh = weapon.AttributeManager.Item.ItemIDLow >> 32; - weapon.FallbackPaintKit = gPlayerWeaponPaints[playerIndex][weapon.AttributeManager.Item.ItemDefinitionIndex]; - weapon.FallbackSeed = gPlayerWeaponSeed[playerIndex][weapon.AttributeManager.Item.ItemDefinitionIndex]; - weapon.FallbackWear = gPlayerWeaponWear[playerIndex][weapon.AttributeManager.Item.ItemDefinitionIndex]; - if (!isKnife && weapon.CBodyComponent != null && weapon.CBodyComponent.SceneNode != null) - { - var skeleton = GetSkeletonInstance(weapon.CBodyComponent.SceneNode); - skeleton.ModelState.MeshGroupMask = 2; - } - } - - private HookResult OnEventItemPurchasePost(EventItemPurchase @event, GameEventInfo info) - { - CCSPlayerController? player = @event.Userid; - - if (player == null || !player.IsValid) return HookResult.Continue; - - if (Config.Additional.SkinVisibilityFix) - AddTimer(0.2f, () => RefreshSkins(player)); - - return HookResult.Continue; - } - private void GiveKnifeToPlayer(CCSPlayerController? player) - { - if (!Config.Additional.KnifeEnabled || player == null || !player.IsValid) return; - - if (g_playersKnife.TryGetValue((int)player.EntityIndex!.Value.Value, out var knife)) - { - player.GiveNamedItem(knife); - } - else if (Config.Additional.GiveRandomKnife) - { - var knifeTypes = weaponList.Where(pair => pair.Key.StartsWith("weapon_knife") || pair.Key.StartsWith("weapon_bayonet")).ToDictionary(pair => pair.Key, pair => pair.Value); - - Random random = new(); - int index = random.Next(knifeTypes.Count); - var randomKnifeClass = knifeTypes.Keys.ElementAt(index); - - player.GiveNamedItem(randomKnifeClass); - } - else - { - var defaultKnife = (CsTeam)player.TeamNum == CsTeam.Terrorist ? "weapon_knife_t" : "weapon_knife"; - player.GiveNamedItem(defaultKnife); - } - } - private void RemoveKnifeFromPlayer(CCSPlayerController? player) - { - if (player == null || !player.IsValid || !player.PawnIsAlive) return; - if (player.PlayerPawn.Value.WeaponServices == null || player.PlayerPawn.Value.ItemServices == null) return; - - var weapons = player.PlayerPawn.Value.WeaponServices.MyWeapons; - if (weapons != null && weapons.Count > 0) - { - CCSPlayer_ItemServices service = new CCSPlayer_ItemServices(player.PlayerPawn.Value.ItemServices.Handle); - //var dropWeapon = VirtualFunction.CreateVoid(service.Handle, GameData.GetOffset("CCSPlayer_ItemServices_DropActivePlayerWeapon")); - - foreach (var weapon in weapons) - { - if (weapon != null && weapon.IsValid && weapon.Value.IsValid) - { - //if (weapon.Value.AttributeManager.Item.ItemDefinitionIndex == 42 || weapon.Value.AttributeManager.Item.ItemDefinitionIndex == 59) - if (weapon.Value.DesignerName.Contains("knife") || weapon.Value.DesignerName.Contains("bayonet")) - { - NativeAPI.IssueClientCommand((int)player.EntityIndex!.Value.Value - 1, "slot3"); - AddTimer(0.5f, () => service.DropActivePlayerWeapon(weapon.Value)); - - /* - CEntityInstance knife = new(weapon.Value.Handle); - AddTimer(1.0f, () => { - knife.Remove(); - if (knife != null && knife.IsValid && player.PawnIsAlive) - }); - */ - - break; - } - } - } - } - } - /* Causing crashes - private void RefreshPlayerKnife(CCSPlayerController? player, bool remove = false) - { - if (player == null || !player.IsValid || player.IsBot || !player.PawnIsAlive) return; - - AddTimer(0.1f, () => - { - if (remove == true) - { - if (PlayerHasKnife(player)) - RemoveKnifeFromPlayer(player); - } - - GiveKnifeToPlayer(player); - }); - - if (Config.Additional.SkinVisibilityFix) - { - AddTimer(0.25f, () => RefreshSkins(player)); - } - } - */ - private void RefreshSkins(CCSPlayerController? player) - { - if (!Utility.IsPlayerValid(player) || !player!.PawnIsAlive) return; - - AddTimer(0.18f, () => NativeAPI.IssueClientCommand((int)player.EntityIndex!.Value.Value - 1, "slot3")); - AddTimer(0.25f, () => NativeAPI.IssueClientCommand((int)player.EntityIndex!.Value.Value - 1, "slot2")); - AddTimer(0.38f, () => NativeAPI.IssueClientCommand((int)player.EntityIndex!.Value.Value - 1, "slot1")); - } - private bool PlayerHasKnife(CCSPlayerController? player) - { - if (!Config.Additional.KnifeEnabled) return false; - - if (player == null || !player.IsValid || !player.PawnIsAlive) - { - return false; - } - - var weapons = player.PlayerPawn.Value.WeaponServices!.MyWeapons; - if (weapons == null || weapons.Count <= 0) return false; - foreach (var weapon in weapons) - { - if (weapon != null && weapon.IsValid && weapon.Value.IsValid) - { - if (weapon.Value.DesignerName.Contains("knife") || weapon.Value.DesignerName.Contains("bayonet")) - { - return true; - } - } - } - return false; - } - private void SetupKnifeMenu() - { - if (!Config.Additional.KnifeEnabled) return; - - var knivesOnly = weaponList - .Where(pair => pair.Key.StartsWith("weapon_knife") || pair.Key.StartsWith("weapon_bayonet")) - .ToDictionary(pair => pair.Key, pair => pair.Value); - - var giveItemMenu = new ChatMenu(Utility.ReplaceTags($" {Config.Messages.KnifeMenuTitle}")); - var handleGive = (CCSPlayerController? player, ChatMenuOption option) => - { - if (Utility.IsPlayerValid(player)) - { - var knifeName = option.Text; - var knifeKey = knivesOnly.FirstOrDefault(x => x.Value == knifeName).Key; - if (!string.IsNullOrEmpty(knifeKey)) - { - string temp = ""; - - if (!string.IsNullOrEmpty(Config.Messages.ChosenKnifeMenu)) - { - temp = $" {Config.Prefix} {Config.Messages.ChosenKnifeMenu}".Replace("{KNIFE}", knifeName); - player!.PrintToChat(Utility.ReplaceTags(temp)); - } - - if (!string.IsNullOrEmpty(Config.Messages.ChosenKnifeMenuKill) && Config.Additional.CommandKillEnabled) - { - temp = $" {Config.Prefix} {Config.Messages.ChosenKnifeMenuKill}"; - player!.PrintToChat(Utility.ReplaceTags(temp)); - } - - g_playersKnife[(int)player!.EntityIndex!.Value.Value] = knifeKey; - - if (player!.PawnIsAlive) - { - if (PlayerHasKnife(player)) - { - RemoveKnifeFromPlayer(player); - } - - g_changedKnife.Add((int)player.EntityIndex!.Value.Value); - GiveKnifeToPlayer(player); - } - - Task.Run(() => SyncKnifeToDatabase((int)player.EntityIndex!.Value.Value, knifeKey)); - - } - } - }; - foreach (var knifePair in knivesOnly) - { - giveItemMenu.AddMenuOption(knifePair.Value, handleGive); - } - AddCommand($"css_{Config.Additional.CommandKnife}", "Knife Menu", (player, info) => - { - if (!Utility.IsPlayerValid(player)) return; - int playerIndex = (int)player!.EntityIndex!.Value.Value; - - if (commandCooldown != null && DateTime.UtcNow >= commandCooldown[playerIndex].AddSeconds(Config.CmdRefreshCooldownSeconds) && playerIndex > 0 && playerIndex < commandCooldown.Length) - { - commandCooldown[playerIndex] = DateTime.UtcNow; - ChatMenus.OpenMenu(player, giveItemMenu); - return; - } - if (!string.IsNullOrEmpty(Config.Messages.CooldownRefreshCommand)) - { - string temp = $" {Config.Prefix} {Config.Messages.CooldownRefreshCommand}"; - player.PrintToChat(Utility.ReplaceTags(temp)); - } - }); - } - - private void SetupSkinsMenu() - { - var classNamesByWeapon = weaponList.ToDictionary(kvp => kvp.Value, kvp => kvp.Key); - var weaponSelectionMenu = new ChatMenu(Utility.ReplaceTags($" {Config.Messages.WeaponMenuTitle}")); - - // Function to handle skin selection for a specific weapon - var handleWeaponSelection = (CCSPlayerController? player, ChatMenuOption option) => - { - if (!Utility.IsPlayerValid(player)) return; - - int playerIndex = (int)player!.EntityIndex!.Value.Value; - string selectedWeapon = option.Text; - if (classNamesByWeapon.TryGetValue(selectedWeapon, out string? selectedWeaponClassname)) - { - if (selectedWeaponClassname == null) return; - var skinsForSelectedWeapon = skinsList?.Where(skin => - skin != null && - skin.TryGetValue("weapon_name", out var weaponName) && - weaponName?.ToString() == selectedWeaponClassname - )?.ToList(); - - var skinSubMenu = new ChatMenu(Utility.ReplaceTags($" {Config.Messages.SkinMenuTitle}").Replace("{WEAPON}", selectedWeapon)); - - // Function to handle skin selection for the chosen weapon - var handleSkinSelection = (CCSPlayerController? p, ChatMenuOption opt) => - { - if (p == null || !p.IsValid) return; - - var steamId = new SteamID(player.SteamID); - var firstSkin = skinsList?.FirstOrDefault(skin => - { - if (skin != null && skin.TryGetValue("weapon_name", out var weaponName)) - { - return weaponName?.ToString() == selectedWeaponClassname; - } - return false; - }); - string selectedSkin = opt.Text; - string selectedPaintID = selectedSkin.Split('(')[1].Trim(')').Trim(); - - if (firstSkin != null && - firstSkin.TryGetValue("weapon_defindex", out var weaponDefIndexObj) && - weaponDefIndexObj != null && - int.TryParse(weaponDefIndexObj.ToString(), out var weaponDefIndex) && - int.TryParse(selectedPaintID, out var paintID)) - { - string temp = $" {Config.Prefix} {Config.Messages.ChosenSkinMenu}".Replace("{SKIN}", selectedSkin); - p.PrintToChat(Utility.ReplaceTags(temp)); - gPlayerWeaponPaints[playerIndex][weaponDefIndex] = paintID; - gPlayerWeaponWear[playerIndex][weaponDefIndex] = 0.0f; - gPlayerWeaponSeed[playerIndex][weaponDefIndex] = 0; - - Task.Run(async () => - { - await SyncWeaponPaintsToDatabase(player); - }); - } - }; - - // Add skin options to the submenu for the selected weapon - if (skinsForSelectedWeapon != null) - { - foreach (var skin in skinsForSelectedWeapon.Where(s => s != null)) - { - if (skin.TryGetValue("paint_name", out var paintNameObj) && skin.TryGetValue("paint", out var paintObj)) - { - var paintName = paintNameObj?.ToString(); - var paint = paintObj?.ToString(); - - if (!string.IsNullOrEmpty(paintName) && !string.IsNullOrEmpty(paint)) - { - skinSubMenu.AddMenuOption($"{paintName} ({paint})", handleSkinSelection); - } - } - } - } - - // Open the submenu for skin selection of the chosen weapon - ChatMenus.OpenMenu(player, skinSubMenu); - } - }; - - // Add weapon options to the weapon selection menu - foreach (var weaponClass in weaponList.Keys) - { - string weaponName = weaponList[weaponClass]; - weaponSelectionMenu.AddMenuOption(weaponName, handleWeaponSelection); - } - // Command to open the weapon selection menu for players - AddCommand($"css_{Config.Additional.CommandSkinSelection}", "Skins selection menu", (player, info) => - { - if (!Utility.IsPlayerValid(player)) return; - int playerIndex = (int)player!.EntityIndex!.Value.Value; - - if (commandCooldown != null && DateTime.UtcNow >= commandCooldown[playerIndex].AddSeconds(Config.CmdRefreshCooldownSeconds) && playerIndex > 0 && playerIndex < commandCooldown.Length) - { - commandCooldown[playerIndex] = DateTime.UtcNow; - ChatMenus.OpenMenu(player, weaponSelectionMenu); - return; - } - if (!string.IsNullOrEmpty(Config.Messages.CooldownRefreshCommand)) - { - string temp = $"{Config.Prefix} {Config.Messages.CooldownRefreshCommand}"; - player.PrintToChat(Utility.ReplaceTags(temp)); - } - - }); - } - - // [ConsoleCommand($"css_{Config.Additional.CommandRefresh}", "refreshskins")] - private void OnCommandRefresh(CCSPlayerController? player, CommandInfo command) - { - if (!Config.Additional.CommandWpEnabled || !Config.Additional.SkinEnabled) return; - if (!Utility.IsPlayerValid(player)) return; - - string temp = ""; - int playerIndex = (int)player!.EntityIndex!.Value.Value; - if (commandCooldown != null && DateTime.UtcNow >= commandCooldown[playerIndex].AddSeconds(Config.CmdRefreshCooldownSeconds)) - { - commandCooldown[playerIndex] = DateTime.UtcNow; - Task.Run(async () => await GetWeaponPaintsFromDatabase(playerIndex)); - if (Config.Additional.KnifeEnabled) - { - if (PlayerHasKnife(player)) - RemoveKnifeFromPlayer(player); - AddTimer(0.3f, () => - { - GiveKnifeToPlayer(player); - }); - - Task.Run(async () => await GetKnifeFromDatabase(playerIndex)); - /* - RemoveKnifeFromPlayer(player); - AddTimer(0.2f, () => GiveKnifeToPlayer(player)); - */ - } - if (!string.IsNullOrEmpty(Config.Messages.SuccessRefreshCommand)) - { - temp = $" {Config.Prefix} {Config.Messages.SuccessRefreshCommand}"; - player.PrintToChat(Utility.ReplaceTags(temp)); - } - return; - } - if (!string.IsNullOrEmpty(Config.Messages.CooldownRefreshCommand)) - { - temp = $" {Config.Prefix} {Config.Messages.CooldownRefreshCommand}"; - player.PrintToChat(Utility.ReplaceTags(temp)); - } - } - // [ConsoleCommand($"css_{Config.Additional.CommandSkin}", "weaponskins")] - private void OnCommandWS(CCSPlayerController? player, CommandInfo command) - { - if (!Config.Additional.SkinEnabled) return; - if (!Utility.IsPlayerValid(player)) return; - - string temp = ""; - - if (!string.IsNullOrEmpty(Config.Messages.WebsiteMessageCommand)) - { - temp = $" {Config.Prefix} {Config.Messages.WebsiteMessageCommand}"; - player!.PrintToChat(Utility.ReplaceTags(temp)); - } - if (!string.IsNullOrEmpty(Config.Messages.SynchronizeMessageCommand)) - { - temp = $" {Config.Prefix} {Config.Messages.SynchronizeMessageCommand}"; - player!.PrintToChat(Utility.ReplaceTags(temp)); - } - if (!Config.Additional.KnifeEnabled) return; - if (!string.IsNullOrEmpty(Config.Messages.KnifeMessageCommand)) - { - temp = $" {Config.Prefix} {Config.Messages.KnifeMessageCommand}"; - player!.PrintToChat(Utility.ReplaceTags(temp)); - } - } - private static CSkeletonInstance GetSkeletonInstance(CGameSceneNode node) - { - Func GetSkeletonInstance = VirtualFunction.Create(node.Handle, 8); - return new CSkeletonInstance(GetSkeletonInstance(node.Handle)); - } - private async Task GetWeaponPaintsFromDatabase(int playerIndex) - { - if (!Config.Additional.SkinEnabled) return; - - CCSPlayerController player = Utilities.GetPlayerFromIndex(playerIndex); - if (!Utility.IsPlayerValid(player)) return; - - var steamId = new SteamID(player.SteamID); - - gPlayerWeaponPaints[playerIndex] = new Dictionary(); - gPlayerWeaponWear[playerIndex] = new Dictionary(); - gPlayerWeaponSeed[playerIndex] = new Dictionary(); - - try - { - if (Config.GlobalShare) - { - var values = new Dictionary - { - { "server_id", GlobalShareServerId.ToString() }, - { "steamid", steamId.SteamId64.ToString() }, - { "skins", "1" } - }; - UriBuilder builder = new UriBuilder(GlobalShareApi); - builder.Query = string.Join("&", values.Select(p => $"{Uri.EscapeDataString(p.Key)}={Uri.EscapeDataString(p.Value)}")); - - using (var httpClient = new HttpClient()) - { - httpClient.BaseAddress = GlobalShareApi; - var formContent = new FormUrlEncodedContent(values); - HttpResponseMessage response = await httpClient.GetAsync(builder.Uri); - - if (response.IsSuccessStatusCode) - { - string responseBody = await response.Content.ReadAsStringAsync(); - JArray jsonArray = JArray.Parse(responseBody); - if (jsonArray != null && jsonArray.Count > 0) - { - foreach (var weapon in jsonArray) - { - int? weaponDefIndex = weapon["weapon_defindex"]?.Value(); - int? weaponPaintId = weapon["weapon_paint_id"]?.Value(); - float? weaponWear = weapon["weapon_wear"]?.Value(); - int? weaponSeed = weapon["weapon_seed"]?.Value(); - - if (weaponDefIndex != null && weaponPaintId != null && weaponWear != null && weaponSeed != null) - { - gPlayerWeaponPaints[playerIndex][weaponDefIndex.Value] = weaponPaintId.Value; - gPlayerWeaponWear[playerIndex][weaponDefIndex.Value] = weaponWear.Value; - gPlayerWeaponSeed[playerIndex][weaponDefIndex.Value] = weaponSeed.Value; - } - } - } - return; - } - else - { - return; - } - } - } - - using (var connection = new MySqlConnection(DatabaseConnectionString)) - { - await connection.OpenAsync(); - - string query = "SELECT * FROM `wp_player_skins` WHERE `steamid` = @steamid"; - - IEnumerable PlayerSkins = await connection.QueryAsync(query, new { steamid = steamId.SteamId64.ToString() }); - - if (PlayerSkins != null && PlayerSkins.AsList().Count > 0) - { - PlayerSkins.ToList().ForEach(row => - { - int weaponDefIndex = row.weapon_defindex ?? default(int); - int weaponPaintId = row.weapon_paint_id ?? default(int); - float weaponWear = row.weapon_wear ?? default(float); - int weaponSeed = row.weapon_seed ?? default(int); - - gPlayerWeaponPaints[playerIndex][weaponDefIndex] = weaponPaintId; - gPlayerWeaponWear[playerIndex][weaponDefIndex] = weaponWear; - gPlayerWeaponSeed[playerIndex][weaponDefIndex] = weaponSeed; - }); - } - else - { - return; - } - await connection.CloseAsync(); - } - } - catch (Exception e) - { - Utility.Log(e.Message); - return; - } - } - private async Task GetKnifeFromDatabase(int playerIndex) - { - if (!Config.Additional.KnifeEnabled) return; - try - { - CCSPlayerController player = Utilities.GetPlayerFromIndex(playerIndex); - if (!Utility.IsPlayerValid(player)) return; - var steamId = new SteamID(player.SteamID); - - if (Config.GlobalShare) - { - var values = new Dictionary - { - { "server_id", GlobalShareServerId.ToString() }, - { "steamid", steamId.SteamId64.ToString() }, - { "knife", "1" } - }; - UriBuilder builder = new UriBuilder(GlobalShareApi); - builder.Query = string.Join("&", values.Select(p => $"{Uri.EscapeDataString(p.Key)}={Uri.EscapeDataString(p.Value)}")); - - using (var httpClient = new HttpClient()) - { - httpClient.BaseAddress = GlobalShareApi; - var formContent = new FormUrlEncodedContent(values); - HttpResponseMessage response = await httpClient.GetAsync(builder.Uri); - - if (response.IsSuccessStatusCode) - { - string result = await response.Content.ReadAsStringAsync(); - if (!string.IsNullOrEmpty(result)) - { - g_playersKnife[playerIndex] = result; - } - else - { - return; - } - - } - else - { - return; - } - } - return; - } - - using (var connection = new MySqlConnection(DatabaseConnectionString)) - { - await connection.OpenAsync(); - string query = "SELECT `knife` FROM `wp_player_knife` WHERE `steamid` = @steamid"; - string? PlayerKnife = await connection.QueryFirstOrDefaultAsync(query, new { steamid = steamId.SteamId64.ToString() }); - - if (PlayerKnife != null) - { - g_playersKnife[playerIndex] = PlayerKnife; - } - else - { - return; - } - await connection.CloseAsync(); - } - //Log($"{player.PlayerName} has this knife -> {g_playersKnife[playerIndex]}"); - } - catch (Exception e) - { - Utility.Log(e.Message); - return; - } - } - private async Task SyncKnifeToDatabase(int playerIndex, string knife) - { - if (!Config.Additional.KnifeEnabled) return; - try - { - CCSPlayerController player = Utilities.GetPlayerFromIndex(playerIndex); - if (player == null || !player.IsValid) return; - var steamId = new SteamID(player.SteamID); - - using var connection = new MySqlConnection(DatabaseConnectionString); - await connection.OpenAsync(); - string query = "INSERT INTO `wp_player_knife` (`steamid`, `knife`) VALUES(@steamid, @newKnife) ON DUPLICATE KEY UPDATE `knife` = @newKnife"; - await connection.ExecuteAsync(query, new { steamid = steamId.SteamId64.ToString(), newKnife = knife }); - await connection.CloseAsync(); - } - catch (Exception e) - { - Utility.Log(e.Message); - return; - } - } - - private async Task SyncWeaponPaintsToDatabase(CCSPlayerController? player) - { - if (!Utility.IsPlayerValid(player)) return; - - int playerIndex = (int)player!.EntityIndex!.Value.Value; - - string steamId = new SteamID(player.SteamID).SteamId64.ToString(); - - using var connection = new MySqlConnection(DatabaseConnectionString); - await connection.OpenAsync(); - - if (!gPlayerWeaponPaints.ContainsKey(playerIndex)) - return; - - foreach (var weaponDefIndex in gPlayerWeaponPaints[playerIndex].Keys) - { - int paintId = gPlayerWeaponPaints[playerIndex][weaponDefIndex]; - float wear = gPlayerWeaponWear.TryGetValue(playerIndex, out var wearDictionary) - && wearDictionary.TryGetValue(weaponDefIndex, out var retrievedWear) - ? retrievedWear - : 0.0f; - - // Assigning values for gPlayerWeaponSeed - int seed = gPlayerWeaponSeed.TryGetValue(playerIndex, out var seedDictionary) - && seedDictionary.TryGetValue(weaponDefIndex, out var retrievedSeed) - ? retrievedSeed - : 0; - - string updateSql = "UPDATE `wp_player_skins` SET `weapon_paint_id` = @paintId, " + - "`weapon_wear` = @wear, `weapon_seed` = @seed WHERE `steamid` = @steamid " + - "AND `weapon_defindex` = @weaponDefIndex"; - - var updateParams = new { paintId, wear, seed, steamid = steamId, weaponDefIndex }; - int rowsAffected = await connection.ExecuteAsync(updateSql, updateParams); - - if (rowsAffected == 0) - { - string insertSql = "INSERT INTO `wp_player_skins` (`steamid`, `weapon_defindex`, " + - "`weapon_paint_id`, `weapon_wear`, `weapon_seed`) " + - "VALUES (@steamid, @weaponDefIndex, @paintId, @wear, @seed)"; - - await connection.ExecuteAsync(insertSql, updateParams); - } - } - await connection.CloseAsync(); - } - - private static int GetRandomPaint(int defindex) - { - Random rnd = new Random(); - - if (skinsList != null) - { - // Filter weapons by the provided defindex - var filteredWeapons = skinsList.FindAll(w => w["weapon_defindex"]?.ToString() == defindex.ToString()); - - if (filteredWeapons.Count > 0) - { - var randomWeapon = filteredWeapons[rnd.Next(filteredWeapons.Count)]; - if (int.TryParse(randomWeapon["paint"]?.ToString(), out int paintValue)) - { - return paintValue; - } - else - { - return 0; - } - - } - } - return 0; - } - - private static void LoadSkinsFromFile(string filePath) - { - if (File.Exists(filePath)) - { - string json = File.ReadAllText(filePath); - var deserializedSkins = JsonConvert.DeserializeObject>(json); - skinsList = deserializedSkins ?? new List(); - } - else - { - throw new FileNotFoundException("File not found.", filePath); - } - } -} +} \ No newline at end of file diff --git a/WeaponSynchronization.cs b/WeaponSynchronization.cs new file mode 100644 index 00000000..560d425f --- /dev/null +++ b/WeaponSynchronization.cs @@ -0,0 +1,272 @@ +using CounterStrikeSharp.API; +using CounterStrikeSharp.API.Core; +using CounterStrikeSharp.API.Modules.Entities; +using Dapper; +using MySqlConnector; +using Newtonsoft.Json.Linq; + +namespace WeaponPaints +{ + internal class WeaponSynchronization + { + private readonly string _databaseConnectionString; + private readonly WeaponPaintsConfig _config; + + private readonly Uri _globalShareApi; + private readonly int _globalShareServerId; + + + internal WeaponSynchronization(string databaseConnectionString, WeaponPaintsConfig config, Uri globalShareApi, int globalShareServerId) + { + _databaseConnectionString = databaseConnectionString; + _config = config; + _globalShareApi = globalShareApi; + _globalShareServerId = globalShareServerId; + } + + internal async Task GetKnifeFromDatabase(int playerIndex) + { + if (!_config.Additional.KnifeEnabled) return; + try + { + CCSPlayerController player = Utilities.GetPlayerFromIndex(playerIndex); + if (!Utility.IsPlayerValid(player)) return; + var steamId = new SteamID(player.SteamID); + + if (_config.GlobalShare) + { + var values = new Dictionary + { + { "server_id", _globalShareServerId.ToString() }, + { "steamid", steamId.SteamId64.ToString() }, + { "knife", "1" } + }; + UriBuilder builder = new UriBuilder(_globalShareApi); + builder.Query = string.Join("&", values.Select(p => $"{Uri.EscapeDataString(p.Key)}={Uri.EscapeDataString(p.Value)}")); + + using (var httpClient = new HttpClient()) + { + httpClient.BaseAddress = _globalShareApi; + var formContent = new FormUrlEncodedContent(values); + HttpResponseMessage response = await httpClient.GetAsync(builder.Uri); + + if (response.IsSuccessStatusCode) + { + string result = await response.Content.ReadAsStringAsync(); + if (!string.IsNullOrEmpty(result)) + { + WeaponPaints.g_playersKnife[playerIndex] = result; + } + else + { + return; + } + + } + else + { + return; + } + } + return; + } + + using (var connection = new MySqlConnection(_databaseConnectionString)) + { + await connection.OpenAsync(); + string query = "SELECT `knife` FROM `wp_player_knife` WHERE `steamid` = @steamid"; + string? PlayerKnife = await connection.QueryFirstOrDefaultAsync(query, new { steamid = steamId.SteamId64.ToString() }); + if (PlayerKnife != null) + { + WeaponPaints.g_playersKnife[playerIndex] = PlayerKnife; + } + else + { + return; + } + await connection.CloseAsync(); + } + //Log($"{player.PlayerName} has this knife -> {g_playersKnife[playerIndex]}"); + } + catch (Exception e) + { + Utility.Log(e.Message); + return; + } + } + + internal async Task SyncKnifeToDatabase(int playerIndex, string knife) + { + if (!_config.Additional.KnifeEnabled) return; + try + { + CCSPlayerController player = Utilities.GetPlayerFromIndex(playerIndex); + if (player == null || !player.IsValid) return; + var steamId = new SteamID(player.SteamID); + + using var connection = new MySqlConnection(_databaseConnectionString); + await connection.OpenAsync(); + string query = "INSERT INTO `wp_player_knife` (`steamid`, `knife`) VALUES(@steamid, @newKnife) ON DUPLICATE KEY UPDATE `knife` = @newKnife"; + await connection.ExecuteAsync(query, new { steamid = steamId.SteamId64.ToString(), newKnife = knife }); + await connection.CloseAsync(); + } + catch (Exception e) + { + Utility.Log(e.Message); + return; + } + } + + internal async Task GetWeaponPaintsFromDatabase(int playerIndex) + { + if (!_config.Additional.SkinEnabled) return; + + CCSPlayerController player = Utilities.GetPlayerFromIndex(playerIndex); + if (!Utility.IsPlayerValid(player)) return; + + + var steamId = new SteamID(player.SteamID); + + if (!WeaponPaints.gPlayerWeaponsInfo.ContainsKey(playerIndex)) + { + WeaponPaints.gPlayerWeaponsInfo[playerIndex] = new Dictionary(); + } + try + { + if (_config.GlobalShare) + { + var values = new Dictionary + { + { "server_id", _globalShareServerId.ToString() }, + { "steamid", steamId.SteamId64.ToString() }, + { "skins", "1" } + }; + UriBuilder builder = new UriBuilder(_globalShareApi); + builder.Query = string.Join("&", values.Select(p => $"{Uri.EscapeDataString(p.Key)}={Uri.EscapeDataString(p.Value)}")); + + using (var httpClient = new HttpClient()) + { + httpClient.BaseAddress = _globalShareApi; + var formContent = new FormUrlEncodedContent(values); + HttpResponseMessage response = await httpClient.GetAsync(builder.Uri); + + if (response.IsSuccessStatusCode) + { + string responseBody = await response.Content.ReadAsStringAsync(); + JArray jsonArray = JArray.Parse(responseBody); + if (jsonArray != null && jsonArray.Count > 0) + { + foreach (var weapon in jsonArray) + { + int? weaponDefIndex = weapon["weapon_defindex"]?.Value(); + int? weaponPaintId = weapon["weapon_paint_id"]?.Value(); + float? weaponWear = weapon["weapon_wear"]?.Value(); + int? weaponSeed = weapon["weapon_seed"]?.Value(); + + if (weaponDefIndex != null && weaponPaintId != null && weaponWear != null && weaponSeed != null) + { + WeaponInfo weaponInfo = new WeaponInfo + { + Paint = weaponPaintId.Value, // Example paint value + Seed = weaponSeed.Value, // Example seed value + Wear = weaponWear.Value // Example wear value + }; + WeaponPaints.gPlayerWeaponsInfo[playerIndex][weaponDefIndex.Value] = weaponInfo; + /* + gPlayerWeaponPaints[playerIndex][weaponDefIndex.Value] = weaponPaintId.Value; + gPlayerWeaponWear[playerIndex][weaponDefIndex.Value] = weaponWear.Value; + gPlayerWeaponSeed[playerIndex][weaponDefIndex.Value] = weaponSeed.Value; + */ + } + } + } + return; + } + else + { + return; + } + } + } + + using (var connection = new MySqlConnection(_databaseConnectionString)) + { + await connection.OpenAsync(); + + string query = "SELECT * FROM `wp_player_skins` WHERE `steamid` = @steamid"; + IEnumerable PlayerSkins = await connection.QueryAsync(query, new { steamid = steamId.SteamId64.ToString() }); + + if (PlayerSkins != null && PlayerSkins.AsList().Count > 0) + { + PlayerSkins.ToList().ForEach(row => + { + int weaponDefIndex = row.weapon_defindex ?? default(int); + int weaponPaintId = row.weapon_paint_id ?? default(int); + float weaponWear = row.weapon_wear ?? default(float); + int weaponSeed = row.weapon_seed ?? default(int); + + WeaponInfo weaponInfo = new WeaponInfo + { + Paint = weaponPaintId, + Seed = weaponSeed, + Wear = weaponWear + }; + WeaponPaints.gPlayerWeaponsInfo[playerIndex][weaponDefIndex] = weaponInfo; + }); + } + else + { + return; + } + await connection.CloseAsync(); + } + } + catch (Exception e) + { + Utility.Log(e.Message); + return; + } + } + + internal async Task SyncWeaponPaintsToDatabase(CCSPlayerController? player) + { + if (!Utility.IsPlayerValid(player)) return; + + int playerIndex = (int)player!.EntityIndex!.Value.Value; + string steamId = new SteamID(player.SteamID).SteamId64.ToString(); + + using var connection = new MySqlConnection(_databaseConnectionString); + await connection.OpenAsync(); + + if (!WeaponPaints.gPlayerWeaponsInfo.ContainsKey(playerIndex)) + return; + + foreach (var weaponInfoPair in WeaponPaints.gPlayerWeaponsInfo[playerIndex]) + { + int weaponDefIndex = weaponInfoPair.Key; + WeaponInfo weaponInfo = weaponInfoPair.Value; + + int paintId = weaponInfo.Paint; + float wear = weaponInfo.Wear; + int seed = weaponInfo.Seed; + + string updateSql = "UPDATE `wp_player_skins` SET `weapon_paint_id` = @paintId, " + + "`weapon_wear` = @wear, `weapon_seed` = @seed WHERE `steamid` = @steamid " + + "AND `weapon_defindex` = @weaponDefIndex"; + + var updateParams = new { paintId, wear, seed, steamid = steamId, weaponDefIndex }; + int rowsAffected = await connection.ExecuteAsync(updateSql, updateParams); + + if (rowsAffected == 0) + { + string insertSql = "INSERT INTO `wp_player_skins` (`steamid`, `weapon_defindex`, " + + "`weapon_paint_id`, `weapon_wear`, `weapon_seed`) " + + "VALUES (@steamid, @weaponDefIndex, @paintId, @wear, @seed)"; + + await connection.ExecuteAsync(insertSql, updateParams); + } + } + await connection.CloseAsync(); + } + } +}