@@ -1,11 +1,13 @@
using CounterStrikeSharp.API ;
using CounterStrikeSharp.API ;
using CounterStrikeSharp.API.Core ;
using CounterStrikeSharp.API.Core ;
using CounterStrikeSharp.API.Core.Attributes.Registration ;
using CounterStrikeSharp.API.Core.Attributes.Registration ;
using CounterStrikeSharp.API.Modules.Commands ;
using CounterStrikeSharp.API.Modules.Commands ;
using CounterStrikeSharp.API.Modules.Entities ;
using CounterStrikeSharp.API.Modules.Entities ;
using CounterStrikeSharp.API.Modules.Memory ;
using CounterStrikeSharp.API.Modules.Memory ;
using CounterStrikeSharp.API.Modules.Menu ;
using CounterStrikeSharp.API.Modules.Utils ;
using CounterStrikeSharp.API.Modules.Utils ;
using Nexd.MySQL ;
using Nexd.MySQL ;
using System.Runtime.ExceptionServices ;
using static CounterStrikeSharp . API . Core . Listeners ;
using static CounterStrikeSharp . API . Core . Listeners ;
namespace WeaponPaints ;
namespace WeaponPaints ;
@@ -14,15 +16,18 @@ public class WeaponPaints : BasePlugin, IPluginConfig<WeaponPaintsConfig>
public override string ModuleName = > "WeaponPaints" ;
public override string ModuleName = > "WeaponPaints" ;
public override string ModuleDescription = > "Connector for web-based player chosen wepaon paints." ;
public override string ModuleDescription = > "Connector for web-based player chosen wepaon paints." ;
public override string ModuleAuthor = > "Nereziel" ;
public override string ModuleAuthor = > "Nereziel" ;
public override string ModuleVersion = > "0.7 " ;
public override string ModuleVersion = > "0.8 " ;
public WeaponPaintsConfig Config { get ; set ; } = new ( ) ;
public WeaponPaintsConfig Config { get ; set ; } = new ( ) ;
MySqlDb ? MySql = null ;
MySqlDb ? MySql = null ;
public DateTime [ ] commandCooldown = new DateTime [ Server . MaxPlayers ] ;
private DateTime [ ] commandCooldown = new DateTime [ Server . MaxPlayers ] ;
private Dictionary < ulong , Dictionary < nint , int > > gPlayerWeaponPaints = new Dictionary < ulong , Dictionary < nint , int > > ( ) ;
private static string PluginPrefix = $" {ChatColors.Green}[WeaponPaints]{ChatColors.White}" ;
private Dictionary < ulong , Dictionary < nint , int > > gPlayerWeaponSeed = new Dictionary < ulong , Dictionary < nint , int > > ( ) ;
private Dictionary < ulong , Dictionary < nint , int > > gPlayerWeaponPaints = new ( ) ;
private Dictionary < ulong , Dictionary < nint , floa t> > gPlayerWeaponWear = new Dictionary < ulong , Dictionary < nint , float > > ( ) ;
private Dictionary < ulong , Dictionary < nint , in t> > gPlayerWeaponSeed = new ( ) ;
private static Dictionary < stri ng, string > knifeTypes = new Dictionary < str ing , string > ( )
private Dictionary < ulo ng, Dictionary < n int , float > > gPlayerWeaponWear = new ( ) ;
private Dictionary < int , string > g_playersKife = new ( ) ;
private static readonly Dictionary < string , string > knifeTypes = new ( )
{
{
{ "m9" , "weapon_knife_m9_bayonet" } ,
{ "m9" , "weapon_knife_m9_bayonet" } ,
{ "karambit" , "weapon_knife_karambit" } ,
{ "karambit" , "weapon_knife_karambit" } ,
@@ -45,7 +50,7 @@ public class WeaponPaints : BasePlugin, IPluginConfig<WeaponPaintsConfig>
{ "skeleton" , "weapon_knife_skeleton" } ,
{ "skeleton" , "weapon_knife_skeleton" } ,
{ "default" , "weapon_knife" }
{ "default" , "weapon_knife" }
} ;
} ;
private static List < string > weaponList = new List < string > ( )
private static readonly List < string > weaponList = new ( )
{
{
"weapon_deagle" , "weapon_elite" , "weapon_fiveseven" , "weapon_glock" ,
"weapon_deagle" , "weapon_elite" , "weapon_fiveseven" , "weapon_glock" ,
"weapon_ak47" , "weapon_aug" , "weapon_awp" , "weapon_famas" ,
"weapon_ak47" , "weapon_aug" , "weapon_awp" , "weapon_famas" ,
@@ -57,30 +62,82 @@ public class WeaponPaints : BasePlugin, IPluginConfig<WeaponPaintsConfig>
"weapon_sg556" , "weapon_ssg08" , "weapon_m4a1_silencer" , "weapon_usp_silencer" ,
"weapon_sg556" , "weapon_ssg08" , "weapon_m4a1_silencer" , "weapon_usp_silencer" ,
"weapon_cz75a" , "weapon_revolver" , "weapon_bayonet" , "weapon_knife"
"weapon_cz75a" , "weapon_revolver" , "weapon_bayonet" , "weapon_knife"
} ;
} ;
public override void Load ( bool hotReload )
public override void Load ( bool hotReload )
{
{
MySql = new MySqlDb ( Config . DatabaseHost ! , Config . DatabaseUser ! , Config . DatabasePassword ! , Config . DatabaseName ! , Config . DatabasePort ) ;
base . Load ( hotReload ) ;
SetGlobalExceptionHandler ( ) ;
MySql = new MySqlDb ( Config . DatabaseHost , Config . DatabaseUser , Config . DatabasePassword , Config . DatabaseName ! , Config . DatabasePort ) ;
RegisterListener < Listeners . OnEntitySpawned > ( OnEntitySpawned ) ;
RegisterListener < Listeners . OnEntitySpawned > ( OnEntitySpawned ) ;
RegisterListener < Listeners . OnClientA uthorized > ( OnClientA uthorized ) ;
RegisterListener < Listeners . OnClientP utInServer > ( OnClientP utInServer ) ;
RegisterListener < Listeners . OnClientDisconnect > ( OnClientDisconnect ) ;
RegisterListener < Listeners . OnClientDisconnect > ( OnClientDisconnect ) ;
RegisterListener < Listeners . OnMapStart > ( OnMapStart ) ;
RegisterEventHandler < EventPlayerSpawn > ( OnPlayerSpawn ) ;
//RegisterEventHandler<EventRoundPrestart>(OnRoundPreStart);
SetupMenus ( ) ;
}
}
public void OnConfigParsed ( WeaponPaintsConfig config )
public void OnConfigParsed ( WeaponPaintsConfig config )
{
{
Config = config ;
Config = config ;
}
}
private void OnClientAuthorized ( int playerSlot , SteamID steamId )
// TODO: fix for map which change mp_t_default_melee
/*private HookResult OnRoundPreStart(EventRoundPrestart @event, GameEventInfo info)
{
{
int slot = playerSlot ;
NativeAPI.IssueServerCommand("mp_t_default_melee \"\"");
Server . NextFrame ( ( ) = >
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 \"\"" ) ;
} ) ;
}
private void OnClientPutInServer ( int playerSlot )
{
int playerIndex = playerSlot + 1 ;
Task . Run ( async ( ) = >
{
{
Task . Run ( ( ) = > GetWeaponPaints FromDatabase( slot ) ) ;
await GetKnife FromDatabase( playerIndex ) ;
await GetWeaponPaintsFromDatabase ( playerIndex ) ;
} ) ;
} ) ;
}
}
private void OnClientDisconnect ( int playerSlot )
private void OnClientDisconnect ( int playerSlot )
{
{
// Clean up after player
// TODO: Clean up after player
}
private HookResult OnPlayerSpawn ( EventPlayerSpawn @event , GameEventInfo info )
{
var player = @event . Userid ;
if ( ! player . IsValid | | ! player . PlayerPawn . IsValid | | player . IsBot )
{
return HookResult . Continue ;
}
if ( ! PlayerHasKnife ( player ) ) player . GiveNamedItem ( g_playersKife [ ( int ) player . EntityIndex ! . Value . Value ] ) ;
return HookResult . Continue ;
}
}
private void OnEntitySpawned ( CEntityInstance entity )
private void OnEntitySpawned ( CEntityInstance entity )
{
{
@@ -88,6 +145,7 @@ public class WeaponPaints : BasePlugin, IPluginConfig<WeaponPaintsConfig>
if ( ! weaponList . Contains ( designerName ) ) return ;
if ( ! weaponList . Contains ( designerName ) ) return ;
bool isKnife = false ;
bool isKnife = false ;
var weapon = new CBasePlayerWeapon ( entity . Handle ) ;
var weapon = new CBasePlayerWeapon ( entity . Handle ) ;
if ( designerName . Contains ( "knife" ) | | designerName . Contains ( "bayonet" ) )
if ( designerName . Contains ( "knife" ) | | designerName . Contains ( "bayonet" ) )
{
{
isKnife = true ;
isKnife = true ;
@@ -103,11 +161,19 @@ public class WeaponPaints : BasePlugin, IPluginConfig<WeaponPaintsConfig>
var playerIndex = ( int ) pawn . Controller . Value . EntityIndex ! . Value . Value ;
var playerIndex = ( int ) pawn . Controller . Value . EntityIndex ! . Value . Value ;
var player = Utilities . GetPlayerFromIndex ( playerIndex ) ;
var player = Utilities . GetPlayerFromIndex ( playerIndex ) ;
if ( player = = null | | ! player . IsValid | | player . IsBot ) return ;
if ( player = = null | | ! player . IsValid | | player . IsBot ) return ;
// TODO: Remove knife crashes here, needs another solution
/*if (isKnife && g_playersKife[(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 ) ;
var steamId = new SteamID ( player . SteamID ) ;
if ( ! gPlayerWeaponPaints . ContainsKey ( steamId . SteamId64 ) ) return ;
if ( ! gPlayerWeaponPaints . ContainsKey ( steamId . SteamId64 ) ) return ;
if ( ! gPlayerWeaponPaints [ steamId . SteamId64 ] . ContainsKey ( weapon . AttributeManager . Item . ItemDefinitionIndex ) ) return ;
if ( ! gPlayerWeaponPaints [ steamId . SteamId64 ] . ContainsKey ( weapon . AttributeManager . Item . ItemDefinitionIndex ) ) return ;
weapon. AttributeManager. Item . ItemIDLow = unchecked ( ( uint ) - 1 ) ;
//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 . ItemIDHigh = unchecked ( ( uint ) - 1 ) ;
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 . FallbackPaintKit = gPlayerWeaponPaints [ steamId . SteamId64 ] [ weapon . AttributeManager . Item . ItemDefinitionIndex ] ;
weapon . FallbackSeed = gPlayerWeaponSeed [ steamId . SteamId64 ] [ weapon . AttributeManager . Item . ItemDefinitionIndex ] ;
weapon . FallbackSeed = gPlayerWeaponSeed [ steamId . SteamId64 ] [ weapon . AttributeManager . Item . ItemDefinitionIndex ] ;
weapon . FallbackWear = gPlayerWeaponWear [ steamId . SteamId64 ] [ weapon . AttributeManager . Item . ItemDefinitionIndex ] ;
weapon . FallbackWear = gPlayerWeaponWear [ steamId . SteamId64 ] [ weapon . AttributeManager . Item . ItemDefinitionIndex ] ;
@@ -118,28 +184,82 @@ public class WeaponPaints : BasePlugin, IPluginConfig<WeaponPaintsConfig>
}
}
} ) ;
} ) ;
}
}
[ConsoleCommand("css_ws", "weaponskins")]
public void RemoveKnifeFromPlayer ( CCSPlayerController player )
public void OnCommandWS ( CCSPlayerController ? player , CommandInfo command )
{
{
if ( player = = null ) return ;
if ( ! player . PawnIsAlive ) return ;
player . PrintToChat ( $"Change weapon skins at {ChatColors.Purple}{Config.WebSite}" ) ;
var weapons = player . PlayerPawn . Value . WeaponServices ! . MyWeapons ;
player . PrintToChat ( $"To synchronize weapon paints type {ChatColors.Purple}!wp" ) ;
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_playersKife [ ( 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 ( "Knife Menu" ) ;
var handleGive = ( CCSPlayerController player , ChatMenuOption option ) = >
{
if ( knifeTypes . TryGetValue ( option . Text , out var knife ) )
{
Task . Run ( ( ) = > SyncKnifeToDatabase ( ( int ) player . EntityIndex ! . Value . Value , knife ) ) ;
g_playersKife [ ( int ) player . EntityIndex ! . Value . Value ] = knifeTypes [ option . Text ] ;
player . PrintToChat ( $"You have chosen {option.Text} as your knife." ) ;
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")]
[ConsoleCommand("css_wp", "refreshskins")]
public void OnCommandRefresh ( CCSPlayerController ? player , CommandInfo command )
public void OnCommandRefresh ( CCSPlayerController ? player , CommandInfo command )
{
{
if ( player = = null ) return ;
if ( player = = null ) return ;
int playerSlot = ( int ) player . EntityIndex ! . Value . Value - 1 ;
int playerIndex = ( int ) player . EntityIndex ! . Value . Value ;
if ( DateTime . UtcNow > = commandCooldown [ playerSlot ] . AddSeconds ( Config . CmdRefreshCooldownSeconds ) )
if ( DateTime . UtcNow > = commandCooldown [ playerIndex ] . AddSeconds ( Config . CmdRefreshCooldownSeconds ) )
{
{
commandCooldown [ playerSlot ] = DateTime . UtcNow ;
commandCooldown [ playerIndex ] = DateTime . UtcNow ;
Task . Run ( async ( ) = > await GetWeaponPaintsFromDatabase ( playerSlot ) ) ;
Task . Run ( async ( ) = > await GetWeaponPaintsFromDatabase ( playerIndex ) ) ;
player . PrintToChat ( " Refreshed weapon paints.") ;
player . PrintToChat ( $"{PluginPrefix} Refreshing weapon paints.") ;
return ;
return ;
}
}
player . PrintToChat ( " You can't refresh weapon paints right now.") ;
player . PrintToChat ( $"{PluginPrefix} You can't refresh weapon paints right now.") ;
}
}
public CSkeletonInstance GetSkeletonInstance ( CGameSceneNode node )
[ConsoleCommand("css_ws", "weaponskins")]
public void OnCommandWS ( CCSPlayerController ? player , CommandInfo command )
{
if ( player = = null ) return ;
player . PrintToChat ( $"{PluginPrefix} Visit {ChatColors.Purple}{Config.WebSite} {ChatColors.White}where you can change skins." ) ;
player . PrintToChat ( $"{PluginPrefix} Type {ChatColors.Purple}!wp {ChatColors.White}in chat to synchronize chosen skins." ) ;
player . PrintToChat ( $"{PluginPrefix} Type {ChatColors.Purple}!knife {ChatColors.White}in chat to open knife menu." ) ;
}
public static CSkeletonInstance GetSkeletonInstance ( CGameSceneNode node )
{
{
Func < nint , nint > GetSkeletonInstance = VirtualFunction . Create < nint , nint > ( node . Handle , 8 ) ;
Func < nint , nint > GetSkeletonInstance = VirtualFunction . Create < nint , nint > ( node . Handle , 8 ) ;
return new CSkeletonInstance ( GetSkeletonInstance ( node . Handle ) ) ;
return new CSkeletonInstance ( GetSkeletonInstance ( node . Handle ) ) ;
@@ -182,9 +302,62 @@ public class WeaponPaints : BasePlugin, IPluginConfig<WeaponPaintsConfig>
gPlayerWeaponSeed [ steamId . SteamId64 ] [ WeaponDefIndex ] = Seed ;
gPlayerWeaponSeed [ steamId . SteamId64 ] [ WeaponDefIndex ] = Seed ;
} ) ;
} ) ;
}
}
catch ( Exception )
catch ( Exception e )
{
{
Log ( e . Message ) ;
return ;
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 ( ) ) ;
MySqlQueryResult result = await MySql ! . Table ( "wp_player_knife" ) . Where ( conditions ) . SelectAsync ( ) ;
string knife = result . Get < string > ( 0 , "knife" ) ;
if ( knife ! = null )
{
g_playersKife [ playerIndex ] = knife ;
}
else
{
g_playersKife [ playerIndex ] = "weapon_knife" ;
}
//Log($"{player.PlayerName} has this knife -> {g_playersKife[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 static void Log ( string message )
{
Console . BackgroundColor = ConsoleColor . DarkGray ;
Console . ForegroundColor = ConsoleColor . Cyan ;
Console . WriteLine ( message ) ;
Console . ResetColor ( ) ;
}
}
}