diff --git a/Commands.cs b/Commands.cs new file mode 100644 index 00000000..d1b3f033 --- /dev/null +++ b/Commands.cs @@ -0,0 +1,289 @@ +using CounterStrikeSharp.API.Core; +using CounterStrikeSharp.API.Modules.Commands; +using CounterStrikeSharp.API.Modules.Menu; + +namespace WeaponPaints +{ + public partial class WeaponPaints + { + private void OnCommandRefresh(CCSPlayerController? player, CommandInfo command) + { + if (!Config.Additional.CommandWpEnabled || !Config.Additional.SkinEnabled || !g_bCommandsAllowed) return; + if (!Utility.IsPlayerValid(player)) return; + if (player == null || player.Index <= 0) return; + int playerIndex = (int)player!.Index; + + PlayerInfo playerInfo = new PlayerInfo + { + UserId = player.UserId, + Index = (int)player.Index, + SteamId = player?.AuthorizedSteamID?.SteamId64.ToString(), + Name = player?.PlayerName, + IpAddress = player?.IpAddress?.Split(":")[0] + }; + + if (playerIndex != 0 && DateTime.UtcNow >= commandCooldown[playerIndex].AddSeconds(Config.CmdRefreshCooldownSeconds)) + { + commandCooldown[playerIndex] = DateTime.UtcNow; + if (weaponSync != null) + Task.Run(async () => await weaponSync.GetWeaponPaintsFromDatabase(playerInfo)); + if (Config.Additional.KnifeEnabled) + { + if (weaponSync != null) + Task.Run(async () => await weaponSync.GetKnifeFromDatabase(playerInfo)); + + RefreshWeapons(player); + } + if (!string.IsNullOrEmpty(Localizer["wp_command_refresh_done"])) + { + player!.Print(Localizer["wp_command_refresh_done"]); + } + return; + } + if (!string.IsNullOrEmpty(Localizer["wp_command_cooldown"])) + { + player!.Print(Localizer["wp_command_cooldown"]); + } + } + + private void OnCommandWS(CCSPlayerController? player, CommandInfo command) + { + if (!Config.Additional.SkinEnabled) return; + if (!Utility.IsPlayerValid(player)) return; + + if (!string.IsNullOrEmpty(Localizer["wp_info_website"])) + { + player!.Print(Localizer["wp_info_website", Config.Website]); + } + if (!string.IsNullOrEmpty(Localizer["wp_info_refresh"])) + { + player!.Print(Localizer["wp_info_refresh"]); + } + if (!Config.Additional.KnifeEnabled) return; + if (!string.IsNullOrEmpty(Localizer["wp_info_knife"])) + { + player!.Print(Localizer["wp_info_knife"]); + } + } + + 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) || !g_bCommandsAllowed) return; + OnCommandRefresh(player, info); + }); + if (Config.Additional.CommandKillEnabled) + { + AddCommand($"css_{Config.Additional.CommandKill}", "kill yourself", (player, info) => + { + if (player == null || !Utility.IsPlayerValid(player) || player.PlayerPawn.Value == null || !player!.PlayerPawn.IsValid) return; + + player.PlayerPawn.Value.CommitSuicide(true, false); + }); + } + } + + private void SetupKnifeMenu() + { + if (!Config.Additional.KnifeEnabled || !g_bCommandsAllowed) 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(Localizer["wp_knife_menu_title"]); + var handleGive = (CCSPlayerController? player, ChatMenuOption option) => + { + if (Utility.IsPlayerValid(player)) + { + if (player == null) return; + var knifeName = option.Text; + var knifeKey = knivesOnly.FirstOrDefault(x => x.Value == knifeName).Key; + if (!string.IsNullOrEmpty(knifeKey)) + { + if (!string.IsNullOrEmpty(Localizer["wp_knife_menu_select"])) + { + player!.Print(Localizer["wp_knife_menu_select", knifeName]); + } + + if (!string.IsNullOrEmpty(Localizer["wp_knife_menu_kill"]) && Config.Additional.CommandKillEnabled) + { + player!.Print(Localizer["wp_knife_menu_kill"]); + } + + PlayerInfo playerInfo = new PlayerInfo + { + UserId = player.UserId, + Index = (int)player.Index, + SteamId = player?.AuthorizedSteamID?.SteamId64.ToString(), + Name = player?.PlayerName, + IpAddress = player?.IpAddress?.Split(":")[0] + }; + + g_playersKnife[(int)player!.Index] = knifeKey; + + if (player!.PawnIsAlive && g_bCommandsAllowed) + { + RefreshWeapons(player); + } + + if (weaponSync != null) + Task.Run(async () => await weaponSync.SyncKnifeToDatabase(playerInfo, knifeKey)); + } + } + }; + foreach (var knifePair in knivesOnly) + { + giveItemMenu.AddMenuOption(knifePair.Value, handleGive); + } + AddCommand($"css_{Config.Additional.CommandKnife}", "Knife Menu", (player, info) => + { + if (!Utility.IsPlayerValid(player) || !g_bCommandsAllowed) return; + int playerIndex = (int)player!.Index; + + if (commandCooldown != null && DateTime.UtcNow >= commandCooldown[playerIndex].AddSeconds(Config.CmdRefreshCooldownSeconds)) + { + commandCooldown[playerIndex] = DateTime.UtcNow; + ChatMenus.OpenMenu(player, giveItemMenu); + return; + } + if (!string.IsNullOrEmpty(Localizer["wp_command_cooldown"])) + { + player!.Print(Localizer["wp_command_cooldown"]); + } + }); + } + + private void SetupSkinsMenu() + { + var classNamesByWeapon = weaponList.ToDictionary(kvp => kvp.Value, kvp => kvp.Key); + var weaponSelectionMenu = new ChatMenu(Localizer["wp_skin_menu_weapon_title"]); + + // Function to handle skin selection for a specific weapon + var handleWeaponSelection = (CCSPlayerController? player, ChatMenuOption option) => + { + if (!Utility.IsPlayerValid(player)) return; + + int playerIndex = (int)player!.Index; + 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(Localizer["wp_skin_menu_skin_title", selectedWeapon]); + + // Function to handle skin selection for the chosen weapon + var handleSkinSelection = (CCSPlayerController? p, ChatMenuOption opt) => + { + if (p == null || !p.IsValid || p.Index <= 0) return; + + playerIndex = (int)p.Index; + + if (p.AuthorizedSteamID == null) return; + + string steamId = p.AuthorizedSteamID.SteamId64.ToString(); + 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)) + { + p!.Print(Localizer["wp_skin_menu_select", selectedSkin]); + + if (!gPlayerWeaponsInfo[playerIndex].ContainsKey(weaponDefIndex)) + { + gPlayerWeaponsInfo[playerIndex][weaponDefIndex] = new WeaponInfo(); + } + + gPlayerWeaponsInfo[playerIndex][weaponDefIndex].Paint = paintID; + gPlayerWeaponsInfo[playerIndex][weaponDefIndex].Wear = 0.01f; + gPlayerWeaponsInfo[playerIndex][weaponDefIndex].Seed = 0; + + PlayerInfo playerInfo = new PlayerInfo + { + UserId = player.UserId, + Index = (int)player.Index, + SteamId = player?.AuthorizedSteamID?.SteamId64.ToString(), + Name = player?.PlayerName, + IpAddress = player?.IpAddress?.Split(":")[0] + }; + + if (!Config.GlobalShare) + { + if (weaponSync != null) + Task.Run(async () => await weaponSync.SyncWeaponPaintsToDatabase(playerInfo)); + } + } + }; + + // 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!.Index; + + 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(Localizer["wp_command_cooldown"])) + { + player!.Print(Localizer["wp_command_cooldown"]); + } + }); + } + } +} \ No newline at end of file diff --git a/Config.cs b/Config.cs index 042f51e9..2795400c 100644 --- a/Config.cs +++ b/Config.cs @@ -3,32 +3,6 @@ using System.Text.Json.Serialization; namespace WeaponPaints { - public class Messages - { - [JsonPropertyName("WebsiteMessageCommand")] - public string WebsiteMessageCommand { get; set; } = "Visit {WEBSITE} where you can change skins."; - [JsonPropertyName("SynchronizeMessageCommand")] - public string SynchronizeMessageCommand { get; set; } = "Type !wp to synchronize chosen skins."; - [JsonPropertyName("KnifeMessageCommand")] - public string KnifeMessageCommand { get; set; } = "Type !knife to open knife menu."; - [JsonPropertyName("CooldownRefreshCommand")] - public string CooldownRefreshCommand { get; set; } = "You can't refresh weapon paints right now."; - [JsonPropertyName("SuccessRefreshCommand")] - public string SuccessRefreshCommand { get; set; } = "Refreshing weapon paints."; - [JsonPropertyName("ChosenKnifeMenu")] - public string ChosenKnifeMenu { get; set; } = "You have chosen {KNIFE} as your knife."; - [JsonPropertyName("ChosenSkinMenu")] - public string ChosenSkinMenu { get; set; } = "You have chosen {SKIN} as your skin."; - [JsonPropertyName("ChosenKnifeMenuKill")] - public string ChosenKnifeMenuKill { get; set; } = "To correctly apply skin for knife, you need to type !kill."; - [JsonPropertyName("KnifeMenuTitle")] - public string KnifeMenuTitle { get; set; } = "Knife Menu."; - [JsonPropertyName("WeaponMenuTitle")] - public string WeaponMenuTitle { get; set; } = "Weapon Menu."; - [JsonPropertyName("SkinMenuTitle")] - public string SkinMenuTitle { get; set; } = "Select skin for {WEAPON}"; - } - public class Additional { [JsonPropertyName("SkinVisibilityFix")] @@ -66,6 +40,8 @@ namespace WeaponPaints [JsonPropertyName("GiveRandomSkin")] public bool GiveRandomSkin { get; set; } = false; + [JsonPropertyName("GiveKnifeAfterRemove")] + public bool GiveKnifeAfterRemove { get; set; } = false; } public class WeaponPaintsConfig : BasePluginConfig @@ -99,9 +75,6 @@ namespace WeaponPaints [JsonPropertyName("Website")] public string Website { get; set; } = "example.com/skins"; - [JsonPropertyName("Messages")] - public Messages Messages { get; set; } = new Messages(); - [JsonPropertyName("Additional")] public Additional Additional { get; set; } = new Additional(); } diff --git a/Events.cs b/Events.cs new file mode 100644 index 00000000..c6707ec9 --- /dev/null +++ b/Events.cs @@ -0,0 +1,309 @@ +using CounterStrikeSharp.API; +using CounterStrikeSharp.API.Core; +using CounterStrikeSharp.API.Modules.Entities; + +namespace WeaponPaints +{ + public partial class WeaponPaints + { + private void OnClientAuthorized(int playerSlot, SteamID steamID) + { + int playerIndex = playerSlot + 1; + + CCSPlayerController? player = Utilities.GetPlayerFromIndex(playerIndex); + + PlayerInfo playerInfo = new PlayerInfo + { + UserId = player.UserId, + Index = (int)player.Index, + SteamId = player?.AuthorizedSteamID?.SteamId64.ToString(), + Name = player?.PlayerName, + IpAddress = player?.IpAddress?.Split(":")[0] + }; + + if (player == null || !player.IsValid || player.IsBot || player.IsHLTV || weaponSync == null) return; + + Task.Run(async () => + { + if (Config.Additional.SkinEnabled) + await weaponSync.GetKnifeFromDatabase(playerInfo); + }); + + //if (Config.Additional.KnifeEnabled && weaponSync != null) + //_ = weaponSync.GetKnifeFromDatabase(playerIndex); + } + + private void OnClientDisconnect(int playerSlot) + { + CCSPlayerController player = Utilities.GetPlayerFromSlot(playerSlot); + + if (player == null || !player.IsValid || player.IsBot || player.IsHLTV) return; + + if (Config.Additional.KnifeEnabled) + g_playersKnife.Remove((int)player.Index); + if (Config.Additional.SkinEnabled) + gPlayerWeaponsInfo.Remove((int)player.Index); + } + + private void OnEntityCreated(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.Index <= 0) return; + int weaponOwner = (int)weapon.OwnerEntity.Index; + var pawn = new CBasePlayerPawn(NativeAPI.GetEntityFromIndex(weaponOwner)); + if (!pawn.IsValid) return; + + var playerIndex = (int)pawn.Controller.Index; + var player = Utilities.GetPlayerFromIndex(playerIndex); + if (!Utility.IsPlayerValid(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; + } + + private HookResult OnItemPickup(EventItemPickup @event, GameEventInfo info) + { + if (@event.Defindex == 42 || @event.Defindex == 59) + { + Server.PrintToChatAll("test1"); + + CCSPlayerController? player = @event.Userid; + if (player == null || !player.IsValid || !g_knifePickupCount.ContainsKey((int)player.Index) || player.IsBot || !g_playersKnife.ContainsKey((int)player.Index)) + return HookResult.Continue; + + Server.PrintToChatAll("test2"); + + + if (g_knifePickupCount[(int)player.Index] >= 2) return HookResult.Continue; + Server.PrintToChatAll("test3"); + + + if (g_playersKnife.ContainsKey((int)player.Index) + && + g_playersKnife[(int)player.Index] != "weapon_knife") + { + Server.PrintToChatAll("usuwam noz"); + g_knifePickupCount[(int)player.Index]++; + + RemovePlayerKnife(player, true); + + if (!PlayerHasKnife(player) && Config.Additional.GiveKnifeAfterRemove) + AddTimer(0.3f, () => GiveKnifeToPlayer(player)); + } + } + return HookResult.Continue; + } + + public HookResult OnPickup(CEntityIOOutput output, string name, CEntityInstance activator, CEntityInstance caller, CVariant value, float delay) + { + CCSPlayerController? player = Utilities.GetEntityFromIndex((int)activator.Index).OriginalController.Value; + + if (player == null || player.IsBot || player.IsHLTV) + return HookResult.Continue; + + if (player == null || !player.IsValid || player.AuthorizedSteamID == null || + !g_knifePickupCount.ContainsKey((int)player.Index) || !g_playersKnife.ContainsKey((int)player.Index)) + return HookResult.Continue; + + CBasePlayerWeapon weapon = new(caller.Handle); + + if (weapon.AttributeManager.Item.ItemDefinitionIndex != 42 && weapon.AttributeManager.Item.ItemDefinitionIndex != 59) + return HookResult.Continue; + + if (g_knifePickupCount[(int)player.Index] >= 2) return HookResult.Continue; + + if (g_playersKnife[(int)player.Index] != "weapon_knife") + { + g_knifePickupCount[(int)player.Index]++; + weapon.Remove(); + if (!PlayerHasKnife(player) && Config.Additional.GiveKnifeAfterRemove) + AddTimer(0.2f, () => GiveKnifeToPlayer(player)); + } + + return HookResult.Continue; + } + + + 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 \"\""); + NativeAPI.IssueServerCommand("mp_equipment_reset_rounds 0"); + + if (Config.GlobalShare) + GlobalShareConnect(); + + weaponSync = new WeaponSynchronization(DatabaseConnectionString, Config, GlobalShareApi, GlobalShareServerId); + }); + + g_hTimerCheckSkinsData = AddTimer(10.0f, () => + { + List players = Utilities.GetPlayers(); + + foreach (CCSPlayerController player in players) + { + if (player.IsBot || player.IsHLTV || player.AuthorizedSteamID == null) continue; + if (gPlayerWeaponsInfo.ContainsKey((int)player.Index)) continue; + + PlayerInfo playerInfo = new PlayerInfo + { + UserId = player.UserId, + Index = (int)player.Index, + SteamId = player?.AuthorizedSteamID?.SteamId64.ToString(), + Name = player?.PlayerName, + IpAddress = player?.IpAddress?.Split(":")[0] + }; + + if (Config.Additional.SkinEnabled && weaponSync != null) + _ = weaponSync.GetWeaponPaintsFromDatabase(playerInfo); + if (Config.Additional.KnifeEnabled && weaponSync != null) + _ = weaponSync.GetKnifeFromDatabase(playerInfo); + } + }, CounterStrikeSharp.API.Modules.Timers.TimerFlags.STOP_ON_MAPCHANGE | CounterStrikeSharp.API.Modules.Timers.TimerFlags.REPEAT); + } + + private HookResult OnPlayerConnectFull(EventPlayerConnectFull @event, GameEventInfo info) + { + CCSPlayerController? player = @event.Userid; + + if (player == null || !player.IsValid || player.IsBot || player.IsHLTV || weaponSync == null) return HookResult.Continue; + + PlayerInfo playerInfo = new PlayerInfo + { + UserId = player.UserId, + Index = (int)player.Index, + SteamId = player?.AuthorizedSteamID?.SteamId64.ToString(), + Name = player?.PlayerName, + IpAddress = player?.IpAddress?.Split(":")[0] + }; + + if (!gPlayerWeaponsInfo.ContainsKey((int)player!.Index)) + { + Console.WriteLine($"[WeaponPaints] Retrying to retrieve player {player.PlayerName} skins"); + Task.Run(async () => + { + if (Config.Additional.SkinEnabled) + await weaponSync.GetWeaponPaintsFromDatabase(playerInfo); + if (Config.Additional.KnifeEnabled) + await weaponSync.GetKnifeFromDatabase(playerInfo); + }); + } + + return HookResult.Continue; + } + + private HookResult OnPlayerSpawn(EventPlayerSpawn @event, GameEventInfo info) + { + CCSPlayerController? player = @event.Userid; + if (player == null || !player.IsValid || player.IsBot) + { + return HookResult.Continue; + } + + if (Config.Additional.KnifeEnabled) + { + g_knifePickupCount[(int)player.Index] = 0; + AddTimer(0.1f, () => GiveKnifeToPlayer(player)); + } + + if (Config.Additional.SkinVisibilityFix) + { + AddTimer(0.3f, () => RefreshSkins(player)); + } + + return HookResult.Continue; + } + + private HookResult OnRoundEnd(EventRoundEnd @event, GameEventInfo info) + { + g_bCommandsAllowed = false; + return HookResult.Continue; + } + + private HookResult OnRoundStart(EventRoundStart @event, GameEventInfo info) + { + NativeAPI.IssueServerCommand("mp_t_default_melee \"\""); + NativeAPI.IssueServerCommand("mp_ct_default_melee \"\""); + NativeAPI.IssueServerCommand("mp_equipment_reset_rounds 0"); + + g_bCommandsAllowed = true; + + return HookResult.Continue; + } + + private void RegisterListeners() + { + RegisterListener(OnEntityCreated); + RegisterListener(OnClientAuthorized); + RegisterListener(OnClientDisconnect); + RegisterListener(OnMapStart); + + RegisterEventHandler(OnPlayerConnectFull); + RegisterEventHandler(OnPlayerSpawn); + RegisterEventHandler(OnRoundStart, HookMode.Pre); + RegisterEventHandler(OnRoundEnd); + RegisterEventHandler(OnEventItemPurchasePost); + //RegisterEventHandler(OnItemPickup); + HookEntityOutput("weapon_knife", "OnPlayerPickup", OnPickup, HookMode.Pre); + } + + /* WORKAROUND FOR CLIENTS WITHOUT STEAMID ON AUTHORIZATION */ + /*private HookResult OnPlayerConnectFull(EventPlayerConnectFull @event, GameEventInfo info) + { + CCSPlayerController? player = @event.Userid; + + if (player == null || !player.IsValid || !player.EntityIndex.HasValue || player.IsHLTV) return HookResult.Continue; + + int playerIndex = (int)player.EntityIndex.Value.Value; + if (Config.Additional.SkinEnabled && weaponSync != null) + _ = weaponSync.GetWeaponPaintsFromDatabase(playerIndex); + if (Config.Additional.KnifeEnabled && weaponSync != null) + _ = weaponSync.GetKnifeFromDatabase(playerIndex); + + Task.Run(async () => + { + if (Config.Additional.SkinEnabled && weaponSync != null) + if (Config.Additional.KnifeEnabled && weaponSync != null) + }); + + return HookResult.Continue; + } + */ + } +} \ No newline at end of file diff --git a/PlayerExtensions.cs b/PlayerExtensions.cs new file mode 100644 index 00000000..9fa41d0c --- /dev/null +++ b/PlayerExtensions.cs @@ -0,0 +1,14 @@ +using CounterStrikeSharp.API.Core; +using System.Text; + +namespace WeaponPaints; + +public static class PlayerExtensions +{ + public static void Print(this CCSPlayerController controller, string message) + { + StringBuilder _message = new(WeaponPaints._localizer["wp_prefix"]); + _message.Append(message); + controller.PrintToChat(_message.ToString()); + } +} \ No newline at end of file diff --git a/PlayerInfo.cs b/PlayerInfo.cs new file mode 100644 index 00000000..dba103b6 --- /dev/null +++ b/PlayerInfo.cs @@ -0,0 +1,11 @@ +namespace WeaponPaints +{ + public class PlayerInfo + { + public int Index { get; set; } + public int? UserId { get; set; } + public string? SteamId { get; set; } + public string? Name { get; set; } + public string? IpAddress { get; set; } + } +} diff --git a/PluginServices.cs b/PluginServices.cs new file mode 100644 index 00000000..5f282702 --- /dev/null +++ b/PluginServices.cs @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/README.md b/README.md index 127c82ff..e12d5dfd 100644 --- a/README.md +++ b/README.md @@ -9,22 +9,23 @@ Unfinished, unoptimized and not fully functional ugly demo weapon paints plugin [![ko-fi](https://ko-fi.com/img/githubbutton_sm.svg)](https://ko-fi.com/E1E2G0P2O) or [![Donate on Steam](https://github.com/Nereziel/cs2-WeaponPaints/assets/32937653/a0d53822-4ca7-4caf-83b4-e1a9b5f8c94e)](https://steamcommunity.com/tradeoffer/new/?partner=41515647&token=gW2W-nXE) ## Features -- Changes only paint, seed and wear on weapons and knives; -- MySQL based or global website at [weaponpaints.fun](https://weaponpaints.fun/), so you dont need MySQL/Website; -- Data sync on player connect; -- Added command **`!wp`** to refresh skins; ***(with cooldown in second can be configured)*** -- Added command **`!ws`** to show website; -- Added command **`!knife`** to show menu with knives; -- Knife change is now limited to have these cvars empty **`mp_t_default_melee ""`** and **`mp_ct_default_melee ""`**; +- Changes only paint, seed and wear on weapons and knives +- MySQL based or global website at [weaponpaints.fun](https://weaponpaints.fun/), so you dont need MySQL/Website +- Data sync on player connect +- Added command **`!wp`** to refresh skins ***(with cooldown in second can be configured)*** +- Added command **`!ws`** to show website +- Added command **`!knife`** to show menu with knives +- Knife change is now limited to have these cvars empty **`mp_t_default_melee ""`** and **`mp_ct_default_melee ""`** +- Translations support, submit a PR if you want to share your translation ## CS2 Server -- Compile and copy plugin to plugins, [more info here](https://docs.cssharp.dev/guides/hello-world-plugin/); -- Setup **`addons/counterstrikesharp/configs/plugins/WeaponPaints/WeaponPaints.json`** set **`GlobalShare`** to **`true`** for global, or include database credentials; -- in **`addons/counterstrikesharp/configs/core.json`** set **FollowCS2ServerGuidelines** to **`false`**; +- Compile and copy plugin to plugins, [more info here](https://docs.cssharp.dev/guides/hello-world-plugin/) +- Setup **`addons/counterstrikesharp/configs/plugins/WeaponPaints/WeaponPaints.json`** set **`GlobalShare`** to **`true`** for global, or include database credentials +- in **`addons/counterstrikesharp/configs/core.json`** set **FollowCS2ServerGuidelines** to **`false`** ## Plugin Configuration
- Spoiler warning + Click to expand
{
 	"Version": 4, // Don't touch
 	"DatabaseHost": "", // MySQL host (required if GlobalShare = false)
@@ -43,8 +44,11 @@ Unfinished, unoptimized and not fully functional ugly demo weapon paints plugin
 	"CooldownRefreshCommand": "You can\u0027t refresh weapon paints right now.", // Cooldown information (!wp command) Set to empty to disable
 	"SuccessRefreshCommand": "Refreshing weapon paints.", // Information about refreshing skins (!wp command) Set to empty to disable
 	"ChosenKnifeMenu": "You have chosen {KNIFE} as your knife.", // Information about choosen knife (!knife command) Set to empty to disable
+	"ChosenSkinMenu": "You have chosen {SKIN} as your skin.", // Information about choosen skin (!skins command) Set to empty to disable
 	"ChosenKnifeMenuKill": "To correctly apply skin for knife, you need to type !kill.", // Information about suicide after knife selection (!knife command) Set to empty to disable
-	"KnifeMenuTitle": "Knife Menu."  // Menu title (!knife menu)
+	"KnifeMenuTitle": "Knife Menu.",  // Menu title (!knife menu)
+	"WeaponMenuTitle": "Weapon Menu.", // Menu title (!skins menu)
+	"SkinMenuTitle": "Select skin for {WEAPON}" // Menu title (!skins menu, after weapon select)
 },
 "Additional": {
 	"SkinVisibilityFix": true, // Enable or disable fix for skin visibility
@@ -54,9 +58,11 @@ Unfinished, unoptimized and not fully functional ugly demo weapon paints plugin
 	"CommandKillEnabled": true, // Enable or disable kill command
 	"CommandKnife": "knife", // Name of knife menu command, u can change to for e.g, knives
 	"CommandSkin": "ws", // Name of skin information command, u can change to for e.g, skins
+	"CommandSkinSelection": "skins", // Name of skins menu command, u can change to for e.g, weapons
 	"CommandRefresh": "wp", // Name of skin refreshing command, u can change to for e.g, refreshskins
 	"CommandKill": "kill", // Name of kill command, u can change to for e.g, suicide
-	"GiveRandomKnife": false  // Give random knife to players if they didn't choose
+	"GiveRandomKnife": false,  // Give random knife to players if they didn't choose
+	"GiveRandomSkins": false  // Give random skins to players if they didn't choose
 },
 
 "ConfigVersion": 4  // Don't touch
@@ -64,12 +70,17 @@ Unfinished, unoptimized and not fully functional ugly demo weapon paints plugin
 
## Web install -Disregard if the config is **`GlobalShare = true`**; -- Requires PHP >= 7.4; ***(Tested on php ver **`8.2.3`** and nginx webserver)*** -- Copy website to web server; ***(Folder `img` not needed)*** -- Get [Steam API Key](https://steamcommunity.com/dev/apikey); -- Fill in database credentials and api key in `class/config.php`; -- Visit website and login via steam; +Disregard if the config is **`GlobalShare = true`** +- Requires PHP >= 7.4 ***(Tested on php ver **`8.2.3`** and nginx webserver)*** +- Copy website to web server ***(Folder `img` not needed)*** +- Get [Steam API Key](https://steamcommunity.com/dev/apikey) +- Fill in database credentials and api key in `class/config.php` +- Visit website and login via steam + +## Web Features +- Basic website +- Steam login/logout +- Change knife, paint, seed and wear ## Known issues - Issue on Windows servers, no knives are given. diff --git a/Utility.cs b/Utility.cs new file mode 100644 index 00000000..d18c8ce2 --- /dev/null +++ b/Utility.cs @@ -0,0 +1,185 @@ +using CounterStrikeSharp.API.Core; +using CounterStrikeSharp.API.Modules.Utils; +using Dapper; +using MySqlConnector; +using Newtonsoft.Json.Linq; +using Newtonsoft.Json; +using System.Reflection; +using Microsoft.Extensions.Logging; + +namespace WeaponPaints +{ + internal static class Utility + { + internal static WeaponPaintsConfig? Config { get; set; } + + 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 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.000001, `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 bool IsPlayerValid(CCSPlayerController? player) + { + return (player != null && player.IsValid && !player.IsBot && !player.IsHLTV && player.AuthorizedSteamID != null); + } + 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 void Log(string message) + { + Console.BackgroundColor = ConsoleColor.DarkGray; + Console.ForegroundColor = ConsoleColor.Cyan; + Console.WriteLine("[WeaponPaints] " + message); + Console.ResetColor(); + } + + internal static string ReplaceTags(string message) + { + if (message.Contains('{')) + { + string modifiedValue = message; + if (Config != null) + { + modifiedValue = modifiedValue.Replace("{WEBSITE}", Config.Website); + } + foreach (FieldInfo field in typeof(ChatColors).GetFields()) + { + string pattern = $"{{{field.Name}}}"; + if (message.Contains(pattern, StringComparison.OrdinalIgnoreCase)) + { + modifiedValue = modifiedValue.Replace(pattern, field.GetValue(null)!.ToString(), StringComparison.OrdinalIgnoreCase); + } + } + return modifiedValue; + } + + return message; + } + + internal static async Task CheckVersion(string version, ILogger logger) + { + using (HttpClient client = new HttpClient()) + { + try + { + HttpResponseMessage response = await client.GetAsync("https://raw.githubusercontent.com/Nereziel/cs2-WeaponPaints/main/VERSION"); + + if (response.IsSuccessStatusCode) + { + string remoteVersion = await response.Content.ReadAsStringAsync(); + remoteVersion = remoteVersion.Trim(); + + int comparisonResult = string.Compare(version, remoteVersion); + + if (comparisonResult < 0) + { + logger.LogWarning("Plugin is outdated! Check https://github.com/Nereziel/cs2-WeaponPaints"); + } + else if (comparisonResult > 0) + { + logger.LogInformation("Probably dev version detected"); + } + else + { + logger.LogInformation("Plugin is up to date"); + } + } + else + { + logger.LogWarning("Failed to check version"); + } + } + catch (Exception ex) + { + Console.WriteLine(ex); + } + } + } + + internal static void ShowAd(string moduleVersion) + { + Console.WriteLine(" "); + Console.WriteLine(" _ _ _______ _______ _______ _______ __ _ _______ _______ ___ __ _ _______ _______ "); + Console.WriteLine("| | _ | || || _ || || || | | || || _ || | | | | || || |"); + Console.WriteLine("| || || || ___|| |_| || _ || _ || |_| || _ || |_| || | | |_| ||_ _|| _____|"); + Console.WriteLine("| || |___ | || |_| || | | || || |_| || || | | | | | | |_____ "); + Console.WriteLine("| || ___|| || ___|| |_| || _ || ___|| || | | _ | | | |_____ |"); + Console.WriteLine("| _ || |___ | _ || | | || | | || | | _ || | | | | | | | _____| |"); + Console.WriteLine("|__| |__||_______||__| |__||___| |_______||_| |__||___| |__| |__||___| |_| |__| |___| |_______|"); + Console.WriteLine(" >> Version: " + moduleVersion); + Console.WriteLine(" >> GitHub: https://github.com/Nereziel/cs2-WeaponPaints"); + Console.WriteLine(" "); + } + + 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(); + } + } +} \ No newline at end of file diff --git a/VERSION b/VERSION new file mode 100644 index 00000000..c95270d1 --- /dev/null +++ b/VERSION @@ -0,0 +1 @@ +1.3f \ No newline at end of file diff --git a/WeaponAction.cs b/WeaponAction.cs new file mode 100644 index 00000000..0ed06d9c --- /dev/null +++ b/WeaponAction.cs @@ -0,0 +1,317 @@ +using CounterStrikeSharp.API; +using CounterStrikeSharp.API.Core; +using CounterStrikeSharp.API.Modules.Memory; +using CounterStrikeSharp.API.Modules.Utils; +using Microsoft.Extensions.Logging; + +namespace WeaponPaints +{ + public partial class WeaponPaints + { + internal static void ChangeWeaponAttributes(CBasePlayerWeapon? weapon, CCSPlayerController? player, bool isKnife = false) + { + if (player == null || weapon == null || !weapon.IsValid || !Utility.IsPlayerValid(player)) return; + + int playerIndex = (int)player.Index; + + 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 (isKnife) + { + weapon.AttributeManager.Item.EntityQuality = 3; + } + + 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.000001f; + 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.IsValid) return; + if (g_playersKnife.TryGetValue((int)player.Index, 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 static bool PlayerHasKnife(CCSPlayerController? player) + { + if (!_config.Additional.KnifeEnabled) return false; + + if (player == null || !player.IsValid || player.PlayerPawn == null || !player.PlayerPawn.IsValid || !player.PawnIsAlive) + { + return false; + } + + if (player.PlayerPawn?.Value == null || player.PlayerPawn?.Value.WeaponServices == null || player.PlayerPawn?.Value.ItemServices == null) + return false; + + var weapons = player.PlayerPawn.Value.WeaponServices?.MyWeapons; + if (weapons == null) return false; + foreach (var weapon in weapons) + { + if (weapon != null && weapon.IsValid && weapon.Value != null && weapon.Value.IsValid) + { + if (weapon.Value.DesignerName.Contains("knife") || weapon.Value.DesignerName.Contains("bayonet")) + { + return true; + } + } + } + return false; + } + + internal void RefreshPlayerKnife(CCSPlayerController? player) + { + if (player == null || !player.IsValid || player.PlayerPawn.Value == null || !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(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 != null && 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.Index <= 0) return; + int weaponEntityIndex = (int)weapon.Index; + NativeAPI.IssueClientCommand((int)player.Index - 1, "slot3"); + AddTimer(0.22f, () => + { + if (player.PlayerPawn.Value.WeaponServices.ActiveWeapon.Value!.DesignerName.Contains("knife") + || + player.PlayerPawn.Value.WeaponServices.ActiveWeapon.Value!.DesignerName.Contains("bayonet") + ) + { + if (player.PawnIsAlive) + { + NativeAPI.IssueClientCommand((int)player.Index - 1, "slot3"); + service.DropActivePlayerWeapon(weapon.Value); + GiveKnifeToPlayer(player); + } + } + }); + + Task.Delay(TimeSpan.FromSeconds(3.5)).ContinueWith(_ => + { + try + { + CEntityInstance? knife = Utilities.GetEntityFromIndex(weaponEntityIndex); + + if (knife != null && knife.IsValid && knife.Handle != -1 && knife.Index > 0) + { + knife.Remove(); + } + } + catch (Exception) { } + }); + + break; + } + } + } + } + } + + internal void RefreshSkins(CCSPlayerController? player) + { + if (!Utility.IsPlayerValid(player) || !player!.PawnIsAlive) return; + + AddTimer(0.18f, () => NativeAPI.IssueClientCommand((int)player.Index - 1, "slot3")); + AddTimer(0.25f, () => NativeAPI.IssueClientCommand((int)player.Index - 1, "slot2")); + AddTimer(0.38f, () => NativeAPI.IssueClientCommand((int)player.Index - 1, "slot1")); + } + + internal void RefreshWeapons(CCSPlayerController? player) + { + if (player == null || !player.IsValid || player.PlayerPawn.Value == null || !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(player.PlayerPawn.Value.ItemServices.Handle); + + foreach (var weapon in weapons) + { + if (weapon != null && weapon.IsValid && weapon.Value != null && weapon.Value.IsValid) + { + if (weapon.Index <= 0 || !weapon.Value.DesignerName.Contains("weapon_")) continue; + //if (weapon.Value.AttributeManager.Item.ItemDefinitionIndex == 42 || weapon.Value.AttributeManager.Item.ItemDefinitionIndex == 59) + try + { + if (weapon.Value.DesignerName.Contains("knife") || weapon.Value.DesignerName.Contains("bayonet")) + { + weapon.Value.Remove(); + GiveKnifeToPlayer(player); + } + else + { + if (!weaponDefindex.ContainsKey(weapon.Value.AttributeManager.Item.ItemDefinitionIndex)) continue; + int clip1, reservedAmmo; + + clip1 = weapon.Value.Clip1; + reservedAmmo = weapon.Value.ReserveAmmo[0]; + + weapon.Value.Remove(); + string weaponByDefindex = weaponDefindex[weapon.Value.AttributeManager.Item.ItemDefinitionIndex]; + CBasePlayerWeapon newWeapon = new(player.GiveNamedItem(weaponByDefindex)); + + Server.NextFrame(() => + { + if (newWeapon == null) return; + try + { + newWeapon.Clip1 = clip1; + newWeapon.ReserveAmmo[0] = reservedAmmo; + } + catch (Exception) + { } + }); + } + } + catch (Exception ex) + { + Logger.LogWarning("Refreshing weapons exception"); + Console.WriteLine("[WeaponPaints] Refreshing weapons exception"); + Console.WriteLine(ex.Message); + } + } + } + if (Config.Additional.SkinVisibilityFix) + RefreshSkins(player); + } + } + + internal void RemovePlayerKnife(CCSPlayerController? player, bool force = false) + { + if (player == null || !player.IsValid || player.PlayerPawn.Value == null || !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 != null && 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 (!force) + { + if ((int)weapon.Index <= 0) return; + int weaponEntityIndex = (int)weapon.Index; + NativeAPI.IssueClientCommand((int)player.Index - 1, "slot3"); + AddTimer(0.35f, () => service.DropActivePlayerWeapon(weapon.Value)); + + AddTimer(1.0f, () => + { + CEntityInstance? knife = Utilities.GetEntityFromIndex(weaponEntityIndex); + if (knife != null && knife.IsValid) + { + knife.Remove(); + } + }); + } + else + { + weapon.Value.Remove(); + } + + break; + } + } + } + } + } + private static int GetRandomPaint(int defindex) + { + + if (skinsList != null) + { + Random rnd = new Random(); + // 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 CSkeletonInstance GetSkeletonInstance(CGameSceneNode node) + { + Func GetSkeletonInstance = VirtualFunction.Create(node.Handle, 8); + return new CSkeletonInstance(GetSkeletonInstance(node.Handle)); + } + } +} \ No newline at end of file diff --git a/WeaponInfo.cs b/WeaponInfo.cs new file mode 100644 index 00000000..dc702bbf --- /dev/null +++ b/WeaponInfo.cs @@ -0,0 +1,9 @@ +namespace WeaponPaints +{ + public class WeaponInfo + { + public int Paint { get; set; } + public int Seed { get; set; } + public float Wear { get; set; } + } +} \ No newline at end of file diff --git a/WeaponPaints.cs b/WeaponPaints.cs index 04b221b0..9eebcf74 100644 --- a/WeaponPaints.cs +++ b/WeaponPaints.cs @@ -1,56 +1,17 @@ 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; +using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Localization; namespace WeaponPaints; -[MinimumApiVersion(61)] -public class WeaponPaints : BasePlugin, IPluginConfig + +[MinimumApiVersion(121)] +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.2"; - public WeaponPaintsConfig Config { get; set; } = new(); - - private string DatabaseConnectionString = string.Empty; - private Uri GlobalShareApi = new Uri("https://weaponpaints.fun/api.php"); - - 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 static List skinsList = new List(); - private static readonly Dictionary knifeTypes = new() - { - { "m9", "weapon_knife_m9_bayonet" }, { "karambit", "weapon_knife_karambit" }, - { "bayonet", "weapon_bayonet" }, { "bowie", "weapon_knife_survival_bowie" }, - { "butterfly", "weapon_knife_butterfly" }, { "falchion", "weapon_knife_falchion" }, - { "flip", "weapon_knife_flip" }, { "gut", "weapon_knife_gut" }, - { "tactical", "weapon_knife_tactical" }, { "shadow", "weapon_knife_push" }, - { "navaja", "weapon_knife_gypsy_jackknife" }, { "stiletto", "weapon_knife_stiletto" }, - { "talon", "weapon_knife_widowmaker" }, { "ursus", "weapon_knife_ursus" }, - { "css", "weapon_knife_css" }, { "paracord", "weapon_knife_cord" }, - { "survival", "weapon_knife_canis" }, { "nomad", "weapon_knife_outdoor" }, - { "skeleton", "weapon_knife_skeleton" }, { "default", "weapon_knife" } - }; - private static readonly Dictionary weaponList = new() + internal static readonly Dictionary weaponList = new() { {"weapon_deagle", "Desert Eagle"}, {"weapon_elite", "Dual Berettas"}, @@ -86,39 +47,150 @@ public class WeaponPaints : BasePlugin, IPluginConfig {"weapon_usp_silencer", "USP-S"}, {"weapon_cz75a", "CZ75-Auto"}, {"weapon_revolver", "R8 Revolver"}, - {"weapon_bayonet", "Bayonet Knife"}, - {"weapon_knife", "Default Knife"} + { "weapon_knife", "Default Knife" }, + { "weapon_knife_m9_bayonet", "M9 Bayonet" }, + { "weapon_knife_karambit", "Karambit" }, + { "weapon_bayonet", "Bayonet" }, + { "weapon_knife_survival_bowie", "Bowie Knife" }, + { "weapon_knife_butterfly", "Butterfly Knife" }, + { "weapon_knife_falchion", "Falchion Knife" }, + { "weapon_knife_flip", "Flip Knife" }, + { "weapon_knife_gut", "Gut Knife" }, + { "weapon_knife_tactical", "Huntsman Knife" }, + { "weapon_knife_push", "Shadow Daggers" }, + { "weapon_knife_gypsy_jackknife", "Navaja Knife" }, + { "weapon_knife_stiletto", "Stiletto Knife" }, + { "weapon_knife_widowmaker", "Talon Knife" }, + { "weapon_knife_ursus", "Ursus Knife" }, + { "weapon_knife_css", "Classic Knife" }, + { "weapon_knife_cord", "Paracord Knife" }, + { "weapon_knife_canis", "Survival Knife" }, + { "weapon_knife_outdoor", "Nomad Knife" }, + { "weapon_knife_skeleton", "Skeleton Knife" } }; + + internal static WeaponPaintsConfig _config = new WeaponPaintsConfig(); + internal static IStringLocalizer? _localizer; + internal static Dictionary g_knifePickupCount = new Dictionary(); + internal static Dictionary g_playersKnife = new(); + internal static Dictionary> gPlayerWeaponsInfo = new Dictionary>(); + internal static List skinsList = new List(); + internal static WeaponSynchronization? weaponSync; + //internal static List g_changedKnife = new(); + internal bool g_bCommandsAllowed = true; + + internal Uri GlobalShareApi = new("https://weaponpaints.fun/api.php"); + internal int GlobalShareServerId = 0; + private DateTime[] commandCooldown = new DateTime[Server.MaxPlayers]; + private string DatabaseConnectionString = string.Empty; + private CounterStrikeSharp.API.Modules.Timers.Timer? g_hTimerCheckSkinsData = null; + public static Dictionary weaponDefindex { get; } = new Dictionary + { + { 1, "weapon_deagle" }, + { 2, "weapon_elite" }, + { 3, "weapon_fiveseven" }, + { 4, "weapon_glock" }, + { 7, "weapon_ak47" }, + { 8, "weapon_aug" }, + { 9, "weapon_awp" }, + { 10, "weapon_famas" }, + { 11, "weapon_g3sg1" }, + { 13, "weapon_galilar" }, + { 14, "weapon_m249" }, + { 16, "weapon_m4a1" }, + { 17, "weapon_mac10" }, + { 19, "weapon_p90" }, + { 23, "weapon_mp5sd" }, + { 24, "weapon_ump45" }, + { 25, "weapon_xm1014" }, + { 26, "weapon_bizon" }, + { 27, "weapon_mag7" }, + { 28, "weapon_negev" }, + { 29, "weapon_sawedoff" }, + { 30, "weapon_tec9" }, + { 32, "weapon_hkp2000" }, + { 33, "weapon_mp7" }, + { 34, "weapon_mp9" }, + { 35, "weapon_nova" }, + { 36, "weapon_p250" }, + { 38, "weapon_scar20" }, + { 39, "weapon_sg556" }, + { 40, "weapon_ssg08" }, + { 60, "weapon_m4a1_silencer" }, + { 61, "weapon_usp_silencer" }, + { 63, "weapon_cz75a" }, + { 64, "weapon_revolver" }, + { 500, "weapon_bayonet" }, + { 503, "weapon_knife_css" }, + { 505, "weapon_knife_flip" }, + { 506, "weapon_knife_gut" }, + { 507, "weapon_knife_karambit" }, + { 508, "weapon_knife_m9_bayonet" }, + { 509, "weapon_knife_tactical" }, + { 512, "weapon_knife_falchion" }, + { 514, "weapon_knife_survival_bowie" }, + { 515, "weapon_knife_butterfly" }, + { 516, "weapon_knife_push" }, + { 517, "weapon_knife_cord" }, + { 518, "weapon_knife_canis" }, + { 519, "weapon_knife_ursus" }, + { 520, "weapon_knife_gypsy_jackknife" }, + { 521, "weapon_knife_outdoor" }, + { 522, "weapon_knife_stiletto" }, + { 523, "weapon_knife_widowmaker" }, + { 525, "weapon_knife_skeleton" } + }; + + public WeaponPaintsConfig Config { get; set; } = new(); + public override string ModuleAuthor => "Nereziel & daffyy"; + public override string ModuleDescription => "Skin and knife selector, standalone and web-based"; + public override string ModuleName => "WeaponPaints"; + public override string ModuleVersion => "1.3g"; + + public static WeaponPaintsConfig GetWeaponPaintsConfig() + { + return _config; + } + public override void Load(bool hotReload) { 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); if (hotReload) { OnMapStart(string.Empty); - Task.Run(async () => - { - for (int i = 1; i <= Server.MaxPlayers; i++) - { - if (Config.Additional.SkinEnabled) - await GetWeaponPaintsFromDatabase(i); - if (Config.Additional.KnifeEnabled) - await GetKnifeFromDatabase(i); - } - }); + List players = Utilities.GetPlayers(); + + foreach (CCSPlayerController player in players) + { + if (player == null || !player.IsValid || player.IsBot || player.IsHLTV || player.AuthorizedSteamID == null) continue; + if (gPlayerWeaponsInfo.ContainsKey((int)player.Index)) continue; + + PlayerInfo playerInfo = new PlayerInfo + { + UserId = player.UserId, + Index = (int)player.Index, + SteamId = player?.AuthorizedSteamID?.SteamId64.ToString(), + Name = player?.PlayerName, + IpAddress = player?.IpAddress?.Split(":")[0] + }; + + if (Config.Additional.SkinEnabled && weaponSync != null) + _ = weaponSync.GetWeaponPaintsFromDatabase(playerInfo); + if (Config.Additional.KnifeEnabled && weaponSync != null) + _ = weaponSync.GetKnifeFromDatabase(playerInfo); + + g_knifePickupCount[(int)player!.Index] = 0; + } + /* + RegisterListeners(); + RegisterCommands(); + */ } if (Config.Additional.KnifeEnabled) @@ -126,139 +198,36 @@ public class WeaponPaints : BasePlugin, IPluginConfig if (Config.Additional.SkinEnabled) SetupSkinsMenu(); + RegisterListeners(); RegisterCommands(); - LoadSkinsFromFile(ModuleDirectory + "/skins.json"); + Utility.LoadSkinsFromFile(ModuleDirectory + "/skins.json"); } + public void OnConfigParsed(WeaponPaintsConfig config) { if (!config.GlobalShare) { if (config.DatabaseHost.Length < 1 || config.DatabaseName.Length < 1 || config.DatabaseUser.Length < 1) { + Logger.LogError("You need to setup Database credentials in config!"); throw new Exception("[WeaponPaints] You need to setup Database credentials in config!"); } } Config = config; + _config = config; + _localizer = Localizer; - ShowAd(); + Utility.Config = config; + Utility.ShowAd(ModuleVersion); + Task.Run(async () => await Utility.CheckVersion(ModuleVersion, Logger)); } - 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; - } - - 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) - { - NativeAPI.IssueServerCommand("mp_t_default_melee \"\""); - NativeAPI.IssueServerCommand("mp_ct_default_melee \"\""); - return HookResult.Continue; - } - */ public override void Unload(bool hotReload) { base.Unload(hotReload); } - private void RegisterCommands() - { - AddCommand($"css_{Config.Additional.CommandSkin}", "Skins info", (player, info) => { if (player == null) return; OnCommandWS(player, info); }); - AddCommand($"css_{Config.Additional.CommandRefresh}", "Skins refresh", (player, info) => { if (player == null) return; OnCommandRefresh(player, info); }); - if (Config.Additional.CommandKillEnabled) - { - AddCommand($"css_{Config.Additional.CommandKill}", "kill yourself", (player, info) => - { - if (player == null || !player.IsValid || !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() { @@ -288,816 +257,12 @@ public class WeaponPaints : BasePlugin, IPluginConfig } else { + Logger.LogError("Unable to retrieve serverid from GlobalShare!"); throw new Exception("[WeaponPaints] Unable to retrieve serverid from GlobalShare!"); } } + + Logger.LogInformation("GlobalShare ONLINE!"); 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) - { - int playerIndex = playerSlot + 1; - CCSPlayerController player = Utilities.GetPlayerFromSlot(playerSlot); - if (player == null || !player.IsValid || player.IsBot) return; - // TODO: Clean up after player - 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) - { - var player = @event.Userid; - if (!player.IsValid || !player.PlayerPawn.IsValid) - { - return HookResult.Continue; - } - - if (Config.Additional.KnifeEnabled) - { - if (!PlayerHasKnife(player)) - GiveKnifeToPlayer(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 (player == null || !player.IsValid || player.IsBot || !player.PawnIsAlive) return HookResult.Continue; - - if (g_playersKnife.ContainsKey((int)player.EntityIndex!.Value.Value) - && - g_playersKnife[(int)player.EntityIndex!.Value.Value] != "weapon_knife") - { - RemoveKnifeFromPlayer(player); - - AddTimer(0.1f, () => - { - 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) 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 (player == null || !player.IsValid || player.IsBot) 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 || player == null || player.IsBot) return; - - int playerIndex = (int)player.EntityIndex!.Value.Value; - - 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) - { - Random random = new Random(); - int index = random.Next(knifeTypes.Count); - var randomKnife = knifeTypes.Values.ElementAt(index); - player.GiveNamedItem(randomKnife); - } - 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; - var weapons = player.PlayerPawn.Value.WeaponServices!.MyWeapons; - if (weapons != null && weapons.Count > 0) - { - foreach (var weapon in weapons) - { - if (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.Remove(); - return; - } - } - } - } - } - /* 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 (player == null || !player.IsValid || player.IsBot || !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.IsValid && weapon.Value.IsValid) - { - if (weapon.Value.DesignerName.Contains("knife")) - { - return true; - } - } - } - return false; - } - private void SetupKnifeMenu() - { - if (!Config.Additional.KnifeEnabled) return; - var giveItemMenu = new ChatMenu(ReplaceTags(Config.Messages.KnifeMenuTitle)); - var handleGive = (CCSPlayerController? player, ChatMenuOption option) => - { - if (player != null && player.IsValid) - { - string temp = ""; - if (knifeTypes.TryGetValue(option.Text, out var knife)) - { - g_playersKnife[(int)player.EntityIndex!.Value.Value] = knifeTypes[option.Text]; - if (!string.IsNullOrEmpty(Config.Messages.ChosenKnifeMenu)) - { - temp = $"{Config.Prefix} {Config.Messages.ChosenKnifeMenu}".Replace("{KNIFE}", option.Text); - player.PrintToChat(ReplaceTags(temp)); - } - if (!string.IsNullOrEmpty(Config.Messages.ChosenKnifeMenuKill) && Config.Additional.CommandKillEnabled) - { - temp = $"{Config.Prefix} {Config.Messages.ChosenKnifeMenuKill}"; - player.PrintToChat(ReplaceTags(temp)); - } - if (player.PawnIsAlive) - { - RemoveKnifeFromPlayer(player); - AddTimer(0.2f, () => - { - GiveKnifeToPlayer(player); - }); - } - Task.Run(() => SyncKnifeToDatabase((int)player.EntityIndex!.Value.Value, knife)); - - /* Old way - RemoveKnifeFromPlayer(player); - AddTimer(0.1f, () => GiveKnifeToPlayer(player)); - */ - } - } - }; - foreach (var knife in knifeTypes) - { - giveItemMenu.AddMenuOption(knife.Key, handleGive); - } - AddCommand($"css_{Config.Additional.CommandKnife}", "Knife Menu", (player, info) => { if (player == null) return; ChatMenus.OpenMenu(player, giveItemMenu); }); - } - - private void SetupSkinsMenu() - { - var classNamesByWeapon = weaponList.ToDictionary(kvp => kvp.Value, kvp => kvp.Key); - var weaponSelectionMenu = new ChatMenu(ReplaceTags(Config.Messages.WeaponMenuTitle)); - - // Function to handle skin selection for a specific weapon - var handleWeaponSelection = (CCSPlayerController? player, ChatMenuOption option) => - { - if (player == null || !player.IsValid) 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(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(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); - } - - foreach (var knifeClass in knifeTypes.Keys) - { - string knifeName = knifeTypes[knifeClass]; - weaponSelectionMenu.AddMenuOption(knifeName, handleWeaponSelection); - } - - // Command to open the weapon selection menu for players - AddCommand($"css_{Config.Additional.CommandSkinSelection}", "Skins selection menu", (player, info) => - { - if (player == null || !player.IsValid) return; - int playerIndex = (int)player.EntityIndex!.Value.Value; - - if (commandCooldown != null && DateTime.UtcNow >= commandCooldown[playerIndex].AddSeconds(Config.CmdRefreshCooldownSeconds)) - { - commandCooldown[playerIndex] = DateTime.UtcNow; - ChatMenus.OpenMenu(player, weaponSelectionMenu); - return; - } - if (!string.IsNullOrEmpty(Config.Messages.CooldownRefreshCommand)) - { - string temp = $"{Config.Prefix} {Config.Messages.CooldownRefreshCommand}"; - player.PrintToChat(ReplaceTags(temp)); - } - - }); - } - // [ConsoleCommand($"css_{Config.Additional.CommandRefresh}", "refreshskins")] - private void OnCommandRefresh(CCSPlayerController? player, CommandInfo command) - { - if (!Config.Additional.CommandWpEnabled || !Config.Additional.SkinEnabled) return; - if (player == null || !player.IsValid || player.IsBot) 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) - { - RemoveKnifeFromPlayer(player); - AddTimer(0.2f, () => - { - 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(ReplaceTags(temp)); - } - return; - } - if (!string.IsNullOrEmpty(Config.Messages.CooldownRefreshCommand)) - { - temp = $"{Config.Prefix} {Config.Messages.CooldownRefreshCommand}"; - player.PrintToChat(ReplaceTags(temp)); - } - } - // [ConsoleCommand($"css_{Config.Additional.CommandSkin}", "weaponskins")] - private void OnCommandWS(CCSPlayerController? player, CommandInfo command) - { - if (!Config.Additional.SkinEnabled) return; - if (player == null) return; - - string temp = ""; - - if (!string.IsNullOrEmpty(Config.Messages.WebsiteMessageCommand)) - { - temp = $"{Config.Prefix} {Config.Messages.WebsiteMessageCommand}"; - player.PrintToChat(ReplaceTags(temp)); - } - if (!string.IsNullOrEmpty(Config.Messages.SynchronizeMessageCommand)) - { - temp = $"{Config.Prefix} {Config.Messages.SynchronizeMessageCommand}"; - player.PrintToChat(ReplaceTags(temp)); - } - if (!Config.Additional.KnifeEnabled) return; - if (!string.IsNullOrEmpty(Config.Messages.KnifeMessageCommand)) - { - temp = $"{Config.Prefix} {Config.Messages.KnifeMessageCommand}"; - player.PrintToChat(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 (player == null || !player.IsValid || player.IsBot) 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) - { - Log(e.Message); - return; - } - } - private async Task GetKnifeFromDatabase(int playerIndex) - { - if (!Config.Additional.KnifeEnabled) return; - try - { - CCSPlayerController player = Utilities.GetPlayerFromIndex(playerIndex); - if (player == null || !player.IsValid || player.IsBot) 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) - { - 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) - { - Log(e.Message); - return; - } - } - - private async Task SyncWeaponPaintsToDatabase(CCSPlayerController? player) - { - if (player == null || !player.IsValid || player.IsBot) 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) - { - Console.WriteLine("WeaponDEFINDEX : " + weaponDefIndex); - 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 string ReplaceTags(string message) - { - if (message.Contains('{')) - { - string modifiedValue = message; - modifiedValue = modifiedValue.Replace("{WEBSITE}", Config.Website); - foreach (FieldInfo field in typeof(ChatColors).GetFields()) - { - string pattern = $"{{{field.Name}}}"; - if (message.Contains(pattern, StringComparison.OrdinalIgnoreCase)) - { - modifiedValue = modifiedValue.Replace(pattern, field.GetValue(null)!.ToString(), StringComparison.OrdinalIgnoreCase); - } - } - return modifiedValue; - } - - return message; - } - - 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); - } - } - - private static void Log(string message) - { - Console.BackgroundColor = ConsoleColor.DarkGray; - Console.ForegroundColor = ConsoleColor.Cyan; - Console.WriteLine("[WeaponPaints] " + message); - Console.ResetColor(); - } - private void ShowAd() - { - Console.WriteLine(" "); - Console.WriteLine(" _ _ _______ _______ _______ _______ __ _ _______ _______ ___ __ _ _______ _______ "); - Console.WriteLine("| | _ | || || _ || || || | | || || _ || | | | | || || |"); - Console.WriteLine("| || || || ___|| |_| || _ || _ || |_| || _ || |_| || | | |_| ||_ _|| _____|"); - Console.WriteLine("| || |___ | || |_| || | | || || |_| || || | | | | | | |_____ "); - Console.WriteLine("| || ___|| || ___|| |_| || _ || ___|| || | | _ | | | |_____ |"); - Console.WriteLine("| _ || |___ | _ || | | || | | || | | _ || | | | | | | | _____| |"); - Console.WriteLine("|__| |__||_______||__| |__||___| |_______||_| |__||___| |__| |__||___| |_| |__| |___| |_______|"); - Console.WriteLine(" >> Version: " + ModuleVersion); - Console.WriteLine(" >> GitHub: https://github.com/Nereziel/cs2-WeaponPaints"); - Console.WriteLine(" "); - - } -} +} \ No newline at end of file diff --git a/WeaponPaints.csproj b/WeaponPaints.csproj index 5db828f3..b7fd0686 100644 --- a/WeaponPaints.csproj +++ b/WeaponPaints.csproj @@ -5,19 +5,17 @@ enable enable true + true - - + + - - deps\CounterStrikeSharp.API.dll - + - diff --git a/WeaponSynchronization.cs b/WeaponSynchronization.cs new file mode 100644 index 00000000..5742a654 --- /dev/null +++ b/WeaponSynchronization.cs @@ -0,0 +1,248 @@ +using Dapper; +using MySqlConnector; +using Newtonsoft.Json.Linq; + +namespace WeaponPaints +{ + internal class WeaponSynchronization + { + private readonly WeaponPaintsConfig _config; + private readonly string _databaseConnectionString; + 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(PlayerInfo player) + { + if (!_config.Additional.KnifeEnabled) return; + if (player.SteamId == null || player.Index == 0) return; + try + { + if (_config.GlobalShare) + { + var values = new Dictionary + { + { "server_id", _globalShareServerId.ToString() }, + { "steamid", player.SteamId }, + { "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[player.Index] = 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 = player.SteamId }); + + if (PlayerKnife != null) + { + WeaponPaints.g_playersKnife[player.Index] = PlayerKnife; + } + else + { + return; + } + await connection.CloseAsync(); + } + } + catch (Exception e) + { + Utility.Log(e.Message); + return; + } + } + + internal async Task GetWeaponPaintsFromDatabase(PlayerInfo player) + { + if (!_config.Additional.SkinEnabled) return; + if (player.SteamId == null || player.Index == 0) return; + + if (!WeaponPaints.gPlayerWeaponsInfo.TryGetValue(player.Index, out _)) + { + WeaponPaints.gPlayerWeaponsInfo[player.Index] = new Dictionary(); + } + try + { + if (_config.GlobalShare) + { + var values = new Dictionary + { + { "server_id", _globalShareServerId.ToString() }, + { "steamid", player.SteamId }, + { "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, + Seed = weaponSeed.Value, + Wear = weaponWear.Value + }; + WeaponPaints.gPlayerWeaponsInfo[player.Index][weaponDefIndex.Value] = weaponInfo; + } + } + } + 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 = player.SteamId }); + + 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[player.Index][weaponDefIndex] = weaponInfo; + }); + } + else + { + return; + } + await connection.CloseAsync(); + } + } + catch (Exception e) + { + Utility.Log(e.Message); + return; + } + } + + internal async Task SyncKnifeToDatabase(PlayerInfo player, string knife) + { + if (!_config.Additional.KnifeEnabled) return; + try + { + if (player.SteamId == null || player.Index == 0) return; + + 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 = player.SteamId, newKnife = knife }); + await connection.CloseAsync(); + } + catch (Exception e) + { + Utility.Log(e.Message); + return; + } + } + internal async Task SyncWeaponPaintsToDatabase(PlayerInfo player) + { + if (player == null || player.Index <= 0 || player.SteamId == null) return; + + using var connection = new MySqlConnection(_databaseConnectionString); + await connection.OpenAsync(); + + if (!WeaponPaints.gPlayerWeaponsInfo.ContainsKey(player.Index)) + return; + + foreach (var weaponInfoPair in WeaponPaints.gPlayerWeaponsInfo[player.Index]) + { + 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 = player.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(); + } + } +} \ No newline at end of file diff --git a/lang/en.json b/lang/en.json new file mode 100644 index 00000000..9537d5a7 --- /dev/null +++ b/lang/en.json @@ -0,0 +1,14 @@ +{ + "wp_prefix": "{lightblue}[WeaponPaints] {default}", + "wp_info_website": "Visit {lime}{0}{default} where you can change skins", + "wp_info_refresh": "Type {lime}!wp{default} to synchronize chosen skins", + "wp_info_knife": "Type {lime}!knife{default} to open knife menu", + "wp_command_cooldown": "{lightred}You can't refresh weapon paints right now", + "wp_command_refresh_done": "{lime}Refreshing weapon paints", + "wp_knife_menu_select": "You have chosen {lime}{0}{default} as your knife", + "wp_knife_menu_kill": "To correctly apply skin for knife, you need to type {lime}!kill{default}", + "wp_knife_menu_title": "Knife Menu", + "wp_skin_menu_weapon_title": "Weapon Menu", + "wp_skin_menu_skin_title": "Select skin for {lime}{0}{default}", + "wp_skin_menu_select": "You have chosen {lime}{0}{default} as your skin" +} \ No newline at end of file diff --git a/lang/lv.json b/lang/lv.json new file mode 100644 index 00000000..6591014c --- /dev/null +++ b/lang/lv.json @@ -0,0 +1,14 @@ +{ + "wp_prefix": "{lightblue}[Ieroču Ādiņas] {default}", + "wp_info_website": "Apmeklē {lime}{0}{default} kur tu vari nomainīt skinus", + "wp_info_refresh": "Raksti {lime}!wp{default} lai sinhronizētu izvēlētos skinus", + "wp_info_knife": "Raksti {lime}!knife{default} lai atvērtu nažu izvēlni", + "wp_command_cooldown": "{lightred} Tu šobrīd nevari atjaunot ieroču skinus...", + "wp_command_refresh_done": "{lime}Izvēlētie skini tiek atjaunoti", + "wp_knife_menu_select": "Tu esi izvēlējies {lime}{0}{default} nazi", + "wp_knife_menu_kill": "Lai pareizi atjaunotu naža skinu, ieraksti čatā {lime}!kill{default}", + "wp_knife_menu_title": "Nažu Izvēlne", + "wp_skin_menu_weapon_title": "Ieroču Izvēlne", + "wp_skin_menu_skin_title": "Izvēlies skinu ierocim: {lime}{0}{default}", + "wp_skin_menu_select": "Tu esi izvēlējies {lime}{0}{default} kā savu skinu" +} diff --git a/lang/pl.json b/lang/pl.json new file mode 100644 index 00000000..b1ee5b00 --- /dev/null +++ b/lang/pl.json @@ -0,0 +1,14 @@ +{ + "wp_prefix": "{lightblue}[WeaponPaints] {default}", + "wp_info_website": "Odwiedź {lime}{0}{default} gdzie będziesz mógł ustawić skiny", + "wp_info_refresh": "Wpisz {lime}!wp{default} aby zsynchronizować swoje skiny", + "wp_info_knife": "Wpisz {lime}!knife{default} aby wy�wietlić menu no�y", + "wp_command_cooldown": "{lightred}Odczekaj chwilę przed wykonaniem tej komendy...", + "wp_command_refresh_done": "{lime}Pomyslnie zsynchronizowano twoje skiny", + "wp_knife_menu_select": "Wybrałeś {lime}{0}{default} jako swój nóż", + "wp_knife_menu_kill": "Do prawidłowego zastosowania noża użyj {lime}!kill{default}", + "wp_knife_menu_title": "Menu noży", + "wp_skin_menu_weapon_title": "Menu broni", + "wp_skin_menu_skin_title": "Wybierz skin dla {lime}{0}{default}", + "wp_skin_menu_select": "Wybrałeś {lime}{0}{default} jako swój skin" +} \ No newline at end of file diff --git a/lang/pt-BR.json b/lang/pt-BR.json new file mode 100644 index 00000000..5bdab322 --- /dev/null +++ b/lang/pt-BR.json @@ -0,0 +1,14 @@ +{ + "wp_prefix": "{lightblue}[WeaponPaints] {default}", + "wp_info_website": "Visite {lime}{0}{default} para mudar suas skins e faca", + "wp_info_refresh": "Digite {lime}!wp{default} para sincronizar as suas skins", + "wp_info_knife": "Digite {lime}!knife{default} para abrir o menu de facas", + "wp_command_cooldown": "{lightred}Você não pode atualizar as skins das armas agora", + "wp_command_refresh_done": "{lime}Sincronizando as suas skins", + "wp_knife_menu_select": "Você escolheu {lime}{0}{default} como sua faca", + "wp_knife_menu_kill": "Para aplicar corretamente a skin da faca, você precisa digitar {lime}!kill{default}", + "wp_knife_menu_title": "Menu de Facas", + "wp_skin_menu_weapon_title": "Menu de Armas", + "wp_skin_menu_skin_title": "Selecionou a skin para {lime}{0}{default}", + "wp_skin_menu_select": "Você escolheu {lime}{0}{default} como sua skin" +} diff --git a/lang/pt-PT.json b/lang/pt-PT.json new file mode 100644 index 00000000..8ce3784f --- /dev/null +++ b/lang/pt-PT.json @@ -0,0 +1,14 @@ +{ + "wp_prefix": "{lightblue}[WeaponPaints] {default}", + "wp_info_website": "Visita {lime}{0}{default} onde podes mudar as tuas skins", + "wp_info_refresh": "Digita {lime}!wp{default} para sincronizar as tuas skins", + "wp_info_knife": "Digita {lime}!knife{default} para abrir o menu de facas", + "wp_command_cooldown": "{lightred}Tu não podes sincronizar agora as tuas skins", + "wp_command_refresh_done": "{lime}Sincronizando as tuas skins", + "wp_knife_menu_select": "Tu escolheste {lime}{0}{default} como a tua faca", + "wp_knife_menu_kill": "Para aplicar corretamente a skins para a tua faca, digita {lime}!kill{default}", + "wp_knife_menu_title": "Menu Facas", + "wp_skin_menu_weapon_title": "Menu de Armas", + "wp_skin_menu_skin_title": "Escolhe a skin para {lime}{0}{default}", + "wp_skin_menu_select": "Tu escolheste {lime}{0}{default} como a tua skin" +} diff --git a/lang/ru.json b/lang/ru.json new file mode 100644 index 00000000..f337dcea --- /dev/null +++ b/lang/ru.json @@ -0,0 +1,14 @@ +{ + "wp_prefix": "{lightblue}[WeaponPaints] {default}", + "wp_info_website": "Посетите сайт {lime}{0},{default} чтобы выбрать скин", + "wp_info_refresh": "Наберите в чат {lime}!wp{default} для синхронизации выбранных скинов", + "wp_info_knife": "Наберите в чат {lime}!knife,{default} чтобы выбрать нож", + "wp_command_cooldown": "{lightred}Вы не можете выбрать оружие прямо сейчас", + "wp_command_refresh_done": "{lime}Обновление скинов для оружия", + "wp_knife_menu_select": "Вы выбрали {lime}{0}{default} скин для ножа", + "wp_knife_menu_kill": "Чтобы правильно применить скин для ножа, набери в чат {lime}!kill{default}", + "wp_knife_menu_title": "Меню ножей", + "wp_skin_menu_weapon_title": "Меню оружия", + "wp_skin_menu_skin_title": "Выберите скин для {lime}{0}{default}", + "wp_skin_menu_select": "Вы выбрали {lime}{0}{default} скина для оружия" +} \ No newline at end of file diff --git a/lang/tr.json b/lang/tr.json new file mode 100644 index 00000000..e105e21a --- /dev/null +++ b/lang/tr.json @@ -0,0 +1,14 @@ +{ + "wp_prefix": "{lightblue}[WeaponPaints] {default}", + "wp_info_website": "Görünümleri değiştirebileceğiniz {lime}{0}{default} adresini ziyaret edin", + "wp_info_refresh": "Seçilen kaplamyı senkronize etmek için {lime}!wp{default} yazın", + "wp_info_knife": "Bıçak menüsünü açmak için {lime}!knife{default} yazın", + "wp_command_cooldown": "{lightred}Şu anda silah kaplamasını yenileyemezsiniz", + "wp_command_refresh_done": "{lime}Silah kaplaması yenileniyor", + "wp_knife_menu_select": "Bıçağınız olarak {lime}{0}{default} seçtiniz", + "wp_knife_menu_kill": "Bıçak için doğru şekilde kaplama uygulamak için {lime}!kill{default} yazmanız gerekir", + "wp_knife_menu_title": "Bıçak Menüsü", + "wp_skin_menu_weapon_title": "Silah Menüsü", + "wp_skin_menu_skin_title": "Select skin for {lime}{0}{default}", + "wp_skin_menu_select": "Teniniz olarak {lime}{0}{default} seçtiniz" +} diff --git a/lang/ua.json b/lang/ua.json new file mode 100644 index 00000000..7561f5ee --- /dev/null +++ b/lang/ua.json @@ -0,0 +1,14 @@ +{ + "wp_prefix": "{lightblue}[WeaponPaints] {default}", + "wp_info_website": "Відвідайте веб-сайт {lime}{0},{default} щоб вибрати скин", + "wp_info_refresh": "Напишіть у чат {lime}!wp{default} для синхронізації вибраних скинів", + "wp_info_knife": "Напишіть у чат {lime}!knife,{default} щоб вибрати ніж", + "wp_command_cooldown": "{lightred}Ви не можете вибрати зброю зараз", + "wp_command_refresh_done": "{lime}Оновлення скинів для зброї", + "wp_knife_menu_select": "Ви вибрали скин {lime}{0}{default} для ножа", + "wp_knife_menu_kill": "Щоб правильно застосувати скин для ножа, напишіть у чат {lime}!kill{default}", + "wp_knife_menu_title": "Меню ножів", + "wp_skin_menu_weapon_title": "Меню зброї", + "wp_skin_menu_skin_title": "Виберіть скин для {lime}{0}{default}", + "wp_skin_menu_select": "Ви вибрали скин {lime}{0}{default} для зброї" +} \ No newline at end of file diff --git a/lang/zh-cn.json b/lang/zh-cn.json new file mode 100644 index 00000000..2d983585 --- /dev/null +++ b/lang/zh-cn.json @@ -0,0 +1,14 @@ +{ + "wp_prefix": "{lightblue}[武器皮肤] {default}", + "wp_info_website": "在线访问 {lime}{0}{default} 更改你的武器皮肤", + "wp_info_refresh": "输入 {lime}!wp{default} 进行在线皮肤同步", + "wp_info_knife": "输入 {lime}!knife{default} 打开刀菜单", + "wp_command_cooldown": "{lightred}皮肤同步刷新冷却中", + "wp_command_refresh_done": "{lime}刷新武器皮肤中", + "wp_knife_menu_select": "你选择了 {lime}{0}{default} 作为你的刀", + "wp_knife_menu_kill": "如需完全应用皮肤到刀上, 你需要输入 {lime}!kill{default} 自杀来进行刷新", + "wp_knife_menu_title": "刀菜单", + "wp_skin_menu_weapon_title": "武器菜单", + "wp_skin_menu_skin_title": "选择 {lime}{0}{default} 的皮肤", + "wp_skin_menu_select": "你选择了 {lime}{0}{default} 作为你的皮肤" +} diff --git a/website/class/config.php b/website/class/config.php index 14721fd0..34b0c046 100644 --- a/website/class/config.php +++ b/website/class/config.php @@ -5,6 +5,8 @@ define('DB_NAME', ''); define('DB_USER', ''); define('DB_PASS', ''); +define('WEB_STYLE_DARK', true); + define('STEAM_API_KEY', ''); define('STEAM_DOMAIN_NAME', ''); define('STEAM_LOGOUT_PAGE', ''); diff --git a/website/getskins.php b/website/getskins.php deleted file mode 100644 index 95539e97..00000000 --- a/website/getskins.php +++ /dev/null @@ -1,73 +0,0 @@ - 1, -"weapon_elite" => 2, -"weapon_fiveseven" => 3, -"weapon_glock" => 4, -"weapon_ak47" => 7, -"weapon_aug" => 8, -"weapon_awp" => 9, -"weapon_famas" => 10, -"weapon_g3sg1" => 10, -"weapon_galilar" => 13, -"weapon_m249" => 14, -"weapon_m4a1" => 16, -"weapon_mac10" => 17, -"weapon_p90" => 19, -"weapon_mp5sd" => 23, -"weapon_ump45" => 24, -"weapon_xm1014" => 25, -"weapon_bizon" => 26, -"weapon_mag7" => 27, -"weapon_negev" => 28, -"weapon_sawedoff" => 29, -"weapon_tec9" => 30, -"weapon_hkp2000" => 32, -"weapon_mp7" => 33, -"weapon_mp9" => 34, -"weapon_nova" => 35, -"weapon_p250" => 36, -"weapon_scar20" => 38, -"weapon_sg556" => 39, -"weapon_ssg08" => 40, -"weapon_m4a1_silencer" => 60, -"weapon_usp_silencer" => 61, -"weapon_cz75a" => 63, -"weapon_revolver" => 64, -"weapon_bayonet" => 500, -"weapon_knife_css" => 503, -"weapon_knife_flip" => 505, -"weapon_knife_gut" => 506, -"weapon_knife_karambit" => 507, -"weapon_knife_m9_bayonet" => 508, -"weapon_knife_tactical" => 509, -"weapon_knife_falchion" => 512, -"weapon_knife_survival_bowie"=> 514, -"weapon_knife_butterfly" => 515, -"weapon_knife_push" => 516, -"weapon_knife_cord" => 517, -"weapon_knife_canis" => 518, -"weapon_knife_ursus" => 519, -"weapon_knife_gypsy_jackknife" => 520, -"weapon_knife_outdoor" => 521, -"weapon_knife_stiletto" => 522, -"weapon_knife_widowmaker" => 523, -"weapon_knife_skeleton" => 525); -$json = json_decode(file_get_contents('skins.json')); -echo "
";
-foreach($json as $skin)
-{
-	if(!str_contains($skin->weapon->id, "weapon_")) continue;
-	$name = $skin->name;
-	$name = str_replace("'","\'",$name);
-	$weapon = $skin->weapon->id;
-	$image = $skin->image;
-	$paint = $skin->paint_index;
-	echo "('{$weapon}', {$weapons[$weapon]}, {$paint}, '{$image}', '{$name}')";
-	echo ",
"; - -} -//print_r($json); -echo "
"; - -?> diff --git a/website/index.php b/website/index.php index ea6f21fe..5339448e 100644 --- a/website/index.php +++ b/website/index.php @@ -4,14 +4,15 @@ require_once 'class/database.php'; require_once 'steamauth/steamauth.php'; require_once 'class/utils.php'; - $db = new DataBase(); if (isset($_SESSION['steamid'])) { - $steamid = $_SESSION['steamid']; + + include('steamauth/userInfo.php'); + $steamid = $steamprofile['steamid']; $weapons = UtilsClass::getWeaponsFromArray(); $skins = UtilsClass::skinsFromJson(); - $querySelected = $query3 = $db->select("SELECT `weapon_defindex`, `weapon_paint_id` FROM `wp_player_skins` WHERE `wp_player_skins`.`steamid` = :steamid", ["steamid" => $steamid]); + $querySelected = $query3 = $db->select("SELECT `weapon_defindex`, `weapon_paint_id`, `weapon_wear` FROM `wp_player_skins` WHERE `wp_player_skins`.`steamid` = :steamid", ["steamid" => $steamid]); $selectedSkins = UtilsClass::getSelectedSkins($querySelected); $selectedKnife = $db->select("SELECT * FROM `wp_player_knife` WHERE `wp_player_knife`.`steamid` = :steamid", ["steamid" => $steamid])[0]; $knifes = UtilsClass::getKnifeTypes(); @@ -22,31 +23,30 @@ if (isset($_SESSION['steamid'])) { if ($ex[0] == "knife") { $db->query("INSERT INTO `wp_player_knife` (`steamid`, `knife`) VALUES(:steamid, :knife) ON DUPLICATE KEY UPDATE `knife` = :knife", ["steamid" => $steamid, "knife" => $knifes[$ex[1]]['weapon_name']]); } else { - if (!is_int($ex[1])) - header("Location: index.php"); - if (array_key_exists($ex[1], $skins[$ex[0]])) { + if (array_key_exists($ex[1], $skins[$ex[0]]) && isset($_POST['wear']) && $_POST['wear'] >= 0.00 && $_POST['wear'] <= 1.00 && isset($_POST['seed'])) { + $wear = floatval($_POST['wear']); // wear + $seed = intval($_POST['seed']); // seed if (array_key_exists($ex[0], $selectedSkins)) { - $db->query("UPDATE wp_player_skins SET weapon_paint_id = :weapon_paint_id WHERE steamid = :steamid AND weapon_defindex = :weapon_defindex", ["steamid" => $steamid, "weapon_defindex" => $ex[0], "weapon_paint_id" => $ex[1]]); + $db->query("UPDATE wp_player_skins SET weapon_paint_id = :weapon_paint_id, weapon_wear = :weapon_wear, weapon_seed = :weapon_seed WHERE steamid = :steamid AND weapon_defindex = :weapon_defindex", ["steamid" => $steamid, "weapon_defindex" => $ex[0], "weapon_paint_id" => $ex[1], "weapon_wear" => $wear, "weapon_seed" => $seed]); } else { - $db->query("INSERT INTO wp_player_skins (`steamid`, `weapon_defindex`, `weapon_paint_id`) VALUES (:steamid, :weapon_defindex, :weapon_paint_id)", ["steamid" => $steamid, "weapon_defindex" => $ex[0], "weapon_paint_id" => $ex[1]]); + $db->query("INSERT INTO wp_player_skins (`steamid`, `weapon_defindex`, `weapon_paint_id`, `weapon_wear`, `weapon_seed`) VALUES (:steamid, :weapon_defindex, :weapon_paint_id, :weapon_wear, :weapon_seed)", ["steamid" => $steamid, "weapon_defindex" => $ex[0], "weapon_paint_id" => $ex[1], "weapon_wear" => $wear, "weapon_seed" => $seed]); } } } - header("Location: index.php"); + header("Location: {$_SERVER['PHP_SELF']}"); } } ?> - +> - - + + + + CS2 Simple Weapon Paints @@ -59,9 +59,9 @@ if (isset($_SESSION['steamid'])) { loginbutton("rectangle"); echo ""; } else { - echo "
Your current weapon skin loadout
"; + echo "

Your current weapon skin loadout Logout

"; echo "
"; - ?> + ?>
@@ -132,14 +132,132 @@ if (isset($_SESSION['steamid'])) { } ?> - +

+ select("SELECT 1 FROM `wp_player_skins` WHERE `steamid` = :steamid AND `weapon_defindex` = :defindex", ["steamid" => $steamid, "defindex" => $defindex]); + $hasSkinData = !empty($queryCheck); + + if ($selectedSkinInfo && $hasSkinData) : + ?> + + + + + + +
+ + select("SELECT `weapon_wear` FROM `wp_player_skins` WHERE `steamid` = :steamid AND `weapon_defindex` = :weapon_defindex", ["steamid" => $steamid, "weapon_defindex" => $defindex]); + $selectedSkinInfo = isset($selectedSkins[$defindex]) ? $selectedSkins[$defindex] : null; + $initialWearValue = isset($selectedSkinInfo['weapon_wear']) ? $selectedSkinInfo['weapon_wear'] : (isset($queryWear[0]['weapon_wear']) ? $queryWear[0]['weapon_wear'] : 0); + + // seed value + $querySeed = $db->select("SELECT `weapon_seed` FROM `wp_player_skins` WHERE `steamid` = :steamid AND `weapon_defindex` = :weapon_defindex", ["steamid" => $steamid, "weapon_defindex" => $defindex]); + $selectedSkinInfo = isset($selectedSkins[$defindex]) ? $selectedSkins[$defindex] : null; + $initialSeedValue = isset($selectedSkinInfo['weapon_seed']) ? $selectedSkinInfo['weapon_seed'] : (isset($querySeed[0]['weapon_seed']) ? $querySeed[0]['weapon_seed'] : 0); + ?> + + +
+ - + +
+ +
diff --git a/website/preview.png b/website/preview.png index bc02c97d..1e2b3f4b 100644 Binary files a/website/preview.png and b/website/preview.png differ