Compare commits

...

113 Commits

Author SHA1 Message Date
Nereziel
74a6dff114 Merge pull request #88 from daffyyyy/some-changes
Update for cssharp 101
2023-12-04 21:03:07 +01:00
daffyyyy
e37f111f1b Update WeaponPaints.csproj 2023-12-04 21:01:36 +01:00
daffyyyy
5e6286b667 Update for cssharp 101 2023-12-04 21:00:24 +01:00
Nereziel
ed4da98a50 edit default fallback
+removed unused php
2023-12-03 21:25:04 +01:00
Nereziel
c825febeac Merge pull request #83 from daffyyyy/some-changes
Fix for !wp and comments cleanup
2023-12-03 13:03:45 +01:00
daffyyyy
1e5c2a439f Fix for !wp and comments cleanup 2023-12-03 13:00:37 +01:00
Nereziel
21eccfb6d9 Merge pull request #82 from daffyyyy/some-changes
CounterStrikeSharp v90
2023-12-02 16:45:01 +01:00
daffyyyy
a6b193cd13 CounterStrikeSharp v90
Adapted for counterstrikesharp v90
2023-12-02 13:52:06 +01:00
Nereziel
b992433d44 Merge pull request #80 from daffyyyy/some-changes
Changes
2023-12-02 12:56:55 +01:00
daffyyyy
06559af230 Changes 2023-12-02 12:54:13 +01:00
Nereziel
dfe6b200d8 Merge pull request #79 from daffyyyy/some-changes
Workaround for long player authorization
2023-11-30 17:43:32 +01:00
daffyyyy
3edcb5e52f Workaround for long player authorization 2023-11-30 17:28:40 +01:00
Nereziel
28936e9bb5 Merge pull request #78 from daffyyyy/some-changes
GlobalShare fix
2023-11-30 14:29:00 +01:00
daffyyyy
2772fb59b9 GlobalShare fix 2023-11-30 14:18:57 +01:00
Nereziel
0f6bb26ac9 Merge pull request #76 from daffyyyy/some-changes
Improved player index
2023-11-30 10:34:07 +01:00
daffyyyy
be51e4d1e9 Improved player index
The latest version of cssharp adds player.Index instead of entityIndex and does not allow to compile
2023-11-30 02:20:16 +01:00
Nereziel
0d521e0cd3 Merge pull request #75 from daffyyyy/some-changes
Some fixes
2023-11-29 20:45:16 +01:00
daffyyyy
00f920713d Some fixes 2023-11-29 19:39:56 +01:00
Nereziel
42d70690eb Merge pull request #74 from daffyyyy/some-changes
Back to onclientauthorized
2023-11-29 13:52:25 +01:00
daffyyyy
73e3a05270 Back to onclientauthorized
Thanks cssharp :D
2023-11-29 12:37:08 +01:00
Nereziel
dd9e01db54 Merge pull request #73 from daffyyyy/some-changes
Major changes
2023-11-29 12:09:42 +01:00
daffyyyy
393281ea12 Maybe, maybe 👍 2023-11-29 11:58:34 +01:00
daffyyyy
a5787d95e0 improved refreshing skins 2023-11-29 11:56:07 +01:00
daffyyyy
6a7b15e942 Update WeaponAction.cs 2023-11-29 00:41:26 +01:00
daffyyyy
8e7a2a2923 fixxx 2023-11-28 23:44:28 +01:00
daffyyyy
93e8af07d2 Changes changes 2023-11-28 00:40:43 +01:00
daffyyyy
7e5485b209 Major changes 2023-11-27 01:52:23 +01:00
Nereziel
414818e314 Merge pull request #69 from daffyyyy/fix-knife-3
TEMP FIX FOR KNIVES
2023-11-26 18:30:24 +01:00
daffyyyy
a42e9a37fc TEMP FIX FOR KNIVES 2023-11-26 18:19:15 +01:00
Nereziel
dcd68aed67 Merge pull request #68 from daffyyyy/fix-knife-3
IMPORTANT KNIVES FIX
2023-11-26 15:25:01 +01:00
daffyyyy
7bb97af619 IMPORTANT KNIVES FIX 2023-11-26 15:16:01 +01:00
Nereziel
cdc58df13c Merge pull request #67 from daffyyyy/fix-knife-3
Fixees
2023-11-25 23:30:31 +01:00
daffyyyy
fe5cc9a82a Update WeaponPaints.cs 2023-11-25 23:17:56 +01:00
daffyyyy
01542a7dc6 Fixees 2023-11-25 23:12:17 +01:00
Nereziel
e1f24abd5d removed exception 2023-11-25 13:10:52 +01:00
Nereziel
646050fb72 Merge pull request #64 from daffyyyy/fix-knife-2
Still knife fix :D (Previous sometimes give random knife to player)
2023-11-24 08:11:37 +01:00
daffyyyy
87dadb9c62 Update WeaponPaints.cs 2023-11-23 01:32:07 +01:00
Nereziel
cf421c5614 Merge pull request #63 from daffyyyy/fix-knife-2
Attempting to repair knives when a map or other plugin was giving them
2023-11-23 00:33:16 +01:00
daffyyyy
fc64e1d261 Additional checks 2023-11-23 00:21:04 +01:00
Nereziel
45fc4a86a5 Merge pull request #61 from daffyyyy/feature/random-skins
Random skins and more
2023-11-22 18:28:21 +01:00
daffyyyy
ad105e5cd2 Merge branch 'feature/random-skins' of https://github.com/daffyyyy/cs2-WeaponPaints into feature/random-skins 2023-11-21 23:54:17 +01:00
daffyyyy
f668f56d66 Give knife on spawn 2023-11-21 23:54:12 +01:00
Dawid Bepierszcz
de6672507a Update build.yml 2023-11-21 22:22:54 +01:00
daffyyyy
2855d4e9ae I baked new things
;)
2023-11-21 21:55:57 +01:00
Nereziel
0c05c25e8e messed discord link 2023-11-21 21:00:12 +01:00
Nereziel
8335ede7d7 Update README.md 2023-11-21 20:07:52 +01:00
Nereziel
d79246f161 Update build.yml 2023-11-21 20:06:31 +01:00
Nereziel
a504506129 test gh act 2023-11-21 20:03:53 +01:00
Nereziel
7bada81eb9 Update build.yml 2023-11-21 20:02:15 +01:00
Nereziel
2929735429 fix php min version 2023-11-21 19:51:10 +01:00
Nereziel
04bb7a2575 Merge pull request #56 from crashzk/patch-1
Update README.md
2023-11-21 19:44:51 +01:00
crashzk
f00ba48f60 Merge branch 'main' into patch-1 2023-11-21 15:43:49 -03:00
crashzk
08342e4a99 Update README.md 2023-11-21 15:43:02 -03:00
Nereziel
29a6041d7a fixed discord link 2023-11-21 19:35:52 +01:00
crashzk
c44433766c Update README.md 2023-11-19 19:22:28 -03:00
crashzk
a987ed972a Update README.md 2023-11-19 19:20:33 -03:00
Nereziel
50777661c5 Merge pull request #55 from daffyyyy/patch-1
Fix when player select "Select skin" as skin
2023-11-19 22:52:08 +01:00
Dawid Bepierszcz
31fd014f55 Fix when player select "Select skin" as skin 2023-11-19 22:42:32 +01:00
Nereziel
760429e644 Merge pull request #54 from daffyyyy/update-readme-1
Update README.md
2023-11-19 22:36:28 +01:00
Dawid Bepierszcz
304d8501cc Update README.md 2023-11-19 22:25:38 +01:00
Nereziel
e9f7db5171 Merge pull request #51 from daffyyyy/website-fix-1
Fixed blank page
2023-11-19 21:14:15 +01:00
Dawid Bepierszcz
648b928b4e Update index.php 2023-11-19 21:10:55 +01:00
Nereziel
530a7d64c7 Update README.md 2023-11-19 20:45:12 +01:00
Nereziel
3453f4c505 Merge pull request #47 from daffyyyy/feature/global-share
EXPERIMENTAL (GlobalShare) - Website for all servers
2023-11-19 19:03:05 +01:00
Nereziel
25b466422b Update build.yml 2023-11-19 19:02:26 +01:00
daffyyyy
6baa59dd9b Update WeaponPaints.cs
New version
2023-11-19 12:30:47 +01:00
daffyyyy
10afe7ce1e Update WeaponPaints.cs 2023-11-19 12:24:57 +01:00
daffyyyy
5eeb0c5fec Initial 2023-11-19 02:07:29 +01:00
Nereziel
e53ee27b39 Merge pull request #44 from daffyyyy/fix-knives_finally
Finally fixed knife problem?
2023-11-18 19:26:37 +01:00
Dawid Bepierszcz
75112b02fe Update WeaponPaints.cs 2023-11-18 19:21:28 +01:00
Nereziel
8e92635231 sync paints on plugin reload 2023-11-18 15:20:26 +01:00
Nereziel
6923295cb6 Update build.yml 2023-11-18 15:14:40 +01:00
Nereziel
e5f98077f3 Update build.yml 2023-11-18 15:12:57 +01:00
Nereziel
3296055841 Merge pull request #42 from daffyyyy/enhancement/mysql-wrapper
Creating tables fix
2023-11-18 11:18:15 +01:00
Dawid Bepierszcz
89de192c20 Update WeaponPaints.cs
Additional check
2023-11-18 09:15:20 +01:00
Dawid Bepierszcz
a1297b9cbe Update WeaponPaints.cs
Creating tables fix
2023-11-18 08:54:33 +01:00
Nereziel
99af078b32 Merge pull request #40 from daffyyyy/enhancement/mysql-wrapper
New mysql stuff, and small changes
2023-11-18 01:29:06 +01:00
Dawid Bepierszcz
b0bef746cd Delete website/database.sql 2023-11-17 23:28:33 +01:00
daffyyyy
117a95e99c New mysql stuff, and small changes 2023-11-17 23:27:07 +01:00
Nereziel
a6d4108ca7 Merge pull request #37 from daffyyyy/hotfix-2
Fixed server crashing
2023-11-16 20:51:10 +01:00
Dawid Bepierszcz
7b45169d8d Update WeaponPaints.cs
Typo
2023-11-16 20:42:20 +01:00
Dawid Bepierszcz
42bd45c3f0 Update WeaponPaints.cs
Fixed crashing
2023-11-16 20:38:35 +01:00
Nereziel
5262739c3d Merge pull request #35 from daffyyyy/fix-knife-1
Fix for knife disappearing and more changes


    Fix for knife disappearing
    Possibility to change commands names
    Skin refresh now also works for knife
    Possibility to set a random knife for players who have not set any via GiveRandomKnife in config
    And probably some smaller changes
2023-11-16 11:37:58 +01:00
Dawid Bepierszcz
c82a0c0d26 Update Config.cs
Changed refresh command to wp by default
2023-11-16 11:30:16 +01:00
Dawid Bepierszcz
9ad7f9fc83 Update Config.cs 2023-11-16 11:23:59 +01:00
Dawid Bepierszcz
9e2dac52e5 Update WeaponPaints.cs 2023-11-16 11:23:26 +01:00
Nereziel
118931f44d Merge pull request #34 from daffyyyy/enhancement/website-knife-choosing
Choosing knife on website
2023-11-16 00:39:19 +01:00
Nereziel
843ae34e31 Merge pull request #33 from daffyyyy/enhancement/config-expansion
Added an additional section to the config
2023-11-16 00:38:33 +01:00
Dawid Bepierszcz
46a720ff83 default knife image 2023-11-16 00:10:26 +01:00
Dawid Bepierszcz
63d059b607 Update utils.php 2023-11-16 00:07:27 +01:00
Dawid Bepierszcz
005ef18338 Update index.php 2023-11-16 00:06:16 +01:00
Dawid Bepierszcz
c0c1fe2566 Update WeaponPaints.cs 2023-11-16 00:05:34 +01:00
Dawid Bepierszcz
c4ad17a7ed MinimumApiVersion 2023-11-15 22:46:42 +01:00
Dawid Bepierszcz
99fde6ec52 Update Config.cs 2023-11-15 22:35:57 +01:00
Dawid Bepierszcz
ca2c3eb212 Update WeaponPaints.cs 2023-11-15 22:35:19 +01:00
Nereziel
b5a4577af1 Update README.md 2023-11-15 22:15:38 +01:00
Nereziel
ba93e4a3aa Update build.yml 2023-11-15 21:22:24 +01:00
Nereziel
7d435da9d1 Update build.yml 2023-11-15 21:19:48 +01:00
Nereziel
50008cf74a Merge pull request #32 from daffyyyy/enhancement/website-skins
Website now use json file to load skins
2023-11-15 20:52:20 +01:00
Dawid Bepierszcz
4b9b169483 Update database.sql, remove weapon_paints 2023-11-15 20:47:16 +01:00
Dawid Bepierszcz
25d251deff Skins in json format 2023-11-15 20:45:55 +01:00
Dawid Bepierszcz
d8f7798f9f New utils class 2023-11-15 20:44:46 +01:00
Dawid Bepierszcz
fb023aff5b Update index.php to base on json 2023-11-15 20:44:18 +01:00
Dawid Bepierszcz
6c91e20a4d Delete website/skins.json
Not needed
2023-11-15 20:43:28 +01:00
Nereziel
93dae346d0 Merge pull request #31 from daffyyyy/patch-giving_knife
Improve knife giving
2023-11-15 18:20:48 +01:00
Dawid Bepierszcz
8e0b1cc99a Update WeaponPaints.cs
respecting the team
2023-11-15 18:18:31 +01:00
Dawid Bepierszcz
c99acbcdb3 Update WeaponPaints.cs
Improved knife giving
And small changes to the code
2023-11-15 18:03:37 +01:00
Nereziel
8beaee94da Merge pull request #28 from daffyyyy/improvement-website-2
Weapon paint  images locally
2023-11-15 16:43:59 +01:00
Nereziel
62238b4446 Merge pull request #27 from daffyyyy/fix-visibility_skins
Weird fix of skin visibility, but at the moment it works xD
2023-11-15 15:03:29 +01:00
Nereziel
03f194ac4e Added discord server 2023-11-15 13:14:12 +01:00
daffyyyy
e775b05f9f Upload paint images
Added paint images
2023-11-14 23:13:55 +01:00
Dawid Bepierszcz
b09b97bbd5 Update database.sql
Paints image locally
2023-11-14 23:06:10 +01:00
Dawid Bepierszcz
e441ccc601 Update WeaponPaints.cs
Weird fix of skin visibility, but at the moment it works xD
2023-11-14 22:49:59 +01:00
1713 changed files with 1930 additions and 116094 deletions

View File

@@ -3,8 +3,16 @@ name: Build
on:
push:
branches: [ "main" ]
paths-ignore:
- '**/README.md'
- '**/.gitignore'
- '**/LICENSE'
pull_request:
branches: [ "main" ]
paths-ignore:
- '**/README.md'
- '**/.gitignore'
- '**/LICENSE'
env:
BUILD_NUMBER: ${{ github.run_number }}
@@ -12,7 +20,6 @@ env:
PROJECT_NAME: "WeaponPaints"
OUTPUT_PATH: "./WeaponPaints"
jobs:
build:
permissions: write-all
@@ -28,19 +35,40 @@ jobs:
run: dotnet restore
- name: Build
run: dotnet build ${{ env.PROJECT_PATH }} -c WeaponPaints -o ${{ env.OUTPUT_PATH }}
publish:
if: github.event_name == 'push'
permissions: write-all
runs-on: ubuntu-latest
needs: build
steps:
- uses: actions/checkout@v3
- name: Setup .NET
uses: actions/setup-dotnet@v3
with:
dotnet-version: 7.0.x
- name: Restore
run: dotnet restore
- name: Build
run: dotnet build ${{ env.PROJECT_PATH }} -c WeaponPaints -o ${{ env.OUTPUT_PATH }}
- name: Clean files
run: |
rm -f \
${{ env.OUTPUT_PATH }}/CounterStrikeSharp.API.dll \
${{ env.OUTPUT_PATH }}/McMaster.NETCore.Plugins.dll \
${{ env.OUTPUT_PATH }}/Microsoft.DotNet.PlatformAbstractions.dll \
${{ env.OUTPUT_PATH }}/Microsoft.Extensions.DependencyModel.dll
${{ env.OUTPUT_PATH }}/Microsoft.Extensions.DependencyModel.dll \
- name: Copy skins.json
run: cp website/data/skins.json ${{ env.OUTPUT_PATH }}/skins.json
- name: Zip
uses: thedoctor0/zip-release@0.7.5
with:
type: 'zip'
filename: '${{ env.PROJECT_NAME }}.zip'
path: ${{ env.OUTPUT_PATH }}
- name: Clean files Website
run: |
rm -rf website/img/
- name: Zip
uses: thedoctor0/zip-release@0.7.5
with:
@@ -54,4 +82,4 @@ jobs:
name: "Build ${{ env.BUILD_NUMBER }}"
tag: "build-${{ env.BUILD_NUMBER }}"
body: |
Place the plugin in game/csgo/addons/counterstrikesharp/plugins/WeaponPaints
Place the plugin in game/csgo/addons/counterstrikesharp/plugins/WeaponPaints

286
Commands.cs Normal file
View File

@@ -0,0 +1,286 @@
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;
string temp = "";
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(Config.Messages.SuccessRefreshCommand))
{
temp = $" {Config.Prefix} {Config.Messages.SuccessRefreshCommand}";
player!.PrintToChat(Utility.ReplaceTags(temp));
}
return;
}
if (!string.IsNullOrEmpty(Config.Messages.CooldownRefreshCommand))
{
temp = $" {Config.Prefix} {Config.Messages.CooldownRefreshCommand}";
player!.PrintToChat(Utility.ReplaceTags(temp));
}
}
private void OnCommandWS(CCSPlayerController? player, CommandInfo command)
{
if (!Config.Additional.SkinEnabled) return;
if (!Utility.IsPlayerValid(player)) return;
string temp;
if (!string.IsNullOrEmpty(Config.Messages.WebsiteMessageCommand))
{
temp = $" {Config.Prefix} {Config.Messages.WebsiteMessageCommand}";
player!.PrintToChat(Utility.ReplaceTags(temp));
}
if (!string.IsNullOrEmpty(Config.Messages.SynchronizeMessageCommand))
{
temp = $" {Config.Prefix} {Config.Messages.SynchronizeMessageCommand}";
player!.PrintToChat(Utility.ReplaceTags(temp));
}
if (!Config.Additional.KnifeEnabled) return;
if (!string.IsNullOrEmpty(Config.Messages.KnifeMessageCommand))
{
temp = $" {Config.Prefix} {Config.Messages.KnifeMessageCommand}";
player!.PrintToChat(Utility.ReplaceTags(temp));
}
}
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(Utility.ReplaceTags($" {Config.Messages.KnifeMenuTitle}"));
var handleGive = (CCSPlayerController? player, ChatMenuOption option) =>
{
if (Utility.IsPlayerValid(player))
{
var knifeName = option.Text;
var knifeKey = knivesOnly.FirstOrDefault(x => x.Value == knifeName).Key;
if (!string.IsNullOrEmpty(knifeKey))
{
string temp = "";
if (!string.IsNullOrEmpty(Config.Messages.ChosenKnifeMenu))
{
temp = $" {Config.Prefix} {Config.Messages.ChosenKnifeMenu}".Replace("{KNIFE}", knifeName);
player!.PrintToChat(Utility.ReplaceTags(temp));
}
if (!string.IsNullOrEmpty(Config.Messages.ChosenKnifeMenuKill) && Config.Additional.CommandKillEnabled)
{
temp = $" {Config.Prefix} {Config.Messages.ChosenKnifeMenuKill}";
player!.PrintToChat(Utility.ReplaceTags(temp));
}
g_playersKnife[(int)player!.Index] = knifeKey;
if (player!.PawnIsAlive && g_bCommandsAllowed)
{
RefreshWeapons(player);
}
if (weaponSync != null)
Task.Run(() => weaponSync.SyncKnifeToDatabase((int)player.Index, 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(Config.Messages.CooldownRefreshCommand))
{
string temp = $" {Config.Prefix} {Config.Messages.CooldownRefreshCommand}";
player.PrintToChat(Utility.ReplaceTags(temp));
}
});
}
private void SetupSkinsMenu()
{
var classNamesByWeapon = weaponList.ToDictionary(kvp => kvp.Value, kvp => kvp.Key);
var weaponSelectionMenu = new ChatMenu(Utility.ReplaceTags($" {Config.Messages.WeaponMenuTitle}"));
// Function to handle skin selection for a specific weapon
var handleWeaponSelection = (CCSPlayerController? player, ChatMenuOption option) =>
{
if (!Utility.IsPlayerValid(player)) return;
int playerIndex = (int)player!.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(Utility.ReplaceTags($" {Config.Messages.SkinMenuTitle}").Replace("{WEAPON}", selectedWeapon));
// Function to handle skin selection for the chosen weapon
var handleSkinSelection = (CCSPlayerController? p, ChatMenuOption opt) =>
{
if (p == null || !p.IsValid || 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))
{
string temp = $" {Config.Prefix} {Config.Messages.ChosenSkinMenu}".Replace("{SKIN}", selectedSkin);
p.PrintToChat(Utility.ReplaceTags(temp));
if (!gPlayerWeaponsInfo[playerIndex].ContainsKey(weaponDefIndex))
{
gPlayerWeaponsInfo[playerIndex][weaponDefIndex] = new WeaponInfo();
}
gPlayerWeaponsInfo[playerIndex][weaponDefIndex].Paint = paintID;
gPlayerWeaponsInfo[playerIndex][weaponDefIndex].Wear = 0.0f;
gPlayerWeaponsInfo[playerIndex][weaponDefIndex].Seed = 0;
if (!Config.GlobalShare)
{
if (weaponSync == null) return;
Task.Run(async () =>
{
await weaponSync.SyncWeaponPaintsToDatabase(p);
});
}
}
};
// 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(Config.Messages.CooldownRefreshCommand))
{
string temp = $"{Config.Prefix} {Config.Messages.CooldownRefreshCommand}";
player.PrintToChat(Utility.ReplaceTags(temp));
}
});
}
}
}

133
Config.cs
View File

@@ -3,54 +3,107 @@ 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("KnifeMenuTitle")]
public string KnifeMenuTitle { get; set; } = "Knife Menu.";
}
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 WeaponPaintsConfig : BasePluginConfig
{
public override int Version { get; set; } = 2;
public class Additional
{
[JsonPropertyName("SkinVisibilityFix")]
public bool SkinVisibilityFix { get; set; } = true;
[JsonPropertyName("DatabaseHost")]
public string DatabaseHost { get; set; } = "";
[JsonPropertyName("KnifeEnabled")]
public bool KnifeEnabled { get; set; } = true;
[JsonPropertyName("DatabasePort")]
public int DatabasePort { get; set; } = 3306;
[JsonPropertyName("SkinEnabled")]
public bool SkinEnabled { get; set; } = true;
[JsonPropertyName("DatabaseUser")]
public string DatabaseUser { get; set; } = "";
[JsonPropertyName("CommandWpEnabled")]
public bool CommandWpEnabled { get; set; } = true;
[JsonPropertyName("DatabasePassword")]
public string DatabasePassword { get; set; } = "";
[JsonPropertyName("CommandKillEnabled")]
public bool CommandKillEnabled { get; set; } = true;
[JsonPropertyName("DatabaseName")]
public string DatabaseName { get; set; } = "";
[JsonPropertyName("CommandKnife")]
public string CommandKnife { get; set; } = "knife";
[JsonPropertyName("CmdRefreshCooldownSeconds")]
public int CmdRefreshCooldownSeconds { get; set; } = 60;
[JsonPropertyName("CommandSkin")]
public string CommandSkin { get; set; } = "ws";
[JsonPropertyName("Prefix")]
public string Prefix { get; set; } = "[WeaponPaints]";
[JsonPropertyName("CommandSkinSelection")]
public string CommandSkinSelection { get; set; } = "skins";
[JsonPropertyName("Website")]
public string Website { get; set; } = "example.com/skins";
[JsonPropertyName("CommandRefresh")]
public string CommandRefresh { get; set; } = "wp";
[JsonPropertyName("CommandKill")]
public string CommandKill { get; set; } = "kill";
[JsonPropertyName("GiveRandomKnife")]
public bool GiveRandomKnife { get; set; } = false;
[JsonPropertyName("GiveRandomSkin")]
public bool GiveRandomSkin { get; set; } = false;
}
public class WeaponPaintsConfig : BasePluginConfig
{
public override int Version { get; set; } = 4;
[JsonPropertyName("DatabaseHost")]
public string DatabaseHost { get; set; } = "";
[JsonPropertyName("DatabasePort")]
public int DatabasePort { get; set; } = 3306;
[JsonPropertyName("DatabaseUser")]
public string DatabaseUser { get; set; } = "";
[JsonPropertyName("DatabasePassword")]
public string DatabasePassword { get; set; } = "";
[JsonPropertyName("DatabaseName")]
public string DatabaseName { get; set; } = "";
[JsonPropertyName("GlobalShare")]
public bool GlobalShare { get; set; } = false;
[JsonPropertyName("CmdRefreshCooldownSeconds")]
public int CmdRefreshCooldownSeconds { get; set; } = 60;
[JsonPropertyName("Prefix")]
public string Prefix { get; set; } = "[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();
}
[JsonPropertyName("Messages")]
public Messages Messages { get; set; } = new Messages();
}
}

265
Events.cs Normal file
View File

@@ -0,0 +1,265 @@
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 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.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)
{
CCSPlayerController? player = @event.Userid;
if (!Utility.IsPlayerValid(player) || !player.PawnIsAlive || g_knifePickupCount[(int)player.Index] >= 2) return HookResult.Continue;
if (g_playersKnife.ContainsKey((int)player.Index)
&&
g_playersKnife[(int)player.Index] != "weapon_knife")
{
g_knifePickupCount[(int)player.Index]++;
RemovePlayerKnife(player, true);
AddTimer(0.3f, () => 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<CCSPlayerController> 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);
}
}, 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)
{
return HookResult.Continue;
}
if (Config.Additional.KnifeEnabled)
{
g_knifePickupCount[(int)player.Index] = 0;
if (!PlayerHasKnife(player))
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<Listeners.OnEntitySpawned>(OnEntitySpawned);
RegisterListener<Listeners.OnClientAuthorized>(OnClientAuthorized);
RegisterListener<Listeners.OnClientDisconnect>(OnClientDisconnect);
RegisterListener<Listeners.OnMapStart>(OnMapStart);
RegisterEventHandler<EventPlayerConnectFull>(OnPlayerConnectFull);
RegisterEventHandler<EventPlayerSpawn>(OnPlayerSpawn);
RegisterEventHandler<EventRoundStart>(OnRoundStart, HookMode.Pre);
RegisterEventHandler<EventRoundEnd>(OnRoundEnd);
RegisterEventHandler<EventItemPurchase>(OnEventItemPurchasePost);
RegisterEventHandler<EventItemPickup>(OnItemPickup);
}
/* 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;
}
*/
}
}

11
PlayerInfo.cs Normal file
View File

@@ -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; }
}
}

108
README.md
View File

@@ -1,33 +1,85 @@
# cs2-WeaponPaints
# CS2 Weapon Paints
[![ko-fi](https://ko-fi.com/img/githubbutton_sm.svg)](https://ko-fi.com/E1E2G0P2O) or [Donate on Steam](https://steamcommunity.com/tradeoffer/new/?partner=41515647&token=gW2W-nXE)
## Description
Unfinished, unoptimized and not fully functional ugly demo weapon paints plugin for **[CSSharp](https://docs.cssharp.dev/)**.
## Created [Discord server](https://discord.gg/d9CvaYPSFe) where you can discus about plugin.
### Consider to donate instead of buying from unknown sources.
[![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 ""`**;
## 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`**;
## Plugin Configuration
<details>
<summary>Click to expand</summary>
<code><pre>{
"Version": 4, // Don't touch
"DatabaseHost": "", // MySQL host (required if GlobalShare = false)
"DatabasePort": 3306, // MySQL port (required if GlobalShare = false)
"DatabaseUser": "", // MySQL username (required if GlobalShare = false)
"DatabasePassword": "", // MySQL user password (required if GlobalShare = false)
"DatabaseName": "", // MySQL database name (required if GlobalShare = false)
"GlobalShare": false, // Enable or disable GlobalShare, plugin can work without mysql credentials but with shared website at weaponpaints.fun
"CmdRefreshCooldownSeconds": 60, // Cooldown time in refreshing skins (!wp command)
"Prefix": "[WeaponPaints]", // Prefix every chat message
"Website": "example.com/skins", // Website used in WebsiteMessageCommand (!ws command)
"Messages": {
"WebsiteMessageCommand": "Visit {WEBSITE} where you can change skins.", // Information about website where player can change skins (!ws command) Set to empty to disable
"SynchronizeMessageCommand": "Type !wp to synchronize chosen skins.", // Information about skins refreshing (!ws command) Set to empty to disable
"KnifeMessageCommand": "Type !knife to open knife menu.", // Information about knife menu (!ws command) Set to empty to disable
"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)
"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
"KnifeEnabled": true, // Enable or disable knife feature
"SkinEnabled": true, // Enable or disable skin feature
"CommandWpEnabled": true, // Enable or disable refreshing command
"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
"GiveRandomSkins": false // Give random skins to players if they didn't choose
},
"ConfigVersion": 4 // Don't touch
}</pre></code>
</details>
## 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;
## Known issues
- Issue on Windows servers, no knives are given.
### Use this plugin at your own risk! Using this may lead to GSLT ban or something else Valve come with. [Valve Server guidelines](https://blog.counter-strike.net/index.php/server_guidelines/)
### Description
Unfinished, unoptimized and not fully functional ugly demo weapon paints plugin for [CSSharp](https://docs.cssharp.dev/).
### Features
- changes only paint, seed and wear on weapons and knives
- mysql based
- 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 ""`
### CS2 server:
- compile and copy plugin to plugins. Info here [https://docs.cssharp.dev/guides/hello-world-plugin/](https://docs.cssharp.dev/guides/hello-world-plugin/)
- setup `addons/counterstrikesharp/configs/plugins/WeaponPaints/WeaponPaints.json` with database credentials
- in `addons/counterstrikesharp/configs/core.json` set **FollowCS2ServerGuidelines** to **false**
### Web install:
- requires PHP (tested on php ver `8.2.3` and nginx webserver)
- copy website to web server
- import `database.sql` to mysql
- get steam api key [https://steamcommunity.com/dev/apikey](https://steamcommunity.com/dev/apikey)
- fill in database credentials and api key in `class/config.php`
- visit website and login via steam
### Preview
## Preview
![preview](https://github.com/Nereziel/cs2-WeaponPaints/blob/main/website/preview.png?raw=true)

144
Utility.cs Normal file
View File

@@ -0,0 +1,144 @@
using CounterStrikeSharp.API.Core;
using CounterStrikeSharp.API.Modules.Utils;
using Dapper;
using MySqlConnector;
using Newtonsoft.Json.Linq;
using Newtonsoft.Json;
using System.Reflection;
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<List<JObject>>(json);
WeaponPaints.skinsList = deserializedSkins ?? new List<JObject>();
}
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 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();
}
}
}

309
WeaponAction.cs Normal file
View File

@@ -0,0 +1,309 @@
using CounterStrikeSharp.API;
using CounterStrikeSharp.API.Core;
using CounterStrikeSharp.API.Modules.Memory;
using CounterStrikeSharp.API.Modules.Utils;
namespace WeaponPaints
{
public partial class WeaponPaints
{
internal static void ChangeWeaponAttributes(CBasePlayerWeapon? weapon, CCSPlayerController? player, bool isKnife = false)
{
if (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 (_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.IsValid)
{
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<nint, nint>(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<CEntityInstance>(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)
{
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);
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<CEntityInstance>(weaponEntityIndex);
if (knife != null && knife.IsValid)
{
knife.Remove();
}
});
}
else
{
weapon.Value.Remove();
}
break;
}
}
}
}
}
private static int GetRandomPaint(int defindex)
{
Random rnd = new Random();
if (WeaponPaints.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 CSkeletonInstance GetSkeletonInstance(CGameSceneNode node)
{
Func<nint, nint> GetSkeletonInstance = VirtualFunction.Create<nint, nint>(node.Handle, 8);
return new CSkeletonInstance(GetSkeletonInstance(node.Handle));
}
}
}

9
WeaponInfo.cs Normal file
View File

@@ -0,0 +1,9 @@
namespace WeaponPaints
{
public class WeaponInfo
{
public int Paint { get; set; }
public int Seed { get; set; }
public float Wear { get; set; }
}
}

View File

@@ -1,431 +1,255 @@
using CounterStrikeSharp.API;
using CounterStrikeSharp.API.Core;
using CounterStrikeSharp.API.Core.Attributes.Registration;
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 Nexd.MySQL;
using System.Runtime.ExceptionServices;
using static CounterStrikeSharp.API.Core.Listeners;
using System.Reflection;
using CounterStrikeSharp.API.Core.Attributes;
using CounterStrikeSharp.API.Modules.Cvars;
using Newtonsoft.Json.Linq;
namespace WeaponPaints;
public class WeaponPaints : BasePlugin, IPluginConfig<WeaponPaintsConfig>
[MinimumApiVersion(101)]
public partial class WeaponPaints : BasePlugin, IPluginConfig<WeaponPaintsConfig>
{
public override string ModuleName => "WeaponPaints";
public override string ModuleDescription => "Connector for web-based player chosen wepaon paints.";
public override string ModuleAuthor => "Nereziel";
public override string ModuleVersion => "0.8";
internal static readonly Dictionary<string, string> weaponList = new()
{
{"weapon_deagle", "Desert Eagle"},
{"weapon_elite", "Dual Berettas"},
{"weapon_fiveseven", "Five-SeveN"},
{"weapon_glock", "Glock-18"},
{"weapon_ak47", "AK-47"},
{"weapon_aug", "AUG"},
{"weapon_awp", "AWP"},
{"weapon_famas", "FAMAS"},
{"weapon_g3sg1", "G3SG1"},
{"weapon_galilar", "Galil AR"},
{"weapon_m249", "M249"},
{"weapon_m4a1", "M4A1"},
{"weapon_mac10", "MAC-10"},
{"weapon_p90", "P90"},
{"weapon_mp5sd", "MP5-SD"},
{"weapon_ump45", "UMP-45"},
{"weapon_xm1014", "XM1014"},
{"weapon_bizon", "PP-Bizon"},
{"weapon_mag7", "MAG-7"},
{"weapon_negev", "Negev"},
{"weapon_sawedoff", "Sawed-Off"},
{"weapon_tec9", "Tec-9"},
{"weapon_hkp2000", "P2000"},
{"weapon_mp7", "MP7"},
{"weapon_mp9", "MP9"},
{"weapon_nova", "Nova"},
{"weapon_p250", "P250"},
{"weapon_scar20", "SCAR-20"},
{"weapon_sg556", "SG 553"},
{"weapon_ssg08", "SSG 08"},
{"weapon_m4a1_silencer", "M4A1-S"},
{"weapon_usp_silencer", "USP-S"},
{"weapon_cz75a", "CZ75-Auto"},
{"weapon_revolver", "R8 Revolver"},
{ "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" }
};
public WeaponPaintsConfig Config { get; set; } = new();
internal static WeaponPaintsConfig _config = new WeaponPaintsConfig();
internal static Dictionary<int, int> g_knifePickupCount = new Dictionary<int, int>();
internal static Dictionary<int, string> g_playersKnife = new();
internal static Dictionary<int, Dictionary<int, WeaponInfo>> gPlayerWeaponsInfo = new Dictionary<int, Dictionary<int, WeaponInfo>>();
internal static List<JObject> skinsList = new List<JObject>();
internal static WeaponSynchronization? weaponSync;
//internal static List<int> g_changedKnife = new();
internal bool g_bCommandsAllowed = true;
MySqlDb? MySql = null;
private DateTime[] commandCooldown = new DateTime[Server.MaxPlayers];
private Dictionary<ulong, Dictionary<nint, int>> gPlayerWeaponPaints = new();
private Dictionary<ulong, Dictionary<nint, int>> gPlayerWeaponSeed = new();
private Dictionary<ulong, Dictionary<nint, float>> gPlayerWeaponWear = new();
private Dictionary<int, string> g_playersKnife = new();
private static readonly Dictionary<string, string> 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 List<string> weaponList = new()
{
"weapon_deagle", "weapon_elite", "weapon_fiveseven", "weapon_glock",
"weapon_ak47", "weapon_aug", "weapon_awp", "weapon_famas",
"weapon_g3sg1", "weapon_galilar", "weapon_m249", "weapon_m4a1",
"weapon_mac10", "weapon_p90", "weapon_mp5sd", "weapon_ump45",
"weapon_xm1014", "weapon_bizon", "weapon_mag7", "weapon_negev",
"weapon_sawedoff", "weapon_tec9", "weapon_hkp2000", "weapon_mp7",
"weapon_mp9", "weapon_nova", "weapon_p250", "weapon_scar20",
"weapon_sg556", "weapon_ssg08", "weapon_m4a1_silencer", "weapon_usp_silencer",
"weapon_cz75a", "weapon_revolver", "weapon_bayonet", "weapon_knife"
};
public override void Load(bool hotReload)
{
base.Load(hotReload);
SetGlobalExceptionHandler();
MySql = new MySqlDb(Config.DatabaseHost, Config.DatabaseUser, Config.DatabasePassword, Config.DatabaseName!, Config.DatabasePort);
RegisterListener<Listeners.OnEntitySpawned>(OnEntitySpawned);
RegisterListener<Listeners.OnClientPutInServer>(OnClientPutInServer);
RegisterListener<Listeners.OnClientDisconnect>(OnClientDisconnect);
RegisterListener<Listeners.OnMapStart>(OnMapStart);
RegisterEventHandler<EventPlayerSpawn>(OnPlayerSpawn);
//RegisterEventHandler<EventRoundPrestart>(OnRoundPreStart);
SetupMenus();
}
public void OnConfigParsed(WeaponPaintsConfig config)
{
if (config.DatabaseHost.Length < 1 || config.DatabaseName.Length < 1 || config.DatabaseUser.Length < 1)
{
throw new Exception("You need to setup Database credentials in config!");
}
internal Uri GlobalShareApi = new Uri("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<int, string> weaponDefindex { get; } = new Dictionary<int, string>
{
{ 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" }
};
Config = config;
}
// 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)
{
RemoveGlobalExceptionHandler();
base.Unload(hotReload);
}
private void GlobalExceptionHandler(object? sender, FirstChanceExceptionEventArgs @event)
{
Log(@event.Exception.ToString());
}
private void SetGlobalExceptionHandler()
{
AppDomain.CurrentDomain.FirstChanceException += this.GlobalExceptionHandler;
}
private void RemoveGlobalExceptionHandler()
{
AppDomain.CurrentDomain.FirstChanceException -= this.GlobalExceptionHandler;
}
private void OnMapStart(string mapName)
{
// TODO
// needed for now
base.AddTimer(2.0f, () => {
NativeAPI.IssueServerCommand("mp_t_default_melee \"\"");
NativeAPI.IssueServerCommand("mp_ct_default_melee \"\"");
});
}
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.3c";
public static WeaponPaintsConfig GetWeaponPaintsConfig()
{
return _config;
}
private void OnClientPutInServer(int playerSlot)
{
int playerIndex = playerSlot + 1;
Task.Run(async () =>
{
await GetKnifeFromDatabase(playerIndex);
await GetWeaponPaintsFromDatabase(playerIndex);
});
}
private void OnClientDisconnect(int playerSlot)
{
// TODO: Clean up after player
g_playersKnife.Remove(playerSlot+1);
}
public override void Load(bool hotReload)
{
if (!Config.GlobalShare)
{
DatabaseConnectionString = Utility.BuildDatabaseConnectionString();
Utility.TestDatabaseConnection();
}
private HookResult OnPlayerSpawn(EventPlayerSpawn @event, GameEventInfo info)
{
var player = @event.Userid;
if (!player.IsValid || !player.PlayerPawn.IsValid || !player.PawnIsAlive)
{
return HookResult.Continue;
}
if (player.IsBot)
{
player.GiveNamedItem("weapon_knife");
return HookResult.Continue;
}
if (hotReload)
{
OnMapStart(string.Empty);
if (!PlayerHasKnife(player))
{
if (g_playersKnife.TryGetValue((int)player.EntityIndex!.Value.Value, out var knife))
{
player.GiveNamedItem(knife);
}
else
{
player.GiveNamedItem("weapon_knife");
}
}
List<CCSPlayerController> players = Utilities.GetPlayers();
return HookResult.Continue;
}
private void OnEntitySpawned(CEntityInstance entity)
{
var designerName = entity.DesignerName;
if (!weaponList.Contains(designerName)) return;
bool isKnife = false;
var weapon = new CBasePlayerWeapon(entity.Handle);
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;
if (designerName.Contains("knife") || designerName.Contains("bayonet"))
{
isKnife = true;
}
Server.NextFrame(() =>
{
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;
}*/
var steamId = new SteamID(player.SteamID);
if (!gPlayerWeaponPaints.ContainsKey(steamId.SteamId64)) return;
if (!gPlayerWeaponPaints[steamId.SteamId64].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[steamId.SteamId64][weapon.AttributeManager.Item.ItemDefinitionIndex];
weapon.FallbackSeed = gPlayerWeaponSeed[steamId.SteamId64][weapon.AttributeManager.Item.ItemDefinitionIndex];
weapon.FallbackWear = gPlayerWeaponWear[steamId.SteamId64][weapon.AttributeManager.Item.ItemDefinitionIndex];
if (!isKnife && weapon.CBodyComponent != null && weapon.CBodyComponent.SceneNode != null)
{
var skeleton = GetSkeletonInstance(weapon.CBodyComponent.SceneNode);
skeleton.ModelState.MeshGroupMask = 2;
}
});
}
public void RemoveKnifeFromPlayer(CCSPlayerController player)
{
if (!player.PawnIsAlive) return;
if (!g_playersKnife.ContainsKey((int)player.EntityIndex!.Value.Value)) return;
var weapons = player.PlayerPawn.Value.WeaponServices!.MyWeapons;
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();
player.GiveNamedItem(g_playersKnife[(int)player.EntityIndex!.Value.Value]);
break;
}
}
}
}
public static bool PlayerHasKnife(CCSPlayerController player)
{
if (!player.PawnIsAlive) return false;
var weapons = player.PlayerPawn.Value.WeaponServices!.MyWeapons;
foreach (var weapon in weapons)
{
if (weapon.IsValid && weapon.Value.IsValid)
{
if (weapon.Value.DesignerName.Contains("knife"))
{
return true;
}
}
}
return false;
}
private void SetupMenus()
{
var giveItemMenu = new ChatMenu(ReplaceTags(Config.Messages.KnifeMenuTitle));
var handleGive = (CCSPlayerController player, ChatMenuOption option) =>
{
if (knifeTypes.TryGetValue(option.Text, out var knife))
{
Task.Run(() => SyncKnifeToDatabase((int)player.EntityIndex!.Value.Value, knife));
g_playersKnife[(int)player.EntityIndex!.Value.Value] = knifeTypes[option.Text];
if (!string.IsNullOrEmpty(Config.Messages.ChosenKnifeMenu)) {
string temp = $"{Config.Prefix} {Config.Messages.ChosenKnifeMenu}".Replace("{KNIFE}", option.Text);
player.PrintToChat(ReplaceTags(temp));
}
RemoveKnifeFromPlayer(player);
}
};
foreach (var knife in knifeTypes)
{
giveItemMenu.AddMenuOption(knife.Key, handleGive);
}
AddCommand("css_knife", "Knife Menu", (player, info) => { if (player == null) return; ChatMenus.OpenMenu(player, giveItemMenu); });
}
[ConsoleCommand("css_wp", "refreshskins")]
public void OnCommandRefresh(CCSPlayerController? player, CommandInfo command)
{
if (player == null) return;
string temp = "";
int playerIndex = (int)player.EntityIndex!.Value.Value;
if (DateTime.UtcNow >= commandCooldown[playerIndex].AddSeconds(Config.CmdRefreshCooldownSeconds))
{
commandCooldown[playerIndex] = DateTime.UtcNow;
Task.Run(async () => await GetWeaponPaintsFromDatabase(playerIndex));
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_ws", "weaponskins")]
public void OnCommandWS(CCSPlayerController? player, CommandInfo command)
{
if (player == null) return;
PlayerInfo playerInfo = new PlayerInfo
{
UserId = player.UserId,
Index = (int)player.Index,
SteamId = player?.AuthorizedSteamID?.SteamId64.ToString(),
Name = player?.PlayerName,
IpAddress = player?.IpAddress?.Split(":")[0]
};
string temp = "";
if (Config.Additional.SkinEnabled && weaponSync != null)
_ = weaponSync.GetWeaponPaintsFromDatabase(playerInfo);
if (Config.Additional.KnifeEnabled && weaponSync != null)
_ = weaponSync.GetKnifeFromDatabase(playerInfo);
}
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 (!string.IsNullOrEmpty(Config.Messages.KnifeMessageCommand)) {
temp = $"{Config.Prefix} {Config.Messages.KnifeMessageCommand}";
player.PrintToChat(ReplaceTags(temp));
}
}
public static CSkeletonInstance GetSkeletonInstance(CGameSceneNode node)
{
Func<nint, nint> GetSkeletonInstance = VirtualFunction.Create<nint, nint>(node.Handle, 8);
return new CSkeletonInstance(GetSkeletonInstance(node.Handle));
}
private async Task GetWeaponPaintsFromDatabase(int playerIndex)
{
try
{
CCSPlayerController player = Utilities.GetPlayerFromIndex(playerIndex);
if (player == null || !player.IsValid) return;
var steamId = new SteamID(player.SteamID);
RegisterListeners();
RegisterCommands();
}
MySqlQueryCondition conditions = new MySqlQueryCondition()
.Add("steamid", "=", steamId.SteamId64.ToString());
if (Config.Additional.KnifeEnabled)
SetupKnifeMenu();
if (Config.Additional.SkinEnabled)
SetupSkinsMenu();
MySqlQueryResult result = await MySql!.Table("wp_player_skins").Where(conditions).SelectAsync();
if (result.Rows < 1) return;
result.ToList().ForEach(pair =>
{
int WeaponDefIndex = result.Get<int>(pair.Key, "weapon_defindex");
int PaintId = result.Get<int>(pair.Key, "weapon_paint_id");
float Wear = result.Get<float>(pair.Key, "weapon_wear");
int Seed = result.Get<int>(pair.Key, "weapon_seed");
RegisterListeners();
RegisterCommands();
if (!gPlayerWeaponPaints.ContainsKey(steamId.SteamId64))
{
gPlayerWeaponPaints[steamId.SteamId64] = new Dictionary<nint, int>();
}
if (!gPlayerWeaponWear.ContainsKey(steamId.SteamId64))
{
gPlayerWeaponWear[steamId.SteamId64] = new Dictionary<nint, float>();
}
if (!gPlayerWeaponSeed.ContainsKey(steamId.SteamId64))
{
gPlayerWeaponSeed[steamId.SteamId64] = new Dictionary<nint, int>();
}
Utility.LoadSkinsFromFile(ModuleDirectory + "/skins.json");
}
gPlayerWeaponPaints[steamId.SteamId64][WeaponDefIndex] = PaintId;
gPlayerWeaponWear[steamId.SteamId64][WeaponDefIndex] = Wear;
gPlayerWeaponSeed[steamId.SteamId64][WeaponDefIndex] = Seed;
});
}
catch (Exception e)
{
Log(e.Message);
return;
}
}
private async Task GetKnifeFromDatabase(int playerIndex)
{
try
{
CCSPlayerController player = Utilities.GetPlayerFromIndex(playerIndex);
if (player == null || !player.IsValid) return;
var steamId = new SteamID(player.SteamID);
MySqlQueryCondition conditions = new MySqlQueryCondition()
.Add("steamid", "=", steamId.SteamId64.ToString());
public void OnConfigParsed(WeaponPaintsConfig config)
{
if (!config.GlobalShare)
{
if (config.DatabaseHost.Length < 1 || config.DatabaseName.Length < 1 || config.DatabaseUser.Length < 1)
{
throw new Exception("[WeaponPaints] You need to setup Database credentials in config!");
}
}
MySqlQueryResult result = await MySql!.Table("wp_player_knife").Where(conditions).SelectAsync();
Config = config;
_config = config;
Utility.Config = config;
Utility.ShowAd(ModuleVersion);
}
if (result.Rows < 1)
{
//g_playersKnife[playerIndex] = "weapon_knife";
return;
}
public override void Unload(bool hotReload)
{
base.Unload(hotReload);
}
string knife = result.Get<string>(0, "knife");
if (knife != null)
{
g_playersKnife[playerIndex] = knife;
}
//Log($"{player.PlayerName} has this knife -> {g_playersKnife[playerIndex]}");
}
catch (Exception e)
{
Log(e.Message);
return;
}
}
private async Task SyncKnifeToDatabase(int playerIndex, string knife)
{
try
{
CCSPlayerController player = Utilities.GetPlayerFromIndex(playerIndex);
if (player == null || !player.IsValid) return;
var steamId = new SteamID(player.SteamID);
await MySql!.ExecuteNonQueryAsync($"INSERT INTO `wp_player_knife` (`steamid`, `knife`) VALUES('{steamId.SteamId64}', '{knife}') ON DUPLICATE KEY UPDATE `knife` = '{knife}';");
}
catch (Exception e)
{
Log(e.Message);
return;
}
}
private void GlobalShareConnect()
{
if (!Config.GlobalShare) return;
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;
}
var values = new Dictionary<string, string>
{
{ "server_address", $"{ConVar.Find("ip")!.StringValue}:{ConVar.Find("hostport")!.GetPrimitiveValue<int>().ToString()}" },
{ "server_hostname", ConVar.Find("hostname")!.StringValue }
};
return message;
}
using (var httpClient = new HttpClient())
{
httpClient.BaseAddress = GlobalShareApi;
var formContent = new FormUrlEncodedContent(values);
private static void Log(string message)
{
Console.BackgroundColor = ConsoleColor.DarkGray;
Console.ForegroundColor = ConsoleColor.Cyan;
Console.WriteLine(message);
Console.ResetColor();
}
}
Task<HttpResponseMessage> responseTask = httpClient.PostAsync("", formContent);
responseTask.Wait();
HttpResponseMessage response = responseTask.Result;
if (response.IsSuccessStatusCode)
{
Task<string> responseBodyTask = response.Content.ReadAsStringAsync();
responseBodyTask.Wait();
string responseBody = responseBodyTask.Result;
GlobalShareServerId = Int32.Parse(responseBody);
}
else
{
throw new Exception("[WeaponPaints] Unable to retrieve serverid from GlobalShare!");
}
}
Console.WriteLine("[WeaponPaints] GlobalShare ONLINE");
}
}

View File

@@ -9,13 +9,8 @@
<ItemGroup>
<PackageReference Include="CounterStrikeSharp.API" Version="*" />
<PackageReference Include="Nexd.MySQL" Version="1.0.1" />
<PackageReference Include="Dapper" Version="2.1.24" />
<PackageReference Include="MySqlConnector" Version="2.3.1" />
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
</ItemGroup>
<ItemGroup>
<Reference Include="CounterStrikeSharp.API">
<HintPath>deps\CounterStrikeSharp.API.dll</HintPath>
</Reference>
</ItemGroup>
</Project>

257
WeaponSynchronization.cs Normal file
View File

@@ -0,0 +1,257 @@
using CounterStrikeSharp.API;
using CounterStrikeSharp.API.Core;
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<string, string>
{
{ "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<string>(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<int, WeaponInfo>();
}
try
{
if (_config.GlobalShare)
{
var values = new Dictionary<string, string>
{
{ "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>();
int? weaponPaintId = weapon["weapon_paint_id"]?.Value<int>();
float? weaponWear = weapon["weapon_wear"]?.Value<float>();
int? weaponSeed = weapon["weapon_seed"]?.Value<int>();
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<dynamic> PlayerSkins = await connection.QueryAsync<dynamic>(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(int playerIndex, string knife)
{
if (!_config.Additional.KnifeEnabled) return;
try
{
CCSPlayerController player = Utilities.GetPlayerFromIndex(playerIndex);
if (player == null || !player.IsValid) return;
if (player.AuthorizedSteamID == null) return;
string steamId = player.AuthorizedSteamID.SteamId64.ToString();
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, newKnife = knife });
await connection.CloseAsync();
}
catch (Exception e)
{
Utility.Log(e.Message);
return;
}
}
internal async Task SyncWeaponPaintsToDatabase(CCSPlayerController? player)
{
if (player == null || !Utility.IsPlayerValid(player)) return;
int playerIndex = (int)player.Index;
if (player.AuthorizedSteamID == null) return;
string steamId = player.AuthorizedSteamID.SteamId64.ToString();
using var connection = new MySqlConnection(_databaseConnectionString);
await connection.OpenAsync();
if (!WeaponPaints.gPlayerWeaponsInfo.ContainsKey(playerIndex))
return;
foreach (var weaponInfoPair in WeaponPaints.gPlayerWeaponsInfo[playerIndex])
{
int weaponDefIndex = weaponInfoPair.Key;
WeaponInfo weaponInfo = weaponInfoPair.Value;
int paintId = weaponInfo.Paint;
float wear = weaponInfo.Wear;
int seed = weaponInfo.Seed;
string updateSql = "UPDATE `wp_player_skins` SET `weapon_paint_id` = @paintId, " +
"`weapon_wear` = @wear, `weapon_seed` = @seed WHERE `steamid` = @steamid " +
"AND `weapon_defindex` = @weaponDefIndex";
var updateParams = new { paintId, wear, seed, steamid = steamId, weaponDefIndex };
int rowsAffected = await connection.ExecuteAsync(updateSql, updateParams);
if (rowsAffected == 0)
{
string insertSql = "INSERT INTO `wp_player_skins` (`steamid`, `weapon_defindex`, " +
"`weapon_paint_id`, `weapon_wear`, `weapon_seed`) " +
"VALUES (@steamid, @weaponDefIndex, @paintId, @wear, @seed)";
await connection.ExecuteAsync(insertSql, updateParams);
}
}
await connection.CloseAsync();
}
}
}

96
website/class/utils.php Normal file
View File

@@ -0,0 +1,96 @@
<?php
class UtilsClass
{
public static function skinsFromJson(): array
{
$skins = [];
$json = json_decode(file_get_contents(__DIR__ . "/../data/skins.json"), true);
foreach ($json as $skin) {
$skins[(int) $skin['weapon_defindex']][(int) $skin['paint']] = [
'weapon_name' => $skin['weapon_name'],
'paint_name' => $skin['paint_name'],
'image_url' => $skin['image'],
];
}
return $skins;
}
public static function getWeaponsFromArray()
{
$weapons = [];
$temp = self::skinsFromJson();
foreach ($temp as $key => $value) {
if (key_exists($key, $weapons))
continue;
$weapons[$key] = [
'weapon_name' => $value[0]['weapon_name'],
'paint_name' => $value[0]['paint_name'],
'image_url' => $value[0]['image_url'],
];
}
return $weapons;
}
public static function getKnifeTypes()
{
$knifes = [];
$temp = self::getWeaponsFromArray();
foreach ($temp as $key => $weapon) {
if (
!in_array($key, [
500,
503,
505,
506,
507,
508,
509,
512,
514,
515,
516,
517,
518,
519,
520,
521,
522,
523,
525
])
)
continue;
$knifes[$key] = [
'weapon_name' => $weapon['weapon_name'],
'paint_name' => rtrim(explode("|", $weapon['paint_name'])[0]),
'image_url' => $weapon['image_url'],
];
$knifes[0] = [
'weapon_name' => "weapon_knife",
'paint_name' => "Default knife",
'image_url' => "https://raw.githubusercontent.com/Nereziel/cs2-WeaponPaints/main/website/img/skins/weapon_knife.png",
];
}
ksort($knifes);
return $knifes;
}
public static function getSelectedSkins(array $temp)
{
$selected = [];
foreach ($temp as $weapon) {
$selected[$weapon['weapon_defindex']] = $weapon['weapon_paint_id'];
}
return $selected;
}
}

1
website/data/skins.json Normal file

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load Diff

View File

@@ -1,73 +0,0 @@
<?php
$weapons = array (
"weapon_deagle" => 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 "<pre>";
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 ",<br>";
}
//print_r($json);
echo "</pre>";
?>

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 77 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 82 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 82 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 83 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 86 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 81 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 76 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 74 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 70 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 82 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 78 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 67 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 97 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 87 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 80 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 92 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 88 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 84 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 88 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 94 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 85 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 81 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 72 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 85 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 75 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 83 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 81 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 83 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 85 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 88 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 83 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 87 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 74 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 80 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 84 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 74 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 79 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 80 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 79 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 78 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 81 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 70 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 71 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 70 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 70 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 98 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 114 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 114 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 107 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 107 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 105 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 102 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 112 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 97 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 97 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 110 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 100 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 106 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 103 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 95 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 113 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 101 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 110 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 109 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 100 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 110 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 105 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 107 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 106 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 108 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 107 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 114 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 100 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 98 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 110 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 100 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 103 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 104 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 112 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 103 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 108 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 106 KiB

Some files were not shown because too many files have changed in this diff Show More