Compare commits

..

3 Commits

Author SHA1 Message Date
Nereziel
2abe48f71a 123 2024-03-02 19:15:50 +01:00
Nereziel
219c201fde testref 2024-02-24 22:44:17 +01:00
Nereziel
acf4a766ca Update Commands.cs 2024-02-24 21:15:45 +01:00
347 changed files with 13506 additions and 2160 deletions

14
.github/FUNDING.yml vendored
View File

@@ -1,14 +0,0 @@
# These are supported funding model platforms
github: # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2]
patreon: # Replace with a single Patreon username
open_collective: # Replace with a single Open Collective username
ko_fi: nereziel # Replace with a single Ko-fi username
tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel
community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry
liberapay: # Replace with a single Liberapay username
issuehunt: # Replace with a single IssueHunt username
otechie: # Replace with a single Otechie username
lfx_crowdfunding: # Replace with a single LFX Crowdfunding project-name e.g., cloud-foundry
polar: # Replace with a single Polar username
custom: ['https://steamcommunity.com/tradeoffer/new/?partner=41515647&token=gW2W-nXE'] # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2']

View File

@@ -58,8 +58,10 @@ jobs:
${{ env.OUTPUT_PATH }}/McMaster.NETCore.Plugins.dll \ ${{ env.OUTPUT_PATH }}/McMaster.NETCore.Plugins.dll \
${{ env.OUTPUT_PATH }}/Microsoft.DotNet.PlatformAbstractions.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 - name: Copy skins.json
run: cp -R website/data ${{ env.OUTPUT_PATH }} run: cp website/data/skins.json ${{ env.OUTPUT_PATH }}/skins.json
- name: Copy gloves.json
run: cp website/data/gloves.json ${{ env.OUTPUT_PATH }}/gloves.json
- name: Zip - name: Zip
run: zip -r "${{ env.PROJECT_NAME }}.zip" "${{ env.OUTPUT_PATH }}" gamedata/ run: zip -r "${{ env.PROJECT_NAME }}.zip" "${{ env.OUTPUT_PATH }}" gamedata/
- name: Clean files Website - name: Clean files Website

1
.gitignore vendored
View File

@@ -2,4 +2,3 @@
bin/ bin/
obj/ obj/
website/getskins.php website/getskins.php
.idea/

Binary file not shown.

File diff suppressed because it is too large Load Diff

View File

@@ -5,24 +5,18 @@ namespace WeaponPaints
{ {
public class Additional public class Additional
{ {
[JsonPropertyName("SkinVisibilityFix")]
public bool SkinVisibilityFix { get; set; } = true;
[JsonPropertyName("KnifeEnabled")] [JsonPropertyName("KnifeEnabled")]
public bool KnifeEnabled { get; set; } = true; public bool KnifeEnabled { get; set; } = true;
[JsonPropertyName("GloveEnabled")] [JsonPropertyName("GloveEnabled")]
public bool GloveEnabled { get; set; } = true; public bool GloveEnabled { get; set; } = true;
[JsonPropertyName("MusicEnabled")]
public bool MusicEnabled { get; set; } = true;
[JsonPropertyName("AgentEnabled")]
public bool AgentEnabled { get; set; } = true;
[JsonPropertyName("SkinEnabled")] [JsonPropertyName("SkinEnabled")]
public bool SkinEnabled { get; set; } = true; public bool SkinEnabled { get; set; } = true;
[JsonPropertyName("PinsEnabled")]
public bool PinsEnabled { get; set; } = true;
[JsonPropertyName("CommandWpEnabled")] [JsonPropertyName("CommandWpEnabled")]
public bool CommandWpEnabled { get; set; } = true; public bool CommandWpEnabled { get; set; } = true;
@@ -30,28 +24,22 @@ namespace WeaponPaints
public bool CommandKillEnabled { get; set; } = true; public bool CommandKillEnabled { get; set; } = true;
[JsonPropertyName("CommandKnife")] [JsonPropertyName("CommandKnife")]
public List<string> CommandKnife { get; set; } = ["knife"]; public string CommandKnife { get; set; } = "knife";
[JsonPropertyName("CommandMusic")]
public List<string> CommandMusic { get; set; } = ["music"];
[JsonPropertyName("CommandGlove")] [JsonPropertyName("CommandGlove")]
public List<string> CommandGlove { get; set; } = ["gloves"]; public string CommandGlove { get; set; } = "gloves";
[JsonPropertyName("CommandAgent")]
public List<string> CommandAgent { get; set; } = ["agents"];
[JsonPropertyName("CommandSkin")] [JsonPropertyName("CommandSkin")]
public List<string> CommandSkin { get; set; } = ["ws"]; public string CommandSkin { get; set; } = "ws";
[JsonPropertyName("CommandSkinSelection")] [JsonPropertyName("CommandSkinSelection")]
public List<string> CommandSkinSelection { get; set; } = ["skins"]; public string CommandSkinSelection { get; set; } = "skins";
[JsonPropertyName("CommandRefresh")] [JsonPropertyName("CommandRefresh")]
public List<string> CommandRefresh { get; set; } = ["wp"]; public string CommandRefresh { get; set; } = "wp";
[JsonPropertyName("CommandKill")] [JsonPropertyName("CommandKill")]
public List<string> CommandKill { get; set; } = ["kill"]; public string CommandKill { get; set; } = "kill";
[JsonPropertyName("GiveRandomKnife")] [JsonPropertyName("GiveRandomKnife")]
public bool GiveRandomKnife { get; set; } = false; public bool GiveRandomKnife { get; set; } = false;
@@ -59,16 +47,16 @@ namespace WeaponPaints
[JsonPropertyName("GiveRandomSkin")] [JsonPropertyName("GiveRandomSkin")]
public bool GiveRandomSkin { get; set; } = false; public bool GiveRandomSkin { get; set; } = false;
[JsonPropertyName("GiveKnifeAfterRemove")]
public bool GiveKnifeAfterRemove { get; set; } = false;
[JsonPropertyName("ShowSkinImage")] [JsonPropertyName("ShowSkinImage")]
public bool ShowSkinImage { get; set; } = true; public bool ShowSkinImage { get; set; } = true;
} }
public class WeaponPaintsConfig : BasePluginConfig public class WeaponPaintsConfig : BasePluginConfig
{ {
[JsonPropertyName("ConfigVersion")] public override int Version { get; set; } = 8; public override int Version { get; set; } = 4;
[JsonPropertyName("SkinsLanguage")]
public string SkinsLanguage { get; set; } = "en";
[JsonPropertyName("DatabaseHost")] [JsonPropertyName("DatabaseHost")]
public string DatabaseHost { get; set; } = ""; public string DatabaseHost { get; set; } = "";
@@ -86,12 +74,15 @@ namespace WeaponPaints
public string DatabaseName { get; set; } = ""; public string DatabaseName { get; set; } = "";
[JsonPropertyName("CmdRefreshCooldownSeconds")] [JsonPropertyName("CmdRefreshCooldownSeconds")]
public int CmdRefreshCooldownSeconds { get; set; } = 10; public int CmdRefreshCooldownSeconds { get; set; } = 60;
[JsonPropertyName("Prefix")]
public string Prefix { get; set; } = "[WeaponPaints]";
[JsonPropertyName("Website")] [JsonPropertyName("Website")]
public string Website { get; set; } = "example.com/skins"; public string Website { get; set; } = "example.com/skins";
[JsonPropertyName("Additional")] [JsonPropertyName("Additional")]
public Additional Additional { get; set; } = new(); public Additional Additional { get; set; } = new Additional();
} }
} }

View File

@@ -1,23 +1,21 @@
using Microsoft.Extensions.Logging; using MySqlConnector;
using MySqlConnector;
namespace WeaponPaints namespace WeaponPaints
{ {
public class Database(string dbConnectionString) public class Database
{ {
public async Task<MySqlConnection> GetConnectionAsync() private readonly string _dbConnectionString;
public Database(string dbConnectionString)
{ {
try _dbConnectionString = dbConnectionString;
{
var connection = new MySqlConnection(dbConnectionString);
await connection.OpenAsync();
return connection;
}
catch (Exception ex)
{
WeaponPaints.Instance.Logger.LogError($"Unable to connect to database: {ex.Message}");
throw;
}
} }
public async Task<MySqlConnection> GetConnectionAsync()
{
var connection = new MySqlConnection(_dbConnectionString);
await connection.OpenAsync();
return connection;
}
} }
} }

419
Events.cs
View File

@@ -1,9 +1,6 @@
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.Entities;
using CounterStrikeSharp.API.Modules.Memory;
using CounterStrikeSharp.API.Modules.Memory.DynamicFunctions;
namespace WeaponPaints namespace WeaponPaints
{ {
@@ -14,47 +11,36 @@ namespace WeaponPaints
{ {
CCSPlayerController? player = @event.Userid; CCSPlayerController? player = @event.Userid;
if (player is null || !player.IsValid || player.IsBot || if (player is null || !player.IsValid || player.IsBot || player.IsHLTV || player.SteamID.ToString().Length != 17 ||
WeaponSync == null || Database == null) return HookResult.Continue; weaponSync == null || _database == null) return HookResult.Continue;
var playerInfo = new PlayerInfo PlayerInfo playerInfo = new(
{ (int)player.Index,
UserId = player.UserId, player.Slot,
Slot = player.Slot, player.UserId,
Index = (int)player.Index, player.SteamID.ToString(),
SteamId = player.SteamID.ToString(), player.PlayerName,
Name = player.PlayerName, player.IpAddress?.Split(":")[0]
IpAddress = player.IpAddress?.Split(":")[0] );
};
try try
{ {
_ = Task.Run(async () => await WeaponSync.GetPlayerData(playerInfo));
/*
if (Config.Additional.SkinEnabled) if (Config.Additional.SkinEnabled)
{ {
_ = Task.Run(async () => await weaponSync.GetWeaponPaintsFromDatabase(playerInfo)); Task.Run(() => weaponSync.GetWeaponPaintsFromDatabase(playerInfo));
} }
if (Config.Additional.KnifeEnabled) if (Config.Additional.KnifeEnabled)
{ {
_ = Task.Run(async () => await weaponSync.GetKnifeFromDatabase(playerInfo)); Task.Run(() => weaponSync.GetKnifeFromDatabase(playerInfo));
} }
if (Config.Additional.GloveEnabled) if (Config.Additional.GloveEnabled)
{ {
_ = Task.Run(async () => await weaponSync.GetGloveFromDatabase(playerInfo)); Task.Run(() => weaponSync.GetGloveFromDatabase(playerInfo));
} }
if (Config.Additional.AgentEnabled)
{
_ = Task.Run(async () => await weaponSync.GetAgentFromDatabase(playerInfo));
}
if (Config.Additional.MusicEnabled)
{
_ = Task.Run(async () => await weaponSync.GetMusicFromDatabase(playerInfo));
}
*/
} }
catch catch (Exception ex)
{ {
Console.WriteLine($"An error occurred: {ex.Message}");
} }
return HookResult.Continue; return HookResult.Continue;
@@ -63,238 +49,261 @@ namespace WeaponPaints
[GameEventHandler] [GameEventHandler]
public HookResult OnPlayerDisconnect(EventPlayerDisconnect @event, GameEventInfo info) public HookResult OnPlayerDisconnect(EventPlayerDisconnect @event, GameEventInfo info)
{ {
CCSPlayerController? player = @event.Userid; CCSPlayerController player = @event.Userid;
if (player is null || !player.IsValid || player.IsBot) return HookResult.Continue; if (player is null || !player.IsValid || player.IsBot ||
player.IsHLTV || player.SteamID.ToString().Length != 17) return HookResult.Continue;
var playerInfo = new PlayerInfo PlayerInfo playerInfo = new(
{ (int)player.Index,
UserId = player.UserId, player.Slot,
Slot = player.Slot, player.UserId,
Index = (int)player.Index, player.SteamID.ToString(),
SteamId = player.SteamID.ToString(), player.PlayerName,
Name = player.PlayerName, player.IpAddress?.Split(":")[0]
IpAddress = player.IpAddress?.Split(":")[0] );
};
if (!GPlayerWeaponsInfo.TryGetValue(player.Slot, out var weaponInfos)) if (weaponSync != null)
return HookResult.Continue; {
// Run weapon sync tasks asynchronously
if (WeaponSync != null) Task.Run(async () =>
_ = Task.Run(async () => await WeaponSync.SyncStatTrakToDatabase(playerInfo, weaponInfos)); {
await weaponSync.SyncWeaponPaintsToDatabase(playerInfo);
});
if (Config.Additional.SkinEnabled) // Remove player data
{ if (Config.Additional.SkinEnabled)
GPlayerWeaponsInfo.TryRemove(player.Slot, out _); {
} gPlayerWeaponsInfo.TryRemove(player.Slot, out _);
if (Config.Additional.KnifeEnabled) }
{ if (Config.Additional.KnifeEnabled)
GPlayersKnife.TryRemove(player.Slot, out _); {
} g_playersKnife.TryRemove(player.Slot, out _);
if (Config.Additional.GloveEnabled) }
{ if (Config.Additional.GloveEnabled)
GPlayersGlove.TryRemove(player.Slot, out _); {
} g_playersGlove.TryRemove(player.Slot, out _);
if (Config.Additional.AgentEnabled) }
{
GPlayersAgent.TryRemove(player.Slot, out _);
}
if (Config.Additional.MusicEnabled)
{
GPlayersMusic.TryRemove(player.Slot, out _);
}
if (Config.Additional.PinsEnabled)
{
GPlayersPin.TryRemove(player.Slot, out _);
} }
_temporaryPlayerWeaponWear.TryRemove(player.Slot, out _); // Remove player's command cooldown
CommandsCooldown.Remove(player.Slot); commandsCooldown.Remove(player.Slot);
return HookResult.Continue; return HookResult.Continue;
} }
private void OnEntityCreated(CEntityInstance entity)
{
if (!Config.Additional.SkinEnabled) return;
if (entity == null || !entity.IsValid || string.IsNullOrEmpty(entity.DesignerName)) return;
string 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;
CBasePlayerPawn? pawn = Utilities.GetEntityFromIndex<CCSPlayerPawn>(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) { }
});
}
public HookResult OnPickup(CEntityIOOutput output, string name, CEntityInstance activator, CEntityInstance caller, CVariant value, float delay)
{
if (!Config.Additional.GiveKnifeAfterRemove)
return HookResult.Continue;
CCSPlayerController? player = Utilities.GetEntityFromIndex<CCSPlayerPawn>((int)activator.Index).OriginalController.Value;
if (player == null || player.IsBot || player.IsHLTV ||
player.SteamID.ToString().Length != 17 || !g_knifePickupCount.TryGetValue(player.Slot, out var pickupCount) ||
!g_playersKnife.ContainsKey(player.Slot))
{
return HookResult.Continue;
}
CBasePlayerWeapon weapon = new(caller.Handle);
if (weapon.AttributeManager.Item.ItemDefinitionIndex != 42 && weapon.AttributeManager.Item.ItemDefinitionIndex != 59)
{
return HookResult.Continue;
}
if (pickupCount >= 2)
{
return HookResult.Continue;
}
if (g_playersKnife[player.Slot] != "weapon_knife")
{
pickupCount++;
g_knifePickupCount[player.Slot] = pickupCount;
AddTimer(0.2f, () => RefreshWeapons(player));
}
return HookResult.Continue;
}
private void OnMapStart(string mapName) private void OnMapStart(string mapName)
{ {
if (Config.Additional is { KnifeEnabled: false, SkinEnabled: false, GloveEnabled: false }) return; if (!Config.Additional.KnifeEnabled && !Config.Additional.SkinEnabled && !Config.Additional.GloveEnabled) return;
if (Database != null) if (_database != null)
WeaponSync = new WeaponSynchronization(Database, Config); weaponSync = new WeaponSynchronization(_database, Config);
// 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");
});
} }
private HookResult OnPlayerSpawn(EventPlayerSpawn @event, GameEventInfo info) private HookResult OnPlayerSpawn(EventPlayerSpawn @event, GameEventInfo info)
{ {
CCSPlayerController? player = @event.Userid; CCSPlayerController? player = @event.Userid;
if (player is null || !player.IsValid || Config.Additional is { KnifeEnabled: false, GloveEnabled: false }) if (player is null || !player.IsValid || !Config.Additional.KnifeEnabled && !Config.Additional.GloveEnabled)
return HookResult.Continue; return HookResult.Continue;
CCSPlayerPawn? pawn = player.PlayerPawn.Value; g_knifePickupCount[player.Slot] = 0;
if (pawn == null || !pawn.IsValid) if (!PlayerHasKnife(player))
return HookResult.Continue; GiveKnifeToPlayer(player);
GivePlayerMusicKit(player); Server.NextFrame(() =>
GivePlayerAgent(player); {
GivePlayerGloves(player); RefreshGloves(player);
});
return HookResult.Continue; return HookResult.Continue;
} }
private HookResult OnRoundEnd(EventRoundEnd @event, GameEventInfo info) private HookResult OnRoundEnd(EventRoundEnd @event, GameEventInfo info)
{ {
_gBCommandsAllowed = false; g_bCommandsAllowed = false;
return HookResult.Continue; return HookResult.Continue;
} }
private HookResult OnRoundStart(EventRoundStart @event, GameEventInfo info) private HookResult OnRoundStart(EventRoundStart @event, GameEventInfo info)
{ {
_gBCommandsAllowed = true; NativeAPI.IssueServerCommand("mp_t_default_melee \"\"");
return HookResult.Continue; NativeAPI.IssueServerCommand("mp_ct_default_melee \"\"");
} NativeAPI.IssueServerCommand("mp_equipment_reset_rounds 0");
public HookResult OnGiveNamedItemPost(DynamicHook hook) g_bCommandsAllowed = true;
{
try
{
var itemServices = hook.GetParam<CCSPlayer_ItemServices>(0);
var weapon = hook.GetReturn<CBasePlayerWeapon>();
if (!weapon.DesignerName.Contains("weapon"))
return HookResult.Continue;
var player = GetPlayerFromItemServices(itemServices);
if (player != null)
GivePlayerWeaponSkin(player, weapon);
}
catch { }
return HookResult.Continue; return HookResult.Continue;
} }
public void OnEntityCreated(CEntityInstance entity)
{
var designerName = entity.DesignerName;
if (designerName.Contains("weapon"))
{
Server.NextFrame(() =>
{
var weapon = new CBasePlayerWeapon(entity.Handle);
if (!weapon.IsValid) return;
try
{
SteamID? steamid = null;
if (weapon.OriginalOwnerXuidLow > 0)
steamid = new SteamID(weapon.OriginalOwnerXuidLow);
CCSPlayerController? player;
if (steamid != null && steamid.IsValid())
{
player = Utilities.GetPlayers().FirstOrDefault(p => p.IsValid && p.SteamID == steamid.SteamId64);
if (player == null)
player = Utilities.GetPlayerFromSteamId(weapon.OriginalOwnerXuidLow);
}
else
{
CCSWeaponBaseGun gun = weapon.As<CCSWeaponBaseGun>();
player = Utilities.GetPlayerFromIndex((int)weapon.OwnerEntity.Index) ?? Utilities.GetPlayerFromIndex((int)gun.OwnerEntity.Value!.Index);
}
if (string.IsNullOrEmpty(player?.PlayerName)) return;
if (!Utility.IsPlayerValid(player)) return;
GivePlayerWeaponSkin(player, weapon);
}
catch (Exception)
{
}
});
}
}
private void OnTick() private void OnTick()
{ {
if (!Config.Additional.ShowSkinImage) return;
foreach (var player in Utilities.GetPlayers().Where(p => foreach (var player in Utilities.GetPlayers().Where(p =>
p is { IsValid: true, PlayerPawn.IsValid: true } && p is not null && p.IsValid &&
(LifeState_t)p.LifeState == LifeState_t.LIFE_ALIVE (LifeState_t)p.LifeState == LifeState_t.LIFE_ALIVE && p.SteamID.ToString().Length == 17
&& !p.IsBot && p is { Connected: PlayerConnectedState.PlayerConnected } && !p.IsBot && !p.IsHLTV && p.Connected == PlayerConnectedState.PlayerConnected && p.Team != CounterStrikeSharp.API.Modules.Utils.CsTeam.None
) )
) )
{ {
if (_playerWeaponImage.TryGetValue(player.Slot, out var value) && !string.IsNullOrEmpty(value)) try
{
if (Config.Additional.ShowSkinImage && PlayerWeaponImage.ContainsKey(player.Slot) && !string.IsNullOrEmpty(PlayerWeaponImage[player.Slot]))
{
player.PrintToCenterHtml("<img src='{PATH}'</img>".Replace("{PATH}", PlayerWeaponImage[player.Slot]));
}
if (player.PlayerPawn?.IsValid != true || player.PlayerPawn?.Value?.IsValid != true)
continue;
var viewModels = GetPlayerViewModels(player);
if (viewModels == null || viewModels.Length == 0)
continue;
var viewModel = viewModels[0];
if (viewModel == null || viewModel.Value == null || viewModel.Value.Weapon == null || viewModel.Value.Weapon.Value == null)
continue;
var weapon = viewModel.Value.Weapon.Value;
if (weapon == null || !weapon.IsValid || weapon.FallbackPaintKit == 0)
continue;
if (viewModel.Value.VMName.Contains("knife"))
continue;
var sceneNode = viewModel.Value.CBodyComponent?.SceneNode;
if (sceneNode == null)
continue;
var skeleton = GetSkeletonInstance(sceneNode);
if (skeleton == null)
continue;
bool skeletonChange = false;
int[] newPaints = { 1171, 1170, 1169, 1164, 1162, 1161, 1159, 1175, 1174, 1167, 1165, 1168, 1163, 1160, 1166, 1173 };
if (newPaints.Contains(weapon.FallbackPaintKit))
{
if (skeleton.ModelState.MeshGroupMask != 1)
{
skeleton.ModelState.MeshGroupMask = 1;
skeletonChange = true;
}
}
else
{
if (skeleton.ModelState.MeshGroupMask != 2)
{
skeleton.ModelState.MeshGroupMask = 2;
skeletonChange = true;
}
}
if (skeletonChange)
Utilities.SetStateChanged(viewModel.Value, "CBaseEntity", "m_CBodyComponent");
}
catch (Exception)
{ {
player.PrintToCenterHtml("<img src='{PATH}'</img>".Replace("{PATH}", value));
} }
} }
} }
[GameEventHandler]
public HookResult OnItemPickup(EventItemPickup @event, GameEventInfo _)
{
if (!IsWindows) return HookResult.Continue;
var player = @event.Userid;
if (player != null && player is { IsValid: true, Connected: PlayerConnectedState.PlayerConnected, PawnIsAlive: true, PlayerPawn.IsValid: true })
{
GiveOnItemPickup(player);
}
return HookResult.Continue;
}
private HookResult OnPlayerDeath(EventPlayerDeath @event, GameEventInfo info)
{
CCSPlayerController? player = @event.Attacker;
if (player is null || !player.IsValid)
return HookResult.Continue;
if (!GPlayerWeaponsInfo.TryGetValue(player.Slot, out _)) return HookResult.Continue;
CBasePlayerWeapon? weapon = player.PlayerPawn.Value?.WeaponServices?.ActiveWeapon.Value;
if (weapon == null) return HookResult.Continue;
int weaponDefIndex = weapon.AttributeManager.Item.ItemDefinitionIndex;
if (!GPlayerWeaponsInfo[player.Slot].TryGetValue(weaponDefIndex, out var weaponInfo) || weaponInfo.Paint == 0)
return HookResult.Continue;
if (!weaponInfo.StatTrak) return HookResult.Continue;
weaponInfo.StatTrakCount += 1;
CAttributeListSetOrAddAttributeValueByName.Invoke(weapon.AttributeManager.Item.NetworkedDynamicAttributes.Handle, "kill eater", ViewAsFloat((uint)weaponInfo.StatTrakCount));
CAttributeListSetOrAddAttributeValueByName.Invoke(weapon.AttributeManager.Item.NetworkedDynamicAttributes.Handle, "kill eater score type", 0);
CAttributeListSetOrAddAttributeValueByName.Invoke(weapon.AttributeManager.Item.AttributeList.Handle, "kill eater", ViewAsFloat((uint)weaponInfo.StatTrakCount));
CAttributeListSetOrAddAttributeValueByName.Invoke(weapon.AttributeManager.Item.AttributeList.Handle, "kill eater score type", 0);
return HookResult.Continue;
}
private void RegisterListeners() private void RegisterListeners()
{ {
RegisterListener<Listeners.OnEntitySpawned>(OnEntityCreated);
//RegisterListener<Listeners.OnClientPutInServer>(OnClientPutInServer);
//RegisterListener<Listeners.OnClientDisconnect>(OnClientDisconnect);
RegisterListener<Listeners.OnMapStart>(OnMapStart); RegisterListener<Listeners.OnMapStart>(OnMapStart);
RegisterListener<Listeners.OnTick>(OnTick);
RegisterEventHandler<EventPlayerSpawn>(OnPlayerSpawn); RegisterEventHandler<EventPlayerSpawn>(OnPlayerSpawn);
RegisterEventHandler<EventRoundStart>(OnRoundStart); RegisterEventHandler<EventRoundStart>(OnRoundStart, HookMode.Pre);
RegisterEventHandler<EventRoundEnd>(OnRoundEnd); RegisterEventHandler<EventRoundEnd>(OnRoundEnd);
RegisterListener<Listeners.OnEntityCreated>(OnEntityCreated);
RegisterEventHandler<EventPlayerDeath>(OnPlayerDeath);
if (Config.Additional.ShowSkinImage) //RegisterEventHandler<EventItemPickup>(OnItemPickup);
RegisterListener<Listeners.OnTick>(OnTick);
if (!IsWindows) HookEntityOutput("weapon_knife", "OnPlayerPickup", OnPickup);
VirtualFunctions.GiveNamedItemFunc.Hook(OnGiveNamedItemPost, HookMode.Post);
} }
} }
} }

View File

@@ -5,12 +5,17 @@ namespace WeaponPaints;
public static class PlayerExtensions public static class PlayerExtensions
{ {
public static void Print(this CCSPlayerController controller, string message) public static void Print(this CCSPlayerController controller, string message)
{ {
if (WeaponPaints._localizer == null) return; if (WeaponPaints._localizer == null)
{
StringBuilder _message = new(WeaponPaints._localizer["wp_prefix"]); controller.PrintToChat(message);
_message.Append(message); }
controller.PrintToChat(_message.ToString()); else
} {
StringBuilder _message = new(WeaponPaints._localizer["wp_prefix"]);
_message.Append(message);
controller.PrintToChat(_message.ToString());
}
}
} }

View File

@@ -1,12 +1,24 @@
namespace WeaponPaints namespace WeaponPaints
{ {
public class PlayerInfo public class PlayerInfo
{ {
public int Index { get; set; } public int Index { get; set; }
public int Slot { get; init; } public int Slot { get; set; }
public int? UserId { get; set; } public int? UserId { get; set; }
public string? SteamId { get; init; } public string? SteamId { get; set; }
public string? Name { get; set; } public string? Name { get; set; }
public string? IpAddress { get; set; } public string? IpAddress { get; set; }
}
public PlayerInfo() { }
public PlayerInfo(int index, int slot, int? userId, string? steamId, string? name, string? ipAddress)
{
Index = index;
Slot = slot;
UserId = userId;
SteamId = steamId;
Name = name;
IpAddress = ipAddress;
}
}
} }

View File

@@ -9,31 +9,21 @@ Unfinished, unoptimized and not fully functional ugly demo weapon paints plugin
[![ko-fi](https://ko-fi.com/img/githubbutton_sm.svg)](https://ko-fi.com/E1E2G0P2O) or [![Donate on Steam](https://github.com/Nereziel/cs2-WeaponPaints/assets/32937653/a0d53822-4ca7-4caf-83b4-e1a9b5f8c94e)](https://steamcommunity.com/tradeoffer/new/?partner=41515647&token=gW2W-nXE) [![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 ## Features
- Changes only paint, seed and wear on weapons, knives, gloves and agents - Changes only paint, seed and wear on weapons and knives
- MySQL based - MySQL based
- Data syncs on player connect - Data syncs on player connect
- Added command **`!wp`** to refresh skins ***(with cooldown in seconds can be configured)*** - Added command **`!wp`** to refresh skins ***(with cooldown in seconds can be configured)***
- Added command **`!ws`** to show website - Added command **`!ws`** to show website
- Added command **`!knife`** to show menu with knives - Added command **`!knife`** to show menu with knives
- Added command **`!gloves`** to show menu with gloves - Knife change is now limited to have these cvars empty **`mp_t_default_melee ""`** and **`mp_ct_default_melee ""`**
- Added command **`!agents`** to show menu with agents
- Translations support, submit a PR if you want to share your translation - Translations support, submit a PR if you want to share your translation
## ⚙️ Requirements
**Ensure all the following dependencies are installed before proceeding**
- [CounterStrikeSharp](https://github.com/roflmuffin/CounterStrikeSharp)
- [PlayerSettings](https://github.com/NickFox007/PlayerSettingsCS2) - Required by MenuManagerCS2
- [AnyBaseLibCS2](https://github.com/NickFox007/AnyBaseLibCS2) - Required by PlayerSettings
- [MenuManagerCS2](https://github.com/NickFox007/MenuManagerCS2)
- MySQL database
## CS2 Server ## CS2 Server
- Have working CounterStrikeSharp (**with RUNTIME!**) - Have working CounterStrikeSharp (**with RUNTIME!**)
- Download from Release and copy plugin to plugins - Download from Release and copy plugin to plugins
- Run server with plugin, **it will generate config if installed correctly!** - Run server with plugin, **it will generate config if installed correctly!**
- Edit `addons/counterstrikesharp/configs/`**`plugins/WeaponPaints/WeaponPaints.json`** include database credentials - Edit `addons/counterstrikesharp/configs/`**`plugins/WeaponPaints/WeaponPaints.json`** include database credentials
- In `addons/counterstrikesharp/configs/`**`core.json`** set **FollowCS2ServerGuidelines** to **`false`** - In `addons/counterstrikesharp/configs/`**`core.json`** set **FollowCS2ServerGuidelines** to **`false`**
- Copy from plugins folder gamedata file **`weaponpaints.json`** to folder **`addons/counterstrikesharp/gamedata/`**
## Plugin Configuration ## Plugin Configuration
<details> <details>
@@ -62,6 +52,7 @@ Unfinished, unoptimized and not fully functional ugly demo weapon paints plugin
"SkinMenuTitle": "Select skin for {WEAPON}" // Menu title (!skins menu, after weapon select) "SkinMenuTitle": "Select skin for {WEAPON}" // Menu title (!skins menu, after weapon select)
}, },
"Additional": { "Additional": {
"SkinVisibilityFix": true, // Enable or disable fix for skin visibility
"KnifeEnabled": true, // Enable or disable knife feature "KnifeEnabled": true, // Enable or disable knife feature
"SkinEnabled": true, // Enable or disable skin feature "SkinEnabled": true, // Enable or disable skin feature
"CommandWpEnabled": true, // Enable or disable refreshing command "CommandWpEnabled": true, // Enable or disable refreshing command
@@ -74,11 +65,13 @@ Unfinished, unoptimized and not fully functional ugly demo weapon paints plugin
"GiveRandomKnife": false, // Give random knife to players if they didn't choose "GiveRandomKnife": false, // Give random knife to players if they didn't choose
"GiveRandomSkins": false // Give random skins to players if they didn't choose "GiveRandomSkins": false // Give random skins to players if they didn't choose
}, },
</pre></code>
"ConfigVersion": 4 // Don't touch
}</pre></code>
</details> </details>
## Web install ## Web install
- Requires PHP >= 7.4 with curl and pdo_mysql ***(Tested on php ver **`8.2.3`** and nginx webserver)*** - Requires PHP >= 7.4 ***(Tested on php ver **`8.2.3`** and nginx webserver)***
- **Before using website, make sure the plugin is correctly loaded in cs2 server!** Mysql tables are created by plugin not by website. - **Before using website, make sure the plugin is correctly loaded in cs2 server!** Mysql tables are created by plugin not by website.
- Copy website to web server ***(Folder `img` not needed)*** - Copy website to web server ***(Folder `img` not needed)***
- Get [Steam API Key](https://steamcommunity.com/dev/apikey) - Get [Steam API Key](https://steamcommunity.com/dev/apikey)
@@ -90,6 +83,11 @@ Unfinished, unoptimized and not fully functional ugly demo weapon paints plugin
- Steam login/logout - Steam login/logout
- Change knife, paint, seed and wear - Change knife, paint, seed and wear
## Known issues
- Issue on Windows servers, no knives are given.
- You can't change knife if it's equpied in cs2 inventory
- Can cause incompatibility with plugins/maps which manipulates weapons and knives
## Troubleshooting ## Troubleshooting
<details> <details>
**Skins are not changing:** **Skins are not changing:**
@@ -98,6 +96,11 @@ Set FollowCSGOGuidelines to false in cssharps core.jcon config
**Database error table does not exists:** **Database error table does not exists:**
Plugin is not loaded or configured with mysql credentials. Tables are auto-created by plugin. Plugin is not loaded or configured with mysql credentials. Tables are auto-created by plugin.
**Knives are disappearing:**
Set in config GiveKnifeAfterRemove to true
**Knives are not changing for players:**
You can't change knife if you have your own equipped
</details> </details>
### 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/) ### 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/)

25
SchemaString.cs Normal file
View File

@@ -0,0 +1,25 @@
using CounterStrikeSharp.API;
using CounterStrikeSharp.API.Modules.Memory;
using System.Runtime.CompilerServices;
using System.Text;
namespace WeaponPaints;
public class SchemaString<TSchemaClass> : NativeObject where TSchemaClass : NativeObject
{
internal SchemaString(TSchemaClass instance, string member) : base(Schema.GetSchemaValue<nint>(instance.Handle, typeof(TSchemaClass).Name!, member))
{ }
internal unsafe void Set(string str)
{
var bytes = Encoding.UTF8.GetBytes(str);
var handle = Handle.ToInt64();
for (var i = 0; i < bytes.Length; i++)
{
Unsafe.Write((void*)(handle + i), bytes[i]);
}
Unsafe.Write((void*)(handle + bytes.Length), 0);
}
}

View File

@@ -1,9 +1,11 @@
using CounterStrikeSharp.API.Core; using CounterStrikeSharp.API.Core;
using CounterStrikeSharp.API.Core.Translations; using CounterStrikeSharp.API.Modules.Utils;
using Dapper; using Dapper;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
using MySqlConnector;
using Newtonsoft.Json; using Newtonsoft.Json;
using Newtonsoft.Json.Linq; using Newtonsoft.Json.Linq;
using System.Reflection;
namespace WeaponPaints namespace WeaponPaints
{ {
@@ -11,75 +13,63 @@ namespace WeaponPaints
{ {
internal static WeaponPaintsConfig? Config { get; set; } 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,
Pooling = true
};
return builder.ConnectionString;
}
internal static async Task CheckDatabaseTables() internal static async Task CheckDatabaseTables()
{ {
if (WeaponPaints.Database is null) return; if (WeaponPaints._database is null) return;
try try
{ {
await using var connection = await WeaponPaints.Database.GetConnectionAsync(); await using var connection = await WeaponPaints._database.GetConnectionAsync();
await using var transaction = await connection.BeginTransactionAsync(); await using var transaction = await connection.BeginTransactionAsync();
try try
{ {
string[] createTableQueries = string[] createTableQueries = new[]
[ {
""" @"CREATE TABLE IF NOT EXISTS `wp_player_skins` (
CREATE TABLE IF NOT EXISTS `wp_player_skins` ( `steamid` varchar(64) NOT NULL,
`steamid` varchar(18) NOT NULL, `weapon_defindex` int(6) NOT NULL,
`weapon_defindex` int(6) NOT NULL, `weapon_paint_id` int(6) NOT NULL,
`weapon_paint_id` int(6) NOT NULL, `weapon_wear` float NOT NULL DEFAULT 0.000001,
`weapon_wear` float NOT NULL DEFAULT 0.000001, `weapon_seed` int(16) NOT NULL DEFAULT 0
`weapon_seed` int(16) NOT NULL DEFAULT 0, ) ENGINE=InnoDB",
`weapon_nametag` VARCHAR(128) DEFAULT NULL,
`weapon_stattrak` tinyint(1) NOT NULL,
`weapon_stattrak_count` int(10) NOT NULL,
`weapon_sticker_0` VARCHAR(128) NOT NULL DEFAULT '0;0;0;0;0;0;0' COMMENT 'id;schema;x;y;wear;scale;rotation',
`weapon_sticker_1` VARCHAR(128) NOT NULL DEFAULT '0;0;0;0;0;0;0' COMMENT 'id;schema;x;y;wear;scale;rotation',
`weapon_sticker_2` VARCHAR(128) NOT NULL DEFAULT '0;0;0;0;0;0;0' COMMENT 'id;schema;x;y;wear;scale;rotation',
`weapon_sticker_3` VARCHAR(128) NOT NULL DEFAULT '0;0;0;0;0;0;0' COMMENT 'id;schema;x;y;wear;scale;rotation',
`weapon_sticker_4` VARCHAR(128) NOT NULL DEFAULT '0;0;0;0;0;0;0' COMMENT 'id;schema;x;y;wear;scale;rotation',
`weapon_keychain`VARCHAR(128) NOT NULL DEFAULT '0;0;0;0;0' COMMENT 'id;x;y;z;seed'
) ENGINE=InnoDB
""",
@"CREATE TABLE IF NOT EXISTS `wp_player_knife` ( @"CREATE TABLE IF NOT EXISTS `wp_player_knife` (
`steamid` varchar(18) NOT NULL, `steamid` varchar(64) NOT NULL,
`knife` varchar(64) NOT NULL, `knife` varchar(64) NOT NULL,
UNIQUE (`steamid`) UNIQUE (`steamid`)
) ENGINE = InnoDB", ) ENGINE = InnoDB",
""" @"CREATE TABLE IF NOT EXISTS `wp_player_gloves` (
CREATE TABLE IF NOT EXISTS `wp_player_gloves` ( `steamid` varchar(64) NOT NULL,
`steamid` varchar(18) NOT NULL, `weapon_defindex` int(11) NOT NULL,
`weapon_defindex` int(11) NOT NULL, UNIQUE (`steamid`)
UNIQUE (`steamid`) ) ENGINE=InnoDB"
) ENGINE=InnoDB };
""", /*string[] createTableQueries = new[]
""" {
CREATE TABLE IF NOT EXISTS `wp_player_agents` ( @"CREATE TABLE IF NOT EXISTS `wp_players` (`steamid` BIGINT UNSIGNED NOT NULL, `created_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, `updated_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (`steamid`)) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;",
`steamid` varchar(18) NOT NULL, @"CREATE TABLE IF NOT EXISTS `wp_players_skins` (`steamid` BIGINT UNSIGNED NOT NULL, `team` SMALLINT UNSIGNED NOT NULL, `weapon` SMALLINT UNSIGNED NOT NULL, `paint` SMALLINT UNSIGNED NOT NULL, `wear` FLOAT NOT NULL DEFAULT 0.00001, `seed` SMALLINT UNSIGNED NOT NULL DEFAULT 0, `nametag` VARCHAR(20) DEFAULT NULL, `stattrack` INT UNSIGNED NOT NULL DEFAULT 0, `stattrack_enabled` SMALLINT NOT NULL DEFAULT 0, PRIMARY KEY (`steamid`,`weapon`,`team`), FOREIGN KEY (`steamid`)) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;",
`agent_ct` varchar(64) DEFAULT NULL, @"CREATE TABLE IF NOT EXISTS `wp_players_knife` (`steamid` BIGINT UNSIGNED NOT NULL, `knife_t` SMALLINT UNSIGNED NOT NULL, `knife_ct` SMALLINT UNSIGNED NOT NULL, PRIMARY KEY (`steamid`), FOREIGN KEY (`steamid`)) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;",
`agent_t` varchar(64) DEFAULT NULL, @"CREATE TABLE IF NOT EXISTS `wp_players_music` (`steamid` BIGINT UNSIGNED NOT NULL, `music` SMALLINT UNSIGNED DEFAULT NULL, PRIMARY KEY (`steamid`), FOREIGN KEY (`steamid`)) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;",
UNIQUE (`steamid`) @"CREATE TABLE IF NOT EXISTS `wp_players_gloves` (`steamid` BIGINT UNSIGNED NOT NULL, `glove_t` SMALLINT UNSIGNED NOT NULL, `glove_ct` SMALLINT UNSIGNED NOT NULL, PRIMARY KEY (`steamid`)) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;"
) ENGINE=InnoDB };*/
""", foreach (var query in createTableQueries)
"""
CREATE TABLE IF NOT EXISTS `wp_player_music` (
`steamid` varchar(64) NOT NULL,
`music_id` int(11) NOT NULL,
UNIQUE (`steamid`)
) ENGINE=InnoDB
""",
"""
CREATE TABLE IF NOT EXISTS `wp_player_pins` (
`steamid` varchar(64) NOT NULL,
`id` int(11) NOT NULL,
UNIQUE (`steamid`)
) ENGINE=InnoDB
""",
];
foreach (var query in createTableQueries)
{ {
await connection.ExecuteAsync(query, transaction: transaction); await connection.ExecuteAsync(query, transaction: transaction);
} }
@@ -100,64 +90,37 @@ namespace WeaponPaints
internal static bool IsPlayerValid(CCSPlayerController? player) internal static bool IsPlayerValid(CCSPlayerController? player)
{ {
if (player is null || WeaponPaints.WeaponSync is null) return false; if (player is null) return false;
return player is { IsValid: true, IsBot: false, IsHLTV: false, UserId: not null }; return (player is not null && player.IsValid && !player.IsBot && !player.IsHLTV && player.UserId.HasValue
&& WeaponPaints.weaponSync != null && player.Connected == PlayerConnectedState.PlayerConnected && player.SteamID.ToString().Length == 17);
} }
internal static void LoadSkinsFromFile(string filePath, ILogger logger) internal static void LoadSkinsFromFile(string filePath)
{
var json = File.ReadAllText(filePath);
try
{
var deserializedSkins = JsonConvert.DeserializeObject<List<JObject>>(json);
WeaponPaints.SkinsList = deserializedSkins ?? [];
}
catch (FileNotFoundException)
{
logger?.LogError("Not found \"skins.json\" file");
}
}
internal static void LoadGlovesFromFile(string filePath, ILogger logger)
{ {
try try
{ {
var json = File.ReadAllText(filePath); string json = File.ReadAllText(filePath);
var deserializedSkins = JsonConvert.DeserializeObject<List<JObject>>(json); var deserializedSkins = JsonConvert.DeserializeObject<List<JObject>>(json);
WeaponPaints.GlovesList = deserializedSkins ?? []; WeaponPaints.skinsList = deserializedSkins ?? new List<JObject>();
} }
catch (FileNotFoundException) catch (FileNotFoundException)
{ {
logger?.LogError("Not found \"gloves.json\" file"); throw;
} }
} }
internal static void LoadAgentsFromFile(string filePath, ILogger logger) internal static void LoadGlovesFromFile(string filePath)
{ {
try try
{ {
var json = File.ReadAllText(filePath); string json = File.ReadAllText(filePath);
var deserializedSkins = JsonConvert.DeserializeObject<List<JObject>>(json); var deserializedSkins = JsonConvert.DeserializeObject<List<JObject>>(json);
WeaponPaints.AgentsList = deserializedSkins ?? []; WeaponPaints.glovesList = deserializedSkins ?? new List<JObject>();
} }
catch (FileNotFoundException) catch (FileNotFoundException)
{ {
logger?.LogError("Not found \"agents.json\" file"); throw;
}
}
internal static void LoadMusicFromFile(string filePath, ILogger logger)
{
try
{
var json = File.ReadAllText(filePath);
var deserializedSkins = JsonConvert.DeserializeObject<List<JObject>>(json);
WeaponPaints.MusicList = deserializedSkins ?? [];
}
catch (FileNotFoundException)
{
logger?.LogError("Not found \"music.json\" file");
} }
} }
@@ -171,49 +134,68 @@ namespace WeaponPaints
internal static string ReplaceTags(string message) internal static string ReplaceTags(string message)
{ {
return message.ReplaceColorTags(); if (message.Contains('{'))
{
string modifiedValue = message;
if (Config != null)
{
modifiedValue = modifiedValue.Replace("{WEBSITE}", Config.Website);
}
foreach (FieldInfo field in typeof(ChatColors).GetFields())
{
string pattern = $"{{{field.Name}}}";
if (message.Contains(pattern, StringComparison.OrdinalIgnoreCase))
{
modifiedValue = modifiedValue.Replace(pattern, field.GetValue(null)!.ToString(), StringComparison.OrdinalIgnoreCase);
}
}
return modifiedValue;
}
return message;
} }
internal static async Task CheckVersion(string version, ILogger logger) internal static async Task CheckVersion(string version, ILogger logger)
{ {
using HttpClient client = new(); using (HttpClient client = new HttpClient())
try
{ {
var response = await client.GetAsync("https://raw.githubusercontent.com/Nereziel/cs2-WeaponPaints/main/VERSION").ConfigureAwait(false); try
if (response.IsSuccessStatusCode)
{ {
var remoteVersion = await response.Content.ReadAsStringAsync().ConfigureAwait(false); HttpResponseMessage response = await client.GetAsync("https://raw.githubusercontent.com/Nereziel/cs2-WeaponPaints/main/VERSION").ConfigureAwait(false);
remoteVersion = remoteVersion.Trim();
var comparisonResult = string.CompareOrdinal(version, remoteVersion); if (response.IsSuccessStatusCode)
switch (comparisonResult)
{ {
case < 0: string remoteVersion = await response.Content.ReadAsStringAsync().ConfigureAwait(false);
remoteVersion = remoteVersion.Trim();
int comparisonResult = string.Compare(version, remoteVersion);
if (comparisonResult < 0)
{
logger.LogWarning("Plugin is outdated! Check https://github.com/Nereziel/cs2-WeaponPaints"); logger.LogWarning("Plugin is outdated! Check https://github.com/Nereziel/cs2-WeaponPaints");
break; }
case > 0: else if (comparisonResult > 0)
{
logger.LogInformation("Probably dev version detected"); logger.LogInformation("Probably dev version detected");
break; }
default: else
{
logger.LogInformation("Plugin is up to date"); logger.LogInformation("Plugin is up to date");
break; }
}
else
{
logger.LogWarning("Failed to check version");
} }
} }
else catch (HttpRequestException ex)
{ {
logger.LogWarning("Failed to check version"); logger.LogError(ex, "Failed to connect to the version server.");
}
catch (Exception ex)
{
logger.LogError(ex, "An error occurred while checking version.");
} }
}
catch (HttpRequestException ex)
{
logger.LogError(ex, "Failed to connect to the version server.");
}
catch (Exception ex)
{
logger.LogError(ex, "An error occurred while checking version.");
} }
} }
@@ -231,5 +213,26 @@ namespace WeaponPaints
Console.WriteLine(" >> GitHub: https://github.com/Nereziel/cs2-WeaponPaints"); Console.WriteLine(" >> GitHub: https://github.com/Nereziel/cs2-WeaponPaints");
Console.WriteLine(" "); 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();
}
*/
} }
} }

View File

@@ -1 +1 @@
2.7a 1.9b

View File

@@ -1,166 +0,0 @@
using System.Collections.Concurrent;
using System.Runtime.InteropServices;
using CounterStrikeSharp.API.Core;
using CounterStrikeSharp.API.Core.Capabilities;
using CounterStrikeSharp.API.Modules.Memory.DynamicFunctions;
using MenuManager;
using Microsoft.Extensions.Localization;
using Newtonsoft.Json.Linq;
namespace WeaponPaints;
public partial class WeaponPaints
{
private 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_taser", "Zeus x27"},
{"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" },
{ "weapon_knife_kukri", "Kukri Knife" }
};
public static IStringLocalizer? _localizer;
internal static readonly ConcurrentDictionary<int, string> GPlayersKnife = new();
internal static readonly ConcurrentDictionary<int, ushort> GPlayersGlove = new();
internal static readonly ConcurrentDictionary<int, ushort> GPlayersMusic = new();
internal static readonly ConcurrentDictionary<int, ushort> GPlayersPin = new();
public static readonly ConcurrentDictionary<int, (string? CT, string? T)> GPlayersAgent = new();
internal static readonly ConcurrentDictionary<int, ConcurrentDictionary<int, WeaponInfo>> GPlayerWeaponsInfo = new();
internal static List<JObject> SkinsList = [];
internal static List<JObject> GlovesList = [];
internal static List<JObject> AgentsList = [];
internal static List<JObject> MusicList = [];
internal static WeaponSynchronization? WeaponSync;
private static bool _gBCommandsAllowed = true;
private readonly Dictionary<int, string> _playerWeaponImage = new();
private static readonly Dictionary<int, DateTime> CommandsCooldown = new();
internal static Database? Database;
private static readonly MemoryFunctionVoid<nint, string, float> CAttributeListSetOrAddAttributeValueByName = new(GameData.GetSignature("CAttributeList_SetOrAddAttributeValueByName"));
private static readonly MemoryFunctionWithReturn<nint, string, int, int> SetBodygroupFunc = new(
GameData.GetSignature("CBaseModelEntity_SetBodygroup"));
private static readonly Func<nint, string, int, int> SetBodygroup = SetBodygroupFunc.Invoke;
private static Dictionary<int, string> WeaponDefindex { get; } = new()
{
{ 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" },
{ 31, "weapon_taser" },
{ 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" },
{ 526, "weapon_knife_kukri" }
};
private const ulong MinimumCustomItemId = 65578;
private ulong _nextItemId = MinimumCustomItemId;
private static readonly bool IsWindows = RuntimeInformation.IsOSPlatform(OSPlatform.Windows);
private readonly ConcurrentDictionary<int, ConcurrentDictionary<int, float>> _temporaryPlayerWeaponWear = new();
internal static IMenuApi? MenuApi;
private static readonly PluginCapability<IMenuApi> MenuCapability = new("menu:nfcore");
}

View File

@@ -1,375 +1,407 @@
using CounterStrikeSharp.API; using CounterStrikeSharp.API;
using CounterStrikeSharp.API.Core; using CounterStrikeSharp.API.Core;
using CounterStrikeSharp.API.Modules.Entities.Constants;
using CounterStrikeSharp.API.Modules.Memory; using CounterStrikeSharp.API.Modules.Memory;
using CounterStrikeSharp.API.Modules.Timers; using CounterStrikeSharp.API.Modules.Timers;
using CounterStrikeSharp.API.Modules.Utils; using CounterStrikeSharp.API.Modules.Utils;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
using System.Collections.Concurrent;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
namespace WeaponPaints namespace WeaponPaints
{ {
public partial class WeaponPaints public partial class WeaponPaints
{ {
private void GivePlayerWeaponSkin(CCSPlayerController player, CBasePlayerWeapon weapon) internal static void ChangeWeaponAttributes(CBasePlayerWeapon? weapon, CCSPlayerController? player, bool isKnife = false)
{ {
if (!Config.Additional.SkinEnabled) return; if (player is null || weapon is null || !weapon.IsValid || !Utility.IsPlayerValid(player)) return;
if (!GPlayerWeaponsInfo.TryGetValue(player.Slot, out _)) return; if (!gPlayerWeaponsInfo.TryGetValue(player.Slot, out _)) return;
bool isKnife = weapon.DesignerName.Contains("knife") || weapon.DesignerName.Contains("bayonet"); if (isKnife && !g_playersKnife.ContainsKey(player.Slot) || isKnife && g_playersKnife[player.Slot] == "weapon_knife") return;
if (isKnife && !GPlayersKnife.ContainsKey(player.Slot) || isKnife && GPlayersKnife[player.Slot] == "weapon_knife") return; int weaponDefIndex = weapon.AttributeManager.Item.ItemDefinitionIndex;
int[] newPaints = { 106, 112, 113, 114, 115, 117, 118, 120, 121, 123, 126, 127, 128, 129, 130, 131, 133, 134, 137, 138, 139, 140, 142, 144, 145, 146, 152, 160, 161, 163, 173, 239, 292, 324, 331, 412, 461, 513, 766, 768, 770, 773, 774, 830, 831, 832, 834, 874, 875, 877, 878, 882, 883, 901, 912, 936, 937, 938, 939, 940, 1054, 1062, 1159, 1160, 1161, 1162, 1163, 1164, 1165, 1166, 1167, 1168, 1169, 1170, 1171, 1172, 1173, 1174, 1175, 1177, 1178, 1179, 1180 };
if (isKnife) if (isKnife)
{ {
var newDefIndex = WeaponDefindex.FirstOrDefault(x => x.Value == GPlayersKnife[player.Slot]);
if (newDefIndex.Key == 0) return;
if (weapon.AttributeManager.Item.ItemDefinitionIndex != newDefIndex.Key)
{
SubclassChange(weapon, (ushort)newDefIndex.Key);
}
weapon.AttributeManager.Item.ItemDefinitionIndex = (ushort)newDefIndex.Key;
weapon.AttributeManager.Item.EntityQuality = 3; weapon.AttributeManager.Item.EntityQuality = 3;
} }
UpdatePlayerEconItemId(weapon.AttributeManager.Item);
int weaponDefIndex = weapon.AttributeManager.Item.ItemDefinitionIndex; int fallbackPaintKit = weapon.FallbackPaintKit;
int fallbackPaintKit = 0;
weapon.AttributeManager.Item.AccountID = (uint)player.SteamID;
if (_config.Additional.GiveRandomSkin && if (_config.Additional.GiveRandomSkin &&
!GPlayerWeaponsInfo[player.Slot].ContainsKey(weaponDefIndex)) !gPlayerWeaponsInfo[player.Slot].ContainsKey(weaponDefIndex))
{ {
// Random skins // 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.FallbackPaintKit = GetRandomPaint(weaponDefIndex);
weapon.FallbackSeed = 0; weapon.FallbackSeed = 0;
weapon.FallbackWear = 0.01f; weapon.FallbackWear = 0.000001f;
weapon.AttributeManager.Item.NetworkedDynamicAttributes.Attributes.RemoveAll();
CAttributeListSetOrAddAttributeValueByName.Invoke(weapon.AttributeManager.Item.NetworkedDynamicAttributes.Handle, "set item texture prefab", GetRandomPaint(weaponDefIndex));
CAttributeListSetOrAddAttributeValueByName.Invoke(weapon.AttributeManager.Item.NetworkedDynamicAttributes.Handle, "set item texture seed", 0);
CAttributeListSetOrAddAttributeValueByName.Invoke(weapon.AttributeManager.Item.NetworkedDynamicAttributes.Handle, "set item texture wear", 0.01f);
weapon.AttributeManager.Item.AttributeList.Attributes.RemoveAll();
CAttributeListSetOrAddAttributeValueByName.Invoke(weapon.AttributeManager.Item.AttributeList.Handle, "set item texture prefab", GetRandomPaint(weaponDefIndex));
CAttributeListSetOrAddAttributeValueByName.Invoke(weapon.AttributeManager.Item.AttributeList.Handle, "set item texture seed", 0);
CAttributeListSetOrAddAttributeValueByName.Invoke(weapon.AttributeManager.Item.AttributeList.Handle, "set item texture wear", 0.01f);
fallbackPaintKit = weapon.FallbackPaintKit; fallbackPaintKit = weapon.FallbackPaintKit;
if (fallbackPaintKit == 0) if (fallbackPaintKit == 0)
return; return;
if (isKnife) return; var foundSkin = skinsList.FirstOrDefault(skin =>
UpdatePlayerWeaponMeshGroupMask(player, weapon, !newPaints.Contains(fallbackPaintKit)); ((int?)skin?["weapon_defindex"] ?? 0) == weaponDefIndex &&
((int?)skin?["paint"] ?? 0) == fallbackPaintKit &&
skin?["paint_name"] != null
);
string skinName = foundSkin?["paint_name"]?.ToString() ?? "";
if (!string.IsNullOrEmpty(skinName))
new SchemaString<CEconItemView>(weapon.AttributeManager.Item, "m_szCustomName").Set(skinName);
if (!isKnife && weapon.CBodyComponent != null && weapon.CBodyComponent.SceneNode != null)
{
var skeleton = GetSkeletonInstance(weapon.CBodyComponent.SceneNode);
int[] newPaints = { 1171, 1170, 1169, 1164, 1162, 1161, 1159, 1175, 1174, 1167, 1165, 1168, 1163, 1160, 1166, 1173 };
if (newPaints.Contains(fallbackPaintKit))
{
skeleton.ModelState.MeshGroupMask = 1;
}
else
{
if (skeleton.ModelState.MeshGroupMask != 2)
{
skeleton.ModelState.MeshGroupMask = 2;
}
}
}
var viewModels = GetPlayerViewModels(player);
if (viewModels == null || viewModels.Length == 0)
return;
var viewModel = viewModels[0];
if (viewModel == null || viewModel.Value == null || viewModel.Value.Weapon == null || viewModel.Value.Weapon.Value == null)
return;
Utilities.SetStateChanged(viewModel.Value, "CBaseEntity", "m_CBodyComponent");
return; return;
} }
if (!GPlayerWeaponsInfo[player.Slot].TryGetValue(weaponDefIndex, out var value) || value.Paint == 0) return; if (!gPlayerWeaponsInfo[player.Slot].ContainsKey(weaponDefIndex)) return;
WeaponInfo weaponInfo = gPlayerWeaponsInfo[player.Slot][weaponDefIndex];
var weaponInfo = value;
//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]}"); //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.ItemID = 16384;
weapon.AttributeManager.Item.ItemIDLow = 16384 & 0xFFFFFFFF; weapon.AttributeManager.Item.ItemIDLow = 16384 & 0xFFFFFFFF;
weapon.AttributeManager.Item.ItemIDHigh = weapon.AttributeManager.Item.ItemIDLow >> 32; weapon.AttributeManager.Item.ItemIDHigh = weapon.AttributeManager.Item.ItemIDLow >> 32;
weapon.AttributeManager.Item.CustomName = weaponInfo.Nametag;
weapon.FallbackPaintKit = weaponInfo.Paint; weapon.FallbackPaintKit = weaponInfo.Paint;
weapon.FallbackSeed = weaponInfo.Seed; weapon.FallbackSeed = weaponInfo.Seed;
weapon.FallbackWear = weaponInfo.Wear; weapon.FallbackWear = weaponInfo.Wear;
CAttributeListSetOrAddAttributeValueByName.Invoke(weapon.AttributeManager.Item.NetworkedDynamicAttributes.Handle, "set item texture prefab", weapon.FallbackPaintKit);
if (weaponInfo.StatTrak)
{
CAttributeListSetOrAddAttributeValueByName.Invoke(weapon.AttributeManager.Item.NetworkedDynamicAttributes.Handle, "kill eater", ViewAsFloat((uint)weaponInfo.StatTrakCount));
CAttributeListSetOrAddAttributeValueByName.Invoke(weapon.AttributeManager.Item.NetworkedDynamicAttributes.Handle, "kill eater score type", 0);
CAttributeListSetOrAddAttributeValueByName.Invoke(weapon.AttributeManager.Item.AttributeList.Handle, "kill eater", ViewAsFloat((uint)weaponInfo.StatTrakCount));
CAttributeListSetOrAddAttributeValueByName.Invoke(weapon.AttributeManager.Item.AttributeList.Handle, "kill eater score type", 0);
}
fallbackPaintKit = weapon.FallbackPaintKit; fallbackPaintKit = weapon.FallbackPaintKit;
if (fallbackPaintKit == 0) if (fallbackPaintKit == 0)
return; return;
if (isKnife) return; var foundSkin1 = skinsList.FirstOrDefault(skin =>
((int?)skin?["weapon_defindex"] ?? 0) == weaponDefIndex &&
((int?)skin?["paint"] ?? 0) == fallbackPaintKit &&
skin?["paint_name"] != null
);
if (weaponInfo.Stickers.Count > 0) SetStickers(player, weapon); var skinName1 = foundSkin1?["paint_name"]?.ToString() ?? "";
if (weaponInfo.KeyChain != null) SetKeychain(player, weapon); if (!string.IsNullOrEmpty(skinName1))
new SchemaString<CEconItemView>(weapon.AttributeManager.Item, "m_szCustomName").Set(skinName1);
UpdatePlayerWeaponMeshGroupMask(player, weapon, !newPaints.Contains(fallbackPaintKit)); if (!isKnife && weapon.CBodyComponent != null && weapon.CBodyComponent.SceneNode != null)
}
// silly method to update sticker when call RefreshWeapons()
private void IncrementWearForWeaponWithStickers(CCSPlayerController player, CBasePlayerWeapon weapon)
{
int weaponDefIndex = weapon.AttributeManager.Item.ItemDefinitionIndex;
if (!GPlayerWeaponsInfo.TryGetValue(player.Slot, out var playerWeapons) ||
!playerWeapons.TryGetValue(weaponDefIndex, out var weaponInfo) ||
weaponInfo.Stickers.Count <= 0) return;
float wearIncrement = 0.001f;
float currentWear = weaponInfo.Wear;
var playerWear = _temporaryPlayerWeaponWear.GetOrAdd(player.Slot, _ => new ConcurrentDictionary<int, float>());
float incrementedWear = playerWear.AddOrUpdate(
weaponDefIndex,
currentWear + wearIncrement,
(_, oldWear) => Math.Min(oldWear + wearIncrement, 1.0f)
);
weapon.FallbackWear = incrementedWear;
}
private void SetStickers(CCSPlayerController? player, CBasePlayerWeapon weapon)
{
if (player == null || !player.IsValid) return;
int weaponDefIndex = weapon.AttributeManager.Item.ItemDefinitionIndex;
if (!GPlayerWeaponsInfo.TryGetValue(player.Slot, out var playerWeapons) ||
!playerWeapons.TryGetValue(weaponDefIndex, out var weaponInfo))
{ {
var skeleton = GetSkeletonInstance(weapon.CBodyComponent.SceneNode);
int[] newPaints = { 1171, 1170, 1169, 1164, 1162, 1161, 1159, 1175, 1174, 1167, 1165, 1168, 1163, 1160, 1166, 1173 };
if (newPaints.Contains(fallbackPaintKit))
{
skeleton.ModelState.MeshGroupMask = 1;
}
else
{
if (skeleton.ModelState.MeshGroupMask != 2)
{
skeleton.ModelState.MeshGroupMask = 2;
}
}
}
var viewModels1 = GetPlayerViewModels(player);
if (viewModels1 == null || viewModels1.Length == 0)
return; return;
}
foreach (var sticker in weaponInfo.Stickers) var viewModel1 = viewModels1[0];
{ if (viewModel1 == null || viewModel1.Value == null || viewModel1.Value.Weapon == null || viewModel1.Value.Weapon.Value == null)
int stickerSlot = weaponInfo.Stickers.IndexOf(sticker); return;
CAttributeListSetOrAddAttributeValueByName.Invoke(weapon.AttributeManager.Item.NetworkedDynamicAttributes.Handle, Utilities.SetStateChanged(viewModel1.Value, "CBaseEntity", "m_CBodyComponent");
$"sticker slot {stickerSlot} id", ViewAsFloat(sticker.Id));
CAttributeListSetOrAddAttributeValueByName.Invoke(weapon.AttributeManager.Item.NetworkedDynamicAttributes.Handle,
$"sticker slot {stickerSlot} schema", sticker.Schema);
CAttributeListSetOrAddAttributeValueByName.Invoke(weapon.AttributeManager.Item.NetworkedDynamicAttributes.Handle,
$"sticker slot {stickerSlot} offset x", sticker.OffsetX);
CAttributeListSetOrAddAttributeValueByName.Invoke(weapon.AttributeManager.Item.NetworkedDynamicAttributes.Handle,
$"sticker slot {stickerSlot} offset y", sticker.OffsetY);
CAttributeListSetOrAddAttributeValueByName.Invoke(weapon.AttributeManager.Item.NetworkedDynamicAttributes.Handle,
$"sticker slot {stickerSlot} wear", sticker.Wear);
CAttributeListSetOrAddAttributeValueByName.Invoke(weapon.AttributeManager.Item.NetworkedDynamicAttributes.Handle,
$"sticker slot {stickerSlot} scale", sticker.Scale);
CAttributeListSetOrAddAttributeValueByName.Invoke(weapon.AttributeManager.Item.NetworkedDynamicAttributes.Handle,
$"sticker slot {stickerSlot} rotation", sticker.Rotation);
}
if (_temporaryPlayerWeaponWear.TryGetValue(player.Slot, out var playerWear) &&
playerWear.TryGetValue(weaponDefIndex, out float storedWear))
{
weapon.FallbackWear = storedWear;
}
} }
private void SetKeychain(CCSPlayerController? player, CBasePlayerWeapon weapon) internal static void GiveKnifeToPlayer(CCSPlayerController? player)
{
if (player == null || !player.IsValid) return;
int weaponDefIndex = weapon.AttributeManager.Item.ItemDefinitionIndex;
if (!GPlayerWeaponsInfo.TryGetValue(player.Slot, out var playerWeaponsInfo) ||
!playerWeaponsInfo.TryGetValue(weaponDefIndex, out var value) ||
value.KeyChain == null) return;
var keyChain = value.KeyChain;
CAttributeListSetOrAddAttributeValueByName.Invoke(weapon.AttributeManager.Item.NetworkedDynamicAttributes.Handle,
"keychain slot 0 id", ViewAsFloat(keyChain.Id));
CAttributeListSetOrAddAttributeValueByName.Invoke(weapon.AttributeManager.Item.NetworkedDynamicAttributes.Handle,
"keychain slot 0 offset x", keyChain.OffsetX);
CAttributeListSetOrAddAttributeValueByName.Invoke(weapon.AttributeManager.Item.NetworkedDynamicAttributes.Handle,
"keychain slot 0 offset y", keyChain.OffsetY);
CAttributeListSetOrAddAttributeValueByName.Invoke(weapon.AttributeManager.Item.NetworkedDynamicAttributes.Handle,
"keychain slot 0 offset z", keyChain.OffsetZ);
CAttributeListSetOrAddAttributeValueByName.Invoke(weapon.AttributeManager.Item.NetworkedDynamicAttributes.Handle,
"keychain slot 0 seed", keyChain.Seed);
}
private static void GiveKnifeToPlayer(CCSPlayerController? player)
{ {
if (!_config.Additional.KnifeEnabled || player == null || !player.IsValid) return; if (!_config.Additional.KnifeEnabled || player == null || !player.IsValid) return;
if (PlayerHasKnife(player)) return; WeaponPaints.Instance.AddTimer(1.0f, () =>
{
string knifeToGive;
if (g_playersKnife.TryGetValue(player.Slot, out var knife))
{
knifeToGive = knife;
}
else if (_config.Additional.GiveRandomKnife)
{
var knifeTypes = weaponList.Where(pair => pair.Key.StartsWith("weapon_knife") || pair.Key.StartsWith("weapon_bayonet")).ToList();
//string knifeToGive = (CsTeam)player.TeamNum == CsTeam.Terrorist ? "weapon_knife_t" : "weapon_knife"; if (knifeTypes.Count == 0)
player.GiveNamedItem(CsItem.Knife); {
Utility.Log("No valid knife types found.");
return;
}
Random random = new();
int index = random.Next(knifeTypes.Count);
knifeToGive = knifeTypes[index].Key;
}
else
{
knifeToGive = (CsTeam)player.TeamNum == CsTeam.Terrorist ? "weapon_knife_t" : "weapon_knife";
}
player.GiveNamedItem(knifeToGive);
});
} }
private static bool PlayerHasKnife(CCSPlayerController? player) internal static bool PlayerHasKnife(CCSPlayerController? player)
{ {
if (!_config.Additional.KnifeEnabled) return false; if (!_config.Additional.KnifeEnabled) return false;
if (player == null || !player.IsValid || !player.PlayerPawn.IsValid) if (player == null || !player.IsValid || player.PlayerPawn == null || !player.PlayerPawn.IsValid)
{ {
return false; return false;
} }
if (player.PlayerPawn.Value == null || player.PlayerPawn.Value.WeaponServices == null || player.PlayerPawn.Value.ItemServices == null) if (player.PlayerPawn?.Value == null || player.PlayerPawn?.Value.WeaponServices == null || player.PlayerPawn?.Value.ItemServices == null)
return false; return false;
var weapons = player.PlayerPawn.Value.WeaponServices?.MyWeapons; var weapons = player.PlayerPawn.Value.WeaponServices?.MyWeapons;
if (weapons == null) return false; if (weapons == null) return false;
foreach (var weapon in weapons) foreach (var weapon in weapons)
{ {
if (!weapon.IsValid || weapon.Value == null || !weapon.Value.IsValid) continue; if (weapon != null && weapon.IsValid && weapon.Value != null && weapon.Value.IsValid)
if (weapon.Value.DesignerName.Contains("knife") || weapon.Value.DesignerName.Contains("bayonet"))
{ {
return true; if (weapon.Value.DesignerName.Contains("knife") || weapon.Value.DesignerName.Contains("bayonet"))
{
return true;
}
} }
} }
return false; return false;
} }
private void RefreshWeapons(CCSPlayerController? player) internal void RefreshWeapons(CCSPlayerController? player)
{ {
if (!_gBCommandsAllowed) return; if (player == null || !player.IsValid || player.PlayerPawn?.Value == null || (LifeState_t)player.LifeState != LifeState_t.LIFE_ALIVE)
if (player == null || !player.IsValid || player.PlayerPawn.Value == null || (LifeState_t)player.LifeState != LifeState_t.LIFE_ALIVE)
return; return;
if (player.PlayerPawn.Value.WeaponServices == null || player.PlayerPawn.Value.ItemServices == null) if (player.PlayerPawn.Value.WeaponServices == null || player.PlayerPawn.Value.ItemServices == null)
return; return;
var weapons = player.PlayerPawn.Value.WeaponServices.MyWeapons; var weapons = player.PlayerPawn.Value.WeaponServices.MyWeapons;
if (weapons.Count == 0) if (weapons == null || weapons.Count == 0)
return; return;
if (player.Team is CsTeam.None or CsTeam.Spectator) if (player.Team == CsTeam.None || player.Team == CsTeam.Spectator)
return; return;
int playerTeam = player.TeamNum; //Dictionary<string, (int, int)> weaponsWithAmmo = new Dictionary<string, (int, int)>();
Dictionary<string, List<(int, int)>> weaponsWithAmmo = new Dictionary<string, List<(int, int)>>();
Dictionary<string, List<(int, int)>> weaponsWithAmmo = [];
// Iterate through each weapon
foreach (var weapon in weapons) foreach (var weapon in weapons)
{ {
if (!weapon.IsValid || weapon.Value == null || if (weapon == null || !weapon.IsValid || weapon.Value == null ||
!weapon.Value.IsValid || !weapon.Value.DesignerName.Contains("weapon_")) !weapon.Value.IsValid || !weapon.Value.DesignerName.Contains("weapon_"))
continue; continue;
CCSWeaponBaseGun gun = weapon.Value.As<CCSWeaponBaseGun>(); CCSWeaponBaseGun gun = weapon.Value.As<CCSWeaponBaseGun>();
if (weapon.Value.Entity == null) continue; if (weapon.Value.Entity == null) continue;
if (weapon.Value.OwnerEntity == null) continue;
if (!weapon.Value.OwnerEntity.IsValid) continue; if (!weapon.Value.OwnerEntity.IsValid) continue;
if (gun == null) continue;
if (gun.Entity == null) continue; if (gun.Entity == null) continue;
if (!gun.IsValid) continue; if (!gun.IsValid) continue;
if (!gun.VisibleinPVS) continue; if (!gun.VisibleinPVS) continue;
try try
{ {
string? weaponByDefindex = null;
CCSWeaponBaseVData? weaponData = weapon.Value.As<CCSWeaponBase>().VData; CCSWeaponBaseVData? weaponData = weapon.Value.As<CCSWeaponBase>().VData;
if (weaponData == null) continue; if (weaponData == null) continue;
if (weaponData.GearSlot is gear_slot_t.GEAR_SLOT_RIFLE or gear_slot_t.GEAR_SLOT_PISTOL) if (weaponData.GearSlot == gear_slot_t.GEAR_SLOT_RIFLE || weaponData.GearSlot == gear_slot_t.GEAR_SLOT_PISTOL)
{ {
if (!WeaponDefindex.TryGetValue(weapon.Value.AttributeManager.Item.ItemDefinitionIndex, out var weaponByDefindex)) if (!WeaponDefindex.TryGetValue(weapon.Value.AttributeManager.Item.ItemDefinitionIndex, out weaponByDefindex))
continue; continue;
int clip1 = weapon.Value.Clip1; int clip1 = weapon.Value.Clip1;
int reservedAmmo = weapon.Value.ReserveAmmo[0]; int reservedAmmo = weapon.Value.ReserveAmmo[0];
if (!weaponsWithAmmo.TryGetValue(weaponByDefindex, out var value)) if (!weaponsWithAmmo.ContainsKey(weaponByDefindex))
{ {
value = []; weaponsWithAmmo.Add(weaponByDefindex, new List<(int, int)>());
weaponsWithAmmo.Add(weaponByDefindex, value);
} }
value.Add((clip1, reservedAmmo)); weaponsWithAmmo[weaponByDefindex].Add((clip1, reservedAmmo));
if (gun.VData == null) return;
weapon.Value?.AddEntityIOEvent("Kill", weapon.Value, null, "", 0.1f);
} }
if (weaponData.GearSlot == gear_slot_t.GEAR_SLOT_KNIFE) //player.RemoveItemByDesignerName(weapon.Value.DesignerName, false);
{
weapon.Value?.AddEntityIOEvent("Kill", weapon.Value, null, "", 0.1f);
}
} }
catch (Exception ex) catch (Exception ex)
{ {
Logger.LogWarning(ex.Message); Logger.LogWarning(ex.Message);
continue;
} }
} }
AddTimer(0.23f, () => for (int i = 1; i <= 3; i++)
{ {
if (!_gBCommandsAllowed) return; player.ExecuteClientCommand($"slot {i}");
player.ExecuteClientCommand($"slot {i}");
if (!PlayerHasKnife(player))
GiveKnifeToPlayer(player);
foreach (var entry in weaponsWithAmmo) AddTimer(0.1f, () =>
{
var weapon = player.PlayerPawn.Value.WeaponServices.ActiveWeapon.Value;
CCSWeaponBaseGun? gun = weapon?.As<CCSWeaponBaseGun>();
if (gun == null || gun.VData == null) return;
if (gun?.VData?.GearSlot == gear_slot_t.GEAR_SLOT_C4 || gun?.VData?.GearSlot == gear_slot_t.GEAR_SLOT_GRENADES) return;
player.DropActiveWeapon();
AddTimer(0.22f, () =>
{
if (gun != null && gun.IsValid && gun.State == CSWeaponState_t.WEAPON_NOT_CARRIED)
{ {
foreach (var ammo in entry.Value) weapon?.Remove();
{ }
var newWeapon = new CBasePlayerWeapon(player.GiveNamedItem(entry.Key)); });
Server.NextFrame(() => });
}
AddTimer(1.2f, () =>
{
GiveKnifeToPlayer(player);
foreach (var entry in weaponsWithAmmo)
{
foreach (var ammo in entry.Value)
{
var newWeapon = new CBasePlayerWeapon(player.GiveNamedItem(entry.Key));
Server.NextFrame(() =>
{ {
try try
{ {
newWeapon.Clip1 = ammo.Item1; if (newWeapon != null)
newWeapon.ReserveAmmo[0] = ammo.Item2; {
newWeapon.Clip1 = ammo.Item1;
IncrementWearForWeaponWithStickers(player, newWeapon); newWeapon.ReserveAmmo[0] = ammo.Item2;
}
} }
catch (Exception ex) catch (Exception ex)
{ {
Logger.LogWarning("Error setting weapon properties: " + ex.Message); Logger.LogWarning("Error setting weapon properties: " + ex.Message);
} }
}); });
} }
} }
}, TimerFlags.STOP_ON_MAPCHANGE); }, TimerFlags.STOP_ON_MAPCHANGE);
} }
private void GivePlayerGloves(CCSPlayerController player) internal void RefreshKnife(CCSPlayerController? player)
{
if (player == null || !player.IsValid || player.PlayerPawn?.Value == null)
return;
if (player.PlayerPawn.Value.WeaponServices == null)
return;
var weapons = player.PlayerPawn.Value.WeaponServices.MyWeapons;
if (weapons != null && weapons.Count > 0)
{
foreach (var weapon in weapons)
{
if (weapon != null && weapon.IsValid && weapon.Value != null && weapon.Value.IsValid && weapon.Index > 0)
{
try
{
CCSWeaponBaseVData? weaponData = weapon.Value.As<CCSWeaponBase>().VData;
if (weapon.Value.DesignerName.Contains("knife") || weaponData?.GearSlot == gear_slot_t.GEAR_SLOT_KNIFE)
{
player.ExecuteClientCommand("slot 3");
weapon.Value.Remove();
GiveKnifeToPlayer(player);
}
}
catch (Exception ex)
{
Logger.LogWarning($"Cannot remove knife: {ex.Message}");
}
}
}
}
}
private static void RefreshGloves(CCSPlayerController player)
{ {
if (!Utility.IsPlayerValid(player) || (LifeState_t)player.LifeState != LifeState_t.LIFE_ALIVE) return; if (!Utility.IsPlayerValid(player) || (LifeState_t)player.LifeState != LifeState_t.LIFE_ALIVE) return;
CCSPlayerPawn? pawn = player.PlayerPawn.Value; CCSPlayerPawn? pawn = player.PlayerPawn.Value;
if (pawn == null || !pawn.IsValid) if (pawn == null || !pawn.IsValid || pawn.LifeState != (byte)LifeState_t.LIFE_ALIVE)
return; return;
var model = pawn.CBodyComponent?.SceneNode?.GetSkeletonInstance()?.ModelState.ModelName ?? string.Empty; string model = pawn.CBodyComponent?.SceneNode?.GetSkeletonInstance()?.ModelState.ModelName ?? string.Empty;
if (!string.IsNullOrEmpty(model)) if (!string.IsNullOrEmpty(model))
{ {
pawn.SetModel("characters/models/tm_jumpsuit/tm_jumpsuit_varianta.vmdl"); pawn.SetModel("characters/models/tm_jumpsuit/tm_jumpsuit_varianta.vmdl");
pawn.SetModel(model); pawn.SetModel(model);
} }
Instance.AddTimer(0.08f, () => Instance.AddTimer(0.06f, () =>
{ {
CEconItemView item = pawn.EconGloves;
try try
{ {
if (!player.IsValid) if (player == null || !player.IsValid)
return; return;
if (!player.PawnIsAlive) if (pawn == null || !pawn.IsValid || pawn.LifeState != (byte)LifeState_t.LIFE_ALIVE)
return; return;
if (!GPlayersGlove.TryGetValue(player.Slot, out var gloveInfo) || gloveInfo == 0) return; if (g_playersGlove.TryGetValue(player.Slot, out var gloveInfo) && gloveInfo != 0)
{
CCSPlayerPawn? pawn = player.PlayerPawn.Value;
if (pawn == null || !pawn.IsValid || pawn.LifeState != (byte)LifeState_t.LIFE_ALIVE)
return;
WeaponInfo weaponInfo = GPlayerWeaponsInfo[player.Slot][gloveInfo]; WeaponInfo weaponInfo = gPlayerWeaponsInfo[player.Slot][gloveInfo];
item.ItemDefinitionIndex = gloveInfo; CEconItemView item = pawn.EconGloves;
item.ItemIDLow = 16384 & 0xFFFFFFFF; item.ItemDefinitionIndex = gloveInfo;
item.ItemIDHigh = 16384; item.ItemIDLow = 16384 & 0xFFFFFFFF;
item.ItemIDHigh = 16384;
CAttributeListSetOrAddAttributeValueByName.Invoke(item.NetworkedDynamicAttributes.Handle, "set item texture prefab", weaponInfo.Paint); CAttributeList_SetOrAddAttributeValueByName.Invoke(item.NetworkedDynamicAttributes.Handle, "set item texture prefab", weaponInfo.Paint);
CAttributeListSetOrAddAttributeValueByName.Invoke(item.NetworkedDynamicAttributes.Handle, "set item texture seed", weaponInfo.Seed); CAttributeList_SetOrAddAttributeValueByName.Invoke(item.NetworkedDynamicAttributes.Handle, "set item texture seed", weaponInfo.Seed);
CAttributeListSetOrAddAttributeValueByName.Invoke(item.NetworkedDynamicAttributes.Handle, "set item texture wear", weaponInfo.Wear); CAttributeList_SetOrAddAttributeValueByName.Invoke(item.NetworkedDynamicAttributes.Handle, "set item texture wear", weaponInfo.Wear);
item.Initialized = true; item.Initialized = true;
SetBodygroup(pawn.Handle, "default_gloves", 1); CBaseModelEntity_SetBodygroup.Invoke(pawn, "default_gloves", 1);
}
} }
catch (Exception) { } catch (Exception) { }
}, TimerFlags.STOP_ON_MAPCHANGE); }, TimerFlags.STOP_ON_MAPCHANGE);
@@ -377,149 +409,50 @@ namespace WeaponPaints
private static int GetRandomPaint(int defindex) private static int GetRandomPaint(int defindex)
{ {
if (SkinsList.Count == 0) if (skinsList == null || skinsList.Count == 0)
return 0; return 0;
Random rnd = new Random(); Random rnd = new Random();
// Filter weapons by the provided defindex // Filter weapons by the provided defindex
var filteredWeapons = SkinsList.Where(w => w["weapon_defindex"]?.ToString() == defindex.ToString()).ToList(); var filteredWeapons = skinsList.Where(w => w["weapon_defindex"]?.ToString() == defindex.ToString()).ToList();
if (filteredWeapons.Count == 0) if (filteredWeapons.Count == 0)
return 0; return 0;
var randomWeapon = filteredWeapons[rnd.Next(filteredWeapons.Count)]; var randomWeapon = filteredWeapons[rnd.Next(filteredWeapons.Count)];
return int.TryParse(randomWeapon["paint"]?.ToString(), out var paintValue) ? paintValue : 0; if (int.TryParse(randomWeapon["paint"]?.ToString(), out int paintValue))
return paintValue;
return 0;
} }
private static void SubclassChange(CBasePlayerWeapon weapon, ushort itemD) private static CSkeletonInstance GetSkeletonInstance(CGameSceneNode node)
{ {
var subclassChangeFunc = VirtualFunction.Create<nint, string, int>( Func<nint, nint> GetSkeletonInstance = VirtualFunction.Create<nint, nint>(node.Handle, 8);
GameData.GetSignature("ChangeSubclass") return new CSkeletonInstance(GetSkeletonInstance(node.Handle));
);
subclassChangeFunc(weapon.Handle, itemD.ToString());
} }
private static void UpdateWeaponMeshGroupMask(CBaseEntity weapon, bool isLegacy = false) private static unsafe CHandle<CBaseViewModel>[]? GetPlayerViewModels(CCSPlayerController player)
{
if (weapon.CBodyComponent?.SceneNode == null) return;
var skeleton = weapon.CBodyComponent.SceneNode.GetSkeletonInstance();
var value = (ulong)(isLegacy ? 2 : 1);
if (skeleton.ModelState.MeshGroupMask != value)
{
skeleton.ModelState.MeshGroupMask = value;
}
}
private static void UpdatePlayerWeaponMeshGroupMask(CCSPlayerController player, CBasePlayerWeapon weapon, bool isLegacy)
{
UpdateWeaponMeshGroupMask(weapon, isLegacy);
var viewModel = GetPlayerViewModel(player);
if (viewModel == null || viewModel.Weapon.Value == null ||
viewModel.Weapon.Value.Index != weapon.Index) return;
UpdateWeaponMeshGroupMask(viewModel, isLegacy);
Utilities.SetStateChanged(viewModel, "CBaseEntity", "m_CBodyComponent");
}
private static void GivePlayerAgent(CCSPlayerController player)
{
if (!GPlayersAgent.TryGetValue(player.Slot, out var value)) return;
var model = player.TeamNum == 3 ? value.CT : value.T;
if (string.IsNullOrEmpty(model)) return;
if (player.PlayerPawn.Value == null)
return;
try
{
Server.NextFrame(() =>
{
player.PlayerPawn.Value.SetModel(
$"characters/models/{model}.vmdl"
);
});
}
catch (Exception)
{
}
}
private static void GivePlayerMusicKit(CCSPlayerController player)
{
if (!GPlayersMusic.TryGetValue(player.Slot, out var value)) return;
if (player.InventoryServices == null) return;
player.InventoryServices.MusicID = value;
Utilities.SetStateChanged(player, "CCSPlayerController", "m_pInventoryServices");
player.MusicKitID = value;
Utilities.SetStateChanged(player, "CCSPlayerController", "m_iMusicKitID");
}
private static void GivePlayerPin(CCSPlayerController player)
{
if (!GPlayersPin.TryGetValue(player.Slot, out var pin)) return;
if (player.InventoryServices == null) return;
for (var index = 0; index < player.InventoryServices.Rank.Length; index++)
{
player.InventoryServices.Rank[index] = index == 5 ? (MedalRank_t)pin : MedalRank_t.MEDAL_RANK_NONE;
Utilities.SetStateChanged(player, "CCSPlayerController", "m_pInventoryServices");
}
}
private void GiveOnItemPickup(CCSPlayerController player)
{
var pawn = player.PlayerPawn.Value;
if (pawn == null) return;
var myWeapons = pawn.WeaponServices?.MyWeapons;
if (myWeapons == null) return;
foreach (var handle in myWeapons)
{
var weapon = handle.Value;
if (weapon != null && weapon.DesignerName.Contains("knife"))
{
GivePlayerWeaponSkin(player, weapon);
}
}
}
private void UpdatePlayerEconItemId(CEconItemView econItemView)
{
var itemId = _nextItemId++;
econItemView.ItemID = itemId;
econItemView.ItemIDLow = (uint)itemId & 0xFFFFFFFF;
econItemView.ItemIDHigh = (uint)itemId >> 32;
}
private static CCSPlayerController? GetPlayerFromItemServices(CCSPlayer_ItemServices itemServices)
{
var pawn = itemServices.Pawn.Value;
if (!pawn.IsValid || !pawn.Controller.IsValid || pawn.Controller.Value == null) return null;
var player = new CCSPlayerController(pawn.Controller.Value.Handle);
return !Utility.IsPlayerValid(player) ? null : player;
}
private static unsafe CBaseViewModel? GetPlayerViewModel(CCSPlayerController player)
{ {
if (player.PlayerPawn.Value == null || player.PlayerPawn.Value.ViewModelServices == null) return null; if (player.PlayerPawn.Value == null || player.PlayerPawn.Value.ViewModelServices == null) return null;
CCSPlayer_ViewModelServices viewModelServices = new(player.PlayerPawn.Value.ViewModelServices!.Handle); CCSPlayer_ViewModelServices viewModelServices = new CCSPlayer_ViewModelServices(player.PlayerPawn.Value.ViewModelServices!.Handle);
var ptr = viewModelServices.Handle + Schema.GetSchemaOffset("CCSPlayer_ViewModelServices", "m_hViewModel"); return GetFixedArray<CHandle<CBaseViewModel>>(viewModelServices.Handle, "CCSPlayer_ViewModelServices", "m_hViewModel", 3);
var references = MemoryMarshal.CreateSpan(ref ptr, 3);
var viewModel = (CHandle<CBaseViewModel>)Activator.CreateInstance(typeof(CHandle<CBaseViewModel>), references[0])!;
return viewModel.Value == null ? null : viewModel.Value;
} }
private static float ViewAsFloat(uint value) public static unsafe T[] GetFixedArray<T>(nint pointer, string @class, string member, int length) where T : CHandle<CBaseViewModel>
{ {
return BitConverter.Int32BitsToSingle((int)value); nint ptr = pointer + Schema.GetSchemaOffset(@class, member);
Span<nint> references = MemoryMarshal.CreateSpan<nint>(ref ptr, length);
T[] values = new T[length];
for (int i = 0; i < length; i++)
{
values[i] = (T)Activator.CreateInstance(typeof(T), references[i])!;
}
return values;
} }
} }
} }

View File

@@ -1,34 +1,18 @@
namespace WeaponPaints namespace WeaponPaints
{ {
public class WeaponInfo public class WeaponInfo
{ {
public int Paint { get; set; } public int Paint { get; set; }
public int Seed { get; set; } public int Seed { get; set; }
public float Wear { get; set; } public float Wear { get; set; }
public string Nametag { get; set; } = "";
public bool StatTrak { get; set; } = false;
public int StatTrakCount { get; set; }
public KeyChainInfo? KeyChain { get; set; }
public List<StickerInfo> Stickers { get; set; } = new();
}
public class StickerInfo public WeaponInfo() : this(0, 0, 0f) { }
{
public uint Id { get; set; }
public uint Schema { get; set; }
public float OffsetX { get; set; }
public float OffsetY { get; set; }
public float Wear { get; set; }
public float Scale { get; set; }
public float Rotation { get; set; }
}
public class KeyChainInfo public WeaponInfo(int paint, int seed, float wear)
{ {
public uint Id { get; set; } Paint = paint;
public float OffsetX { get; set; } Seed = seed;
public float OffsetY { get; set; } Wear = wear;
public float OffsetZ { get; set; } }
public uint Seed { get; set; } }
}
} }

View File

@@ -1,22 +1,167 @@
using CounterStrikeSharp.API; using CounterStrikeSharp.API;
using CounterStrikeSharp.API.Core; using CounterStrikeSharp.API.Core;
using CounterStrikeSharp.API.Core.Attributes; using CounterStrikeSharp.API.Core.Attributes;
using CounterStrikeSharp.API.Modules.Memory.DynamicFunctions;
using Microsoft.Extensions.Localization;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
using MySqlConnector; using MySqlConnector;
using Newtonsoft.Json.Linq;
using System.Collections.Concurrent;
namespace WeaponPaints; namespace WeaponPaints;
[MinimumApiVersion(276)] [MinimumApiVersion(168)]
public partial class WeaponPaints : BasePlugin, IPluginConfig<WeaponPaintsConfig> public partial class WeaponPaints : BasePlugin, IPluginConfig<WeaponPaintsConfig>
{ {
internal static WeaponPaints Instance { get; private set; } = new(); internal static WeaponPaints Instance { get; private set; } = new();
internal static readonly int[] newPaints = { 1171, 1170, 1169, 1164, 1162, 1161, 1159, 1175, 1174, 1167, 1165, 1168, 1163, 1160, 1166, 1173 };
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_taser", "Zeus x27"},
{"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" },
{ "weapon_knife_kukri", "Kukri Knife" }
};
public WeaponPaintsConfig Config { get; set; } = new(); internal static WeaponPaintsConfig _config = new WeaponPaintsConfig();
private static WeaponPaintsConfig _config { get; set; } = new(); internal static IStringLocalizer? _localizer;
public override string ModuleAuthor => "Nereziel & daffyy"; internal static Dictionary<int, int> g_knifePickupCount = new Dictionary<int, int>();
public override string ModuleDescription => "Skin, gloves, agents and knife selector, standalone and web-based"; internal static ConcurrentDictionary<int, string> g_playersKnife = new ConcurrentDictionary<int, string>();
internal static ConcurrentDictionary<int, ushort> g_playersGlove = new ConcurrentDictionary<int, ushort>();
internal static ConcurrentDictionary<int, ConcurrentDictionary<int, WeaponInfo>> gPlayerWeaponsInfo = new ConcurrentDictionary<int, ConcurrentDictionary<int, WeaponInfo>>();
internal static List<JObject> skinsList = new List<JObject>();
internal static List<JObject> glovesList = new List<JObject>();
internal static WeaponSynchronization? weaponSync;
public static bool g_bCommandsAllowed = true;
internal Dictionary<int, string> PlayerWeaponImage = new();
internal static Dictionary<int, DateTime> commandsCooldown = new Dictionary<int, DateTime>();
internal static Database? _database;
internal static MemoryFunctionVoid<nint, string, float> CAttributeList_SetOrAddAttributeValueByName = new(GameData.GetSignature("CAttributeList_SetOrAddAttributeValueByName"));
internal static MemoryFunctionVoid<CBaseModelEntity, string, UInt64> CBaseModelEntity_SetBodygroup = new(GameData.GetSignature("CBaseModelEntity_SetBodygroup"));
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" },
{ 31, "weapon_taser" },
{ 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" },
{ 526, "weapon_knife_kukri" }
};
public static MemoryFunctionVoid<IntPtr, string, IntPtr, IntPtr, IntPtr, IntPtr, IntPtr, IntPtr> GiveNamedItem2 = new(@"\x55\x48\x89\xE5\x41\x57\x41\x56\x41\x55\x41\x54\x53\x48\x83\xEC\x18\x48\x89\x7D\xC8\x48\x85\xF6\x74");
public WeaponPaintsConfig Config { get; set; } = new();
public override string ModuleAuthor => "Nereziel & daffyy";
public override string ModuleDescription => "Skin, gloves and knife selector, standalone and web-based";
public override string ModuleName => "WeaponPaints"; public override string ModuleName => "WeaponPaints";
public override string ModuleVersion => "2.7a"; public override string ModuleVersion => "1.9b";
public static WeaponPaintsConfig GetWeaponPaintsConfig()
{
return _config;
}
public override void Load(bool hotReload) public override void Load(bool hotReload)
{ {
@@ -26,41 +171,41 @@ public partial class WeaponPaints : BasePlugin, IPluginConfig<WeaponPaintsConfig
{ {
OnMapStart(string.Empty); OnMapStart(string.Empty);
foreach (var player in Enumerable foreach (var player in Utilities.GetPlayers())
.OfType<CCSPlayerController>(Utilities.GetPlayers().TakeWhile(player => WeaponSync != null))
.Where(player => player.IsValid &&
!string.IsNullOrEmpty(player.IpAddress) && player is
{ IsBot: false, Connected: PlayerConnectedState.PlayerConnected }))
{ {
GPlayerWeaponsInfo.TryRemove(player.Slot, out _); if (weaponSync == null || player is null || !player.IsValid || player.SteamID.ToString().Length != 17 || !player.PawnIsAlive || player.IsBot ||
GPlayersKnife.TryRemove(player.Slot, out _); player.IsHLTV || player.Connected != PlayerConnectedState.PlayerConnected)
GPlayersGlove.TryRemove(player.Slot, out _); continue;
GPlayersAgent.TryRemove(player.Slot, out _);
GPlayersPin.TryRemove(player.Slot, out _);
var playerInfo = new PlayerInfo g_knifePickupCount[(int)player.Slot] = 0;
{ gPlayerWeaponsInfo.TryRemove((int)player.Slot, out _);
UserId = player.UserId, g_playersKnife.TryRemove((int)player.Slot, out _);
Slot = player.Slot,
Index = (int)player.Index,
SteamId = player?.SteamID.ToString(),
Name = player?.PlayerName,
IpAddress = player?.IpAddress?.Split(":")[0]
};
_ = Task.Run(async () => PlayerInfo playerInfo = new PlayerInfo(
(int)player.Slot,
player.Slot,
player.UserId,
player?.SteamID.ToString(),
player?.PlayerName,
player?.IpAddress?.Split(":")[0]);
if (Config.Additional.SkinEnabled)
{ {
if (WeaponSync != null) await WeaponSync.GetPlayerData(playerInfo); Task.Run(() => weaponSync.GetWeaponPaintsFromDatabase(playerInfo));
}); }
if (Config.Additional.KnifeEnabled)
{
Task.Run(() => weaponSync.GetKnifeFromDatabase(playerInfo));
}
if (Config.Additional.GloveEnabled)
{
Task.Run(() => weaponSync.GetGloveFromDatabase(playerInfo));
}
} }
AddTimer(2.0f, () => OnAllPluginsLoaded(hotReload));
} }
Utility.LoadSkinsFromFile(ModuleDirectory + $"/data/skins_{_config.SkinsLanguage}.json", Logger); Utility.LoadSkinsFromFile(ModuleDirectory + "/skins.json");
Utility.LoadGlovesFromFile(ModuleDirectory + $"/data/gloves_{_config.SkinsLanguage}.json", Logger); Utility.LoadGlovesFromFile(ModuleDirectory + "/gloves.json");
Utility.LoadAgentsFromFile(ModuleDirectory + $"/data/agents_{_config.SkinsLanguage}.json", Logger);
Utility.LoadMusicFromFile(ModuleDirectory + $"/data/music_{_config.SkinsLanguage}.json", Logger);
if (Config.Additional.KnifeEnabled) if (Config.Additional.KnifeEnabled)
SetupKnifeMenu(); SetupKnifeMenu();
@@ -68,34 +213,27 @@ public partial class WeaponPaints : BasePlugin, IPluginConfig<WeaponPaintsConfig
SetupSkinsMenu(); SetupSkinsMenu();
if (Config.Additional.GloveEnabled) if (Config.Additional.GloveEnabled)
SetupGlovesMenu(); SetupGlovesMenu();
if (Config.Additional.AgentEnabled)
SetupAgentsMenu();
if (Config.Additional.MusicEnabled)
SetupMusicMenu();
RegisterListeners(); RegisterListeners();
RegisterCommands(); RegisterCommands();
} }
public static void PlayerGiveNamedItem(CCSPlayerController player, string item)
{
if (!player.PlayerPawn.IsValid) return;
if (player.PlayerPawn.Value == null) return;
if (!player.PlayerPawn.Value.IsValid) return;
if (player.PlayerPawn.Value.ItemServices == null) return;
public void OnConfigParsed(WeaponPaintsConfig config) GiveNamedItem2.Invoke(player.PlayerPawn.Value.ItemServices.Handle, item, 0, 0, 0, 0, 0, 0);
}
public void OnConfigParsed(WeaponPaintsConfig config)
{ {
Config = config;
_config = config;
if (config.DatabaseHost.Length < 1 || config.DatabaseName.Length < 1 || config.DatabaseUser.Length < 1) if (config.DatabaseHost.Length < 1 || config.DatabaseName.Length < 1 || config.DatabaseUser.Length < 1)
{ {
Logger.LogError("You need to setup Database credentials in \"configs/plugins/WeaponPaints/WeaponPaints.json\"!"); Logger.LogError("You need to setup Database credentials in config!");
Unload(false); throw new Exception("[WeaponPaints] You need to setup Database credentials in config!");
return;
} }
if (!File.Exists(Path.GetDirectoryName(Path.GetDirectoryName(ModuleDirectory)) + "/gamedata/weaponpaints.json"))
{
Logger.LogError("You need to upload \"weaponpaints.json\" to \"gamedata directory\"!");
Unload(false);
return;
}
var builder = new MySqlConnectionStringBuilder var builder = new MySqlConnectionStringBuilder
{ {
Server = config.DatabaseHost, Server = config.DatabaseHost,
@@ -103,13 +241,15 @@ public partial class WeaponPaints : BasePlugin, IPluginConfig<WeaponPaintsConfig
Password = config.DatabasePassword, Password = config.DatabasePassword,
Database = config.DatabaseName, Database = config.DatabaseName,
Port = (uint)config.DatabasePort, Port = (uint)config.DatabasePort,
Pooling = true, Pooling = true
MaximumPoolSize = 640,
}; };
Database = new Database(builder.ConnectionString); _database = new(builder.ConnectionString);
_ = Utility.CheckDatabaseTables(); _ = Utility.CheckDatabaseTables();
Config = config;
_config = config;
_localizer = Localizer; _localizer = Localizer;
Utility.Config = config; Utility.Config = config;
@@ -117,17 +257,8 @@ public partial class WeaponPaints : BasePlugin, IPluginConfig<WeaponPaintsConfig
Task.Run(async () => await Utility.CheckVersion(ModuleVersion, Logger)); Task.Run(async () => await Utility.CheckVersion(ModuleVersion, Logger));
} }
public override void OnAllPluginsLoaded(bool hotReload) public override void Unload(bool hotReload)
{ {
try base.Unload(hotReload);
{
MenuApi = MenuCapability.Get();
}
catch (Exception)
{
MenuApi = null;
Logger.LogError("Error while loading required plugins");
throw;
}
} }
} }

View File

@@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup> <PropertyGroup>
<TargetFramework>net8.0</TargetFramework> <TargetFramework>net7.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings> <ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable> <Nullable>enable</Nullable>
<CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies> <CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies>
@@ -9,23 +9,13 @@
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="CounterStrikeSharp.API" Version="1.0.281" /> <PackageReference Include="CounterStrikeSharp.API" Version="1.0.172" />
<PackageReference Include="Dapper" Version="2.1.35" /> <PackageReference Include="Dapper" Version="2.1.28" />
<PackageReference Include="MySqlConnector" Version="2.4.0-beta.1" /> <PackageReference Include="MySqlConnector" Version="2.3.5" />
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" /> <PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<None Update="lang\**\*.*" CopyToOutputDirectory="PreserveNewest" /> <None Update="lang\**\*.*" CopyToOutputDirectory="PreserveNewest" />
</ItemGroup> </ItemGroup>
<ItemGroup>
<None Update="gamedata\*.*" CopyToOutputDirectory="PreserveNewest" />
</ItemGroup>
<ItemGroup>
<Reference Include="MenuManagerApi">
<HintPath>3rd_party\MenuManagerApi.dll</HintPath>
</Reference>
</ItemGroup>
</Project> </Project>

View File

@@ -1,2 +0,0 @@
<wpf:ResourceDictionary xml:space="preserve" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:s="clr-namespace:System;assembly=mscorlib" xmlns:ss="urn:shemas-jetbrains-com:settings-storage-xaml" xmlns:wpf="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
<s:Boolean x:Key="/Default/AddReferences/RecentPaths/=C_003A_005CUsers_005Cxdaff_005CDocuments_005CGitHub_005Ccs2_002DWeaponPaints_005C3rd_005Fparty_005CMenuManagerApi_002Edll/@EntryIndexedValue">True</s:Boolean></wpf:ResourceDictionary>

View File

@@ -1,8 +1,6 @@
using Dapper; using Dapper;
using MySqlConnector;
using System.Collections.Concurrent; using System.Collections.Concurrent;
namespace WeaponPaints namespace WeaponPaints
{ {
internal class WeaponSynchronization internal class WeaponSynchronization
@@ -16,257 +14,104 @@ namespace WeaponPaints
_config = config; _config = config;
} }
internal async Task GetPlayerData(PlayerInfo? player) internal async Task GetKnifeFromDatabase(PlayerInfo player)
{ {
if (!_config.Additional.KnifeEnabled) return;
try try
{ {
await using var connection = await _database.GetConnectionAsync(); await using var connection = await _database.GetConnectionAsync();
string query = "SELECT `knife` FROM `wp_player_knife` WHERE `steamid` = @steamid";
if (_config.Additional.KnifeEnabled) string? playerKnife = await connection.QueryFirstOrDefaultAsync<string>(query, new { steamid = player.SteamId });
GetKnifeFromDatabase(player, connection);
if (_config.Additional.GloveEnabled)
GetGloveFromDatabase(player, connection);
if (_config.Additional.AgentEnabled)
GetAgentFromDatabase(player, connection);
if (_config.Additional.MusicEnabled)
GetMusicFromDatabase(player, connection);
if (_config.Additional.SkinEnabled)
GetWeaponPaintsFromDatabase(player, connection);
if (_config.Additional.PinsEnabled)
GetPinsFromDatabase(player, connection);
}
catch (Exception ex)
{
// Log the exception or handle it appropriately
Console.WriteLine($"An error occurred: {ex.Message}");
}
}
private void GetKnifeFromDatabase(PlayerInfo? player, MySqlConnection connection)
{
try
{
if (!_config.Additional.KnifeEnabled || string.IsNullOrEmpty(player?.SteamId))
return;
const string query = "SELECT `knife` FROM `wp_player_knife` WHERE `steamid` = @steamid";
var playerKnife = connection.QueryFirstOrDefault<string>(query, new { steamid = player.SteamId });
if (!string.IsNullOrEmpty(playerKnife)) if (!string.IsNullOrEmpty(playerKnife))
{ {
WeaponPaints.GPlayersKnife[player.Slot] = playerKnife; WeaponPaints.g_playersKnife[player.Slot] = playerKnife;
} }
} }
catch (Exception ex) catch (Exception e)
{ {
Utility.Log($"An error occurred in GetKnifeFromDatabase: {ex.Message}"); Utility.Log(e.Message);
return;
} }
} }
private void GetGloveFromDatabase(PlayerInfo? player, MySqlConnection connection) internal async Task GetGloveFromDatabase(PlayerInfo player)
{ {
if (!_config.Additional.GloveEnabled) return;
try try
{ {
if (!_config.Additional.GloveEnabled || string.IsNullOrEmpty(player?.SteamId)) // Ensure proper disposal of resources using "using" statement
return; await using var connection = await _database.GetConnectionAsync();
const string query = "SELECT `weapon_defindex` FROM `wp_player_gloves` WHERE `steamid` = @steamid"; // Construct the SQL query with specific columns for better performance
var gloveData = connection.QueryFirstOrDefault<ushort?>(query, new { steamid = player.SteamId }); string query = "SELECT `weapon_defindex` FROM `wp_player_gloves` WHERE `steamid` = @steamid";
// Execute the query and retrieve glove data
ushort? gloveData = await connection.QueryFirstOrDefaultAsync<ushort?>(query, new { steamid = player.SteamId });
// Check if glove data is retrieved successfully
if (gloveData != null) if (gloveData != null)
{ {
WeaponPaints.GPlayersGlove[player.Slot] = gloveData.Value; // Update g_playersGlove dictionary with glove data
WeaponPaints.g_playersGlove[player.Slot] = gloveData.Value;
} }
} }
catch (Exception ex) catch (Exception e)
{ {
Utility.Log($"An error occurred in GetGloveFromDatabase: {ex.Message}"); // Log any exceptions occurred during database operation
Utility.Log("An error occurred while fetching glove data: " + e.Message);
} }
} }
private void GetAgentFromDatabase(PlayerInfo? player, MySqlConnection connection) internal async Task GetWeaponPaintsFromDatabase(PlayerInfo player)
{ {
if (!_config.Additional.SkinEnabled || player == null || player.SteamId == null) return;
try try
{ {
if (!_config.Additional.AgentEnabled || string.IsNullOrEmpty(player?.SteamId)) await using var connection = await _database.GetConnectionAsync();
return; string query = "SELECT `weapon_defindex`, `weapon_paint_id`, `weapon_wear`, `weapon_seed` FROM `wp_player_skins` WHERE `steamid` = @steamid";
const string query = "SELECT `agent_ct`, `agent_t` FROM `wp_player_agents` WHERE `steamid` = @steamid"; var playerSkins = await connection.QueryAsync<dynamic>(query, new { steamid = player.SteamId });
var agentData = connection.QueryFirstOrDefault<(string, string)>(query, new { steamid = player.SteamId });
if (agentData == default) return; if (playerSkins == null) return;
var agentCT = agentData.Item1;
var agentT = agentData.Item2;
if (!string.IsNullOrEmpty(agentCT) || !string.IsNullOrEmpty(agentT))
{
WeaponPaints.GPlayersAgent[player.Slot] = (
agentCT,
agentT
);
}
}
catch (Exception ex)
{
Utility.Log($"An error occurred in GetAgentFromDatabase: {ex.Message}");
}
}
private void GetWeaponPaintsFromDatabase(PlayerInfo? player, MySqlConnection connection)
{
try
{
if (!_config.Additional.SkinEnabled || player == null || string.IsNullOrEmpty(player.SteamId))
return;
var weaponInfos = new ConcurrentDictionary<int, WeaponInfo>(); var weaponInfos = new ConcurrentDictionary<int, WeaponInfo>();
const string query = "SELECT * FROM `wp_player_skins` WHERE `steamid` = @steamid";
var playerSkins = connection.Query<dynamic>(query, new { steamid = player.SteamId });
foreach (var row in playerSkins) foreach (var row in playerSkins)
{ {
int weaponDefIndex = row?.weapon_defindex ?? 0; int weaponDefIndex = row?.weapon_defindex ?? 0;
int weaponPaintId = row?.weapon_paint_id ?? 0; int weaponPaintId = row?.weapon_paint_id ?? 0;
float weaponWear = row?.weapon_wear ?? 0f; float weaponWear = row?.weapon_wear ?? 0f;
int weaponSeed = row?.weapon_seed ?? 0; int weaponSeed = row?.weapon_seed ?? 0;
string weaponNameTag = row?.weapon_nametag ?? "";
string[]? keyChainParts = row?.weapon_keychain?.ToString().Split(';');
KeyChainInfo keyChainInfo = new KeyChainInfo();
if (keyChainParts!.Length == 5 &&
uint.TryParse(keyChainParts[0], out uint keyChainId) &&
float.TryParse(keyChainParts[1], out float keyChainOffsetX) &&
float.TryParse(keyChainParts[2], out float keyChainOffsetY) &&
float.TryParse(keyChainParts[3], out float keyChainOffsetZ) &&
uint.TryParse(keyChainParts[4], out uint keyChainSeed))
{
// Successfully parsed the values
keyChainInfo.Id = keyChainId;
keyChainInfo.OffsetX = keyChainOffsetX;
keyChainInfo.OffsetY = keyChainOffsetY;
keyChainInfo.OffsetZ = keyChainOffsetZ;
keyChainInfo.Seed = keyChainSeed;
}
else
{
// Failed to parse the values, default to 0
keyChainInfo.Id = 0;
keyChainInfo.OffsetX = 0f;
keyChainInfo.OffsetY = 0f;
keyChainInfo.OffsetZ = 0f;
keyChainInfo.Seed = 0;
}
// Create the WeaponInfo object
WeaponInfo weaponInfo = new WeaponInfo WeaponInfo weaponInfo = new WeaponInfo
{ {
Paint = weaponPaintId, Paint = weaponPaintId,
Seed = weaponSeed, Seed = weaponSeed,
Wear = weaponWear, Wear = weaponWear
Nametag = weaponNameTag,
KeyChain = keyChainInfo
}; };
// Retrieve and parse sticker data (up to 5 slots)
for (int i = 0; i <= 4; i++)
{
// Access the sticker data dynamically using reflection
string stickerColumn = $"weapon_sticker_{i}";
var stickerData = ((IDictionary<string, object>)row!)[stickerColumn]; // Safely cast row to a dictionary
if (string.IsNullOrEmpty(stickerData.ToString())) continue;
var parts = stickerData.ToString()!.Split(';');
//"id;schema;x;y;wear;scale;rotation"
if (parts.Length != 7 ||
!uint.TryParse(parts[0], out uint stickerId) ||
!uint.TryParse(parts[1], out uint stickerSchema) ||
!float.TryParse(parts[2], out float stickerOffsetX) ||
!float.TryParse(parts[3], out float stickerOffsetY) ||
!float.TryParse(parts[4], out float stickerWear) ||
!float.TryParse(parts[5], out float stickerScale) ||
!float.TryParse(parts[6], out float stickerRotation)) continue;
StickerInfo stickerInfo = new StickerInfo
{
Id = stickerId,
Schema = stickerSchema,
OffsetX = stickerOffsetX,
OffsetY = stickerOffsetY,
Wear = stickerWear,
Scale = stickerScale,
Rotation = stickerRotation
};
weaponInfo.Stickers.Add(stickerInfo);
}
weaponInfos[weaponDefIndex] = weaponInfo; weaponInfos[weaponDefIndex] = weaponInfo;
} }
WeaponPaints.GPlayerWeaponsInfo[player.Slot] = weaponInfos; WeaponPaints.gPlayerWeaponsInfo[player.Slot] = weaponInfos;
} }
catch (Exception ex) catch (Exception e)
{ {
Utility.Log($"An error occurred in GetWeaponPaintsFromDatabase: {ex.Message}"); Utility.Log($"Database error occurred: {e.Message}");
}
}
private void GetMusicFromDatabase(PlayerInfo? player, MySqlConnection connection)
{
try
{
if (!_config.Additional.MusicEnabled || string.IsNullOrEmpty(player?.SteamId))
return;
const string query = "SELECT `music_id` FROM `wp_player_music` WHERE `steamid` = @steamid";
var musicData = connection.QueryFirstOrDefault<ushort?>(query, new { steamid = player.SteamId });
if (musicData != null)
{
WeaponPaints.GPlayersMusic[player.Slot] = musicData.Value;
}
}
catch (Exception ex)
{
Utility.Log($"An error occurred in GetMusicFromDatabase: {ex.Message}");
}
}
private void GetPinsFromDatabase(PlayerInfo? player, MySqlConnection connection)
{
try
{
if (string.IsNullOrEmpty(player?.SteamId))
return;
const string query = "SELECT `id` FROM `wp_player_pins` WHERE `steamid` = @steamid";
var pinData = connection.QueryFirstOrDefault<ushort?>(query, new { steamid = player.SteamId });
if (pinData != null)
{
WeaponPaints.GPlayersPin[player.Slot] = pinData.Value;
}
}
catch (Exception ex)
{
Utility.Log($"An error occurred in GetPinsFromDatabase: {ex.Message}");
} }
} }
internal async Task SyncKnifeToDatabase(PlayerInfo player, string knife) internal async Task SyncKnifeToDatabase(PlayerInfo player, string knife)
{ {
if (!_config.Additional.KnifeEnabled || string.IsNullOrEmpty(player.SteamId) || string.IsNullOrEmpty(knife)) return; if (!_config.Additional.KnifeEnabled || player == null || string.IsNullOrEmpty(player.SteamId) || string.IsNullOrEmpty(knife)) return;
const string query = "INSERT INTO `wp_player_knife` (`steamid`, `knife`) VALUES(@steamid, @newKnife) ON DUPLICATE KEY UPDATE `knife` = @newKnife";
try try
{ {
await using var connection = await _database.GetConnectionAsync(); await using var connection = await _database.GetConnectionAsync();
string query = "INSERT INTO `wp_player_knife` (`steamid`, `knife`) VALUES(@steamid, @newKnife) ON DUPLICATE KEY UPDATE `knife` = @newKnife";
await connection.ExecuteAsync(query, new { steamid = player.SteamId, newKnife = knife }); await connection.ExecuteAsync(query, new { steamid = player.SteamId, newKnife = knife });
} }
catch (Exception e) catch (Exception e)
@@ -275,14 +120,15 @@ namespace WeaponPaints
} }
} }
internal async Task SyncGloveToDatabase(PlayerInfo player, int defindex) internal async Task SyncGloveToDatabase(PlayerInfo player, int defindex)
{ {
if (!_config.Additional.GloveEnabled || string.IsNullOrEmpty(player.SteamId)) return; if (!_config.Additional.GloveEnabled || player == null || string.IsNullOrEmpty(player.SteamId)) return;
try try
{ {
await using var connection = await _database.GetConnectionAsync(); await using var connection = await _database.GetConnectionAsync();
const string query = "INSERT INTO `wp_player_gloves` (`steamid`, `weapon_defindex`) VALUES(@steamid, @weapon_defindex) ON DUPLICATE KEY UPDATE `weapon_defindex` = @weapon_defindex"; string query = "INSERT INTO `wp_player_gloves` (`steamid`, `weapon_defindex`) VALUES(@steamid, @weapon_defindex) ON DUPLICATE KEY UPDATE `weapon_defindex` = @weapon_defindex";
await connection.ExecuteAsync(query, new { steamid = player.SteamId, weapon_defindex = defindex }); await connection.ExecuteAsync(query, new { steamid = player.SteamId, weapon_defindex = defindex });
} }
catch (Exception e) catch (Exception e)
@@ -291,129 +137,44 @@ namespace WeaponPaints
} }
} }
internal async Task SyncAgentToDatabase(PlayerInfo player)
{
if (!_config.Additional.AgentEnabled || string.IsNullOrEmpty(player.SteamId)) return;
const string query = """
INSERT INTO `wp_player_agents` (`steamid`, `agent_ct`, `agent_t`)
VALUES(@steamid, @agent_ct, @agent_t)
ON DUPLICATE KEY UPDATE
`agent_ct` = @agent_ct,
`agent_t` = @agent_t
""";
try
{
await using var connection = await _database.GetConnectionAsync();
await connection.ExecuteAsync(query, new { steamid = player.SteamId, agent_ct = WeaponPaints.GPlayersAgent[player.Slot].CT, agent_t = WeaponPaints.GPlayersAgent[player.Slot].T });
}
catch (Exception e)
{
Utility.Log($"Error syncing agents to database: {e.Message}");
}
}
internal async Task SyncWeaponPaintsToDatabase(PlayerInfo player) internal async Task SyncWeaponPaintsToDatabase(PlayerInfo player)
{ {
if (string.IsNullOrEmpty(player.SteamId) || !WeaponPaints.GPlayerWeaponsInfo.TryGetValue(player.Slot, out var weaponsInfo)) if (player == null || string.IsNullOrEmpty(player.SteamId) || !WeaponPaints.gPlayerWeaponsInfo.TryGetValue(player.Slot, out var weaponsInfo))
return; return;
try try
{ {
await using var connection = await _database.GetConnectionAsync(); await using var connection = await _database.GetConnectionAsync();
foreach (var (weaponDefIndex, weaponInfo) in weaponsInfo) foreach (var weaponInfoPair in weaponsInfo)
{ {
var paintId = weaponInfo.Paint; int weaponDefIndex = weaponInfoPair.Key;
var wear = weaponInfo.Wear; WeaponInfo weaponInfo = weaponInfoPair.Value;
var seed = weaponInfo.Seed;
const string queryCheckExistence = "SELECT COUNT(*) FROM `wp_player_skins` WHERE `steamid` = @steamid AND `weapon_defindex` = @weaponDefIndex"; int paintId = weaponInfo.Paint;
float wear = weaponInfo.Wear;
int seed = weaponInfo.Seed;
var existingRecordCount = await connection.ExecuteScalarAsync<int>(queryCheckExistence, new { steamid = player.SteamId, weaponDefIndex = weaponDefIndex }); string query = "INSERT INTO `wp_player_skins` (`steamid`, `weapon_defindex`, `weapon_paint_id`, `weapon_wear`, `weapon_seed`) " +
"VALUES (@steamid, @weaponDefIndex, @paintId, @wear, @seed) " +
"ON DUPLICATE KEY UPDATE `weapon_paint_id` = @paintId, `weapon_wear` = @wear, `weapon_seed` = @seed";
string query; var parameters = new DynamicParameters();
object parameters; parameters.Add("@steamid", player.SteamId);
parameters.Add("@weaponDefIndex", weaponDefIndex);
parameters.Add("@paintId", paintId);
parameters.Add("@wear", wear);
parameters.Add("@seed", seed);
if (existingRecordCount > 0) await connection.ExecuteAsync(query, parameters);
{ }
query = "UPDATE `wp_player_skins` SET `weapon_paint_id` = @paintId, `weapon_wear` = @wear, `weapon_seed` = @seed WHERE `steamid` = @steamid AND `weapon_defindex` = @weaponDefIndex";
parameters = new { steamid = player.SteamId, weaponDefIndex = weaponDefIndex, paintId, wear, seed };
}
else
{
query = "INSERT INTO `wp_player_skins` (`steamid`, `weapon_defindex`, `weapon_paint_id`, `weapon_wear`, `weapon_seed`) " +
"VALUES (@steamid, @weaponDefIndex, @paintId, @wear, @seed)";
parameters = new { steamid = player.SteamId, weaponDefIndex = weaponDefIndex, paintId, wear, seed };
}
await connection.ExecuteAsync(query, parameters); }
} catch (Exception e)
}
catch (Exception e)
{ {
Utility.Log($"Error syncing weapon paints to database: {e.Message}"); Utility.Log($"Error syncing weapon paints to database: {e.Message}");
} }
} }
internal async Task SyncMusicToDatabase(PlayerInfo player, ushort music)
{
if (!_config.Additional.MusicEnabled || string.IsNullOrEmpty(player.SteamId)) return;
try
{
await using var connection = await _database.GetConnectionAsync();
const string query = "INSERT INTO `wp_player_music` (`steamid`, `music_id`) VALUES(@steamid, @newMusic) ON DUPLICATE KEY UPDATE `music_id` = @newMusic";
await connection.ExecuteAsync(query, new { steamid = player.SteamId, newMusic = music });
}
catch (Exception e)
{
Utility.Log($"Error syncing music kit to database: {e.Message}");
}
}
internal async Task SyncStatTrakToDatabase(PlayerInfo player, ConcurrentDictionary<int,WeaponInfo> weaponInfos)
{
if (WeaponPaints.WeaponSync == null || weaponInfos.IsEmpty) return;
var statTrakWeapons = weaponInfos
.Where(w => w.Value is { StatTrak: true, StatTrakCount: > 0 })
.ToDictionary(w => w.Key, w => w.Value.StatTrakCount);
if (statTrakWeapons.Count == 0) return;
if (string.IsNullOrEmpty(player.SteamId))
return;
try
{
await using var connection = await _database.GetConnectionAsync();
await using var transaction = await connection.BeginTransactionAsync();
foreach (var (defindex, statTrakCount) in statTrakWeapons)
{
const string query = @"
INSERT INTO `wp_player_skins` (`steamid`, `weapon_defindex`, `weapon_stattrak_count`)
VALUES (@steamid, @weaponDefIndex, @StatTrakCount)
ON DUPLICATE KEY UPDATE `weapon_stattrak_count` = @StatTrakCount";
var parameters = new
{
steamid = player.SteamId,
weaponDefIndex = defindex,
StatTrakCount = statTrakCount
};
await connection.ExecuteAsync(query, parameters, transaction);
}
await transaction.CommitAsync();
}
catch (Exception e)
{
Utility.Log($"Error syncing stattrak to database: {e.Message}");
}
}
} }
} }

16
gamedata/gloves.json Normal file
View File

@@ -0,0 +1,16 @@
{
"CAttributeList_SetOrAddAttributeValueByName": {
"signatures": {
"library": "server",
"windows": "\\x40\\x53\\x41\\x56\\x41\\x57\\x48\\x81\\xEC\\x90\\x00\\x00\\x00\\x0F\\x29\\x74\\x24\\x70",
"linux": "\\x55\\x48\\x89\\xE5\\x41\\x57\\x41\\x56\\x49\\x89\\xFE\\x41\\x55\\x41\\x54\\x49\\x89\\xF4\\x53\\x48\\x83\\xEC\\x78"
}
},
"CBaseModelEntity_SetBodygroup": {
"signatures": {
"library": "server",
"windows": "\\x48\\x89\\x5C\\x24\\x08\\x48\\x89\\x74\\x24\\x10\\x57\\x48\\x83\\xEC\\x20\\x41\\x8B\\xF8\\x48\\x8B\\xF2\\x48\\x8B\\xD9\\xE8\\x2A\\x2A\\x2A\\x2A",
"linux": "\\x55\\x48\\x89\\xE5\\x41\\x56\\x49\\x89\\xF6\\x41\\x55\\x41\\x89\\xD5\\x41\\x54\\x49\\x89\\xFC\\x48\\x83\\xEC\\x08"
}
}
}

View File

@@ -1,23 +0,0 @@
{
"ChangeSubclass": {
"signatures": {
"library": "server",
"windows": "48 89 6C 24 ? 56 48 83 EC ? 48 8B EA 48 8B F1 E8 ? ? ? ? 84 C0 0F 84",
"linux": "55 48 89 E5 41 57 41 56 41 55 49 89 F5 41 54 49 89 FC 53 48 81 EC A8 00 00 00"
}
},
"CAttributeList_SetOrAddAttributeValueByName": {
"signatures": {
"library": "server",
"windows": "40 53 41 56 41 57 48 81 EC 90 00 00 00 0F 29 74 24 70",
"linux": "55 48 89 E5 41 57 41 56 49 89 FE 41 55 41 54 49 89 F4 53 48 83 EC 78"
}
},
"CBaseModelEntity_SetBodygroup": {
"signatures": {
"library": "server",
"windows": "48 89 5C 24 08 48 89 74 24 10 57 48 83 EC 20 41 8B F8 48 8B F2 48 8B D9 E8 ? ? ? ?",
"linux": "55 48 89 E5 41 56 49 89 F6 41 55 41 89 D5 41 54 49 89 FC 48 83 EC 08"
}
}
}

View File

@@ -4,8 +4,6 @@
"wp_info_refresh": "Type {lime}!wp{default} to synchronize chosen skins", "wp_info_refresh": "Type {lime}!wp{default} to synchronize chosen skins",
"wp_info_knife": "Type {lime}!knife{default} to open knife menu", "wp_info_knife": "Type {lime}!knife{default} to open knife menu",
"wp_info_glove": "Type {lime}!gloves{default} to open gloves menu", "wp_info_glove": "Type {lime}!gloves{default} to open gloves menu",
"wp_info_agent": "Type {lime}!agents{default} to open agents menu",
"wp_info_music": "Type {lime}!music{default} to open music menu",
"wp_command_cooldown": "{lightred}You can't refresh weapon paints right now", "wp_command_cooldown": "{lightred}You can't refresh weapon paints right now",
"wp_command_refresh_done": "{lime}Refreshing weapon paints", "wp_command_refresh_done": "{lime}Refreshing weapon paints",
"wp_knife_menu_select": "You have chosen {lime}{0}{default} as your knife", "wp_knife_menu_select": "You have chosen {lime}{0}{default} as your knife",
@@ -13,13 +11,7 @@
"wp_knife_menu_title": "Knife Menu", "wp_knife_menu_title": "Knife Menu",
"wp_glove_menu_select": "You have chosen {lime}{0}{default} as your glove", "wp_glove_menu_select": "You have chosen {lime}{0}{default} as your glove",
"wp_glove_menu_title": "Gloves Menu", "wp_glove_menu_title": "Gloves Menu",
"wp_agent_menu_select": "You have chosen {lime}{0}{default} as your agent",
"wp_agent_menu_title": "Agents Menu",
"wp_music_menu_title": "Music Menu",
"wp_music_menu_select": "You have chosen {lime}{0}{default} as your music kit",
"wp_skin_menu_weapon_title": "Weapon Menu", "wp_skin_menu_weapon_title": "Weapon Menu",
"wp_skin_menu_skin_title": "Select skin for {lime}{0}{default}", "wp_skin_menu_skin_title": "Select skin for {lime}{0}{default}",
"wp_skin_menu_select": "You have chosen {lime}{0}{default} as your skin", "wp_skin_menu_select": "You have chosen {lime}{0}{default} as your skin"
"None": "None"
} }

View File

@@ -1,25 +1,17 @@
{ {
"wp_prefix": "{lightblue}[WeaponPaints] {default}", "wp_prefix": "{lightblue}[WeaponPaints] {default}",
"wp_info_website": "Apmeklē {lime}{0}{default}, kur varat mainīt ādas", "wp_info_website": "Apmeklējiet {lime}{0}{default}, kur varat mainīt ādas",
"wp_info_refresh": "Ievadiet {lime}!wp{default}, lai sinhronizētu izvēlētās ādas", "wp_info_refresh": "Ievadiet {lime}!wp{default}, lai sinhronizētu izvēlētās ādas",
"wp_info_knife": "Ievadiet {lime}!knife{default}, lai atvērtu nazis izvēlni", "wp_info_knife": "Ievadiet {lime}!knife{default}, lai atvērtu nazis izvēlni",
"wp_info_glove": "Ievadiet {lime}!gloves{default}, lai atvērtu cimdi izvēlni", "wp_info_glove": "Ievadiet {lime}!gloves{default}, lai atvērtu cimdi izvēlni",
"wp_info_agent": "Ievadiet {lime}!agents{default}, lai atvērtu aģentu izvēlni", "wp_command_cooldown": "{lightred}Šobrīd jūs nevarat atjaunot ieroču ādas",
"wp_info_music": "Ievadiet {lime}!music{default}, lai atvērtu mūzikas izvēlni", "wp_command_refresh_done": "{lime}Atjauno ieroču ādas",
"wp_command_cooldown": "{lightred}Šobrīd nevarat atsvaidzināt ieroča krāsas",
"wp_command_refresh_done": "{lime}Atsvaidzinot ieroča krāsas",
"wp_knife_menu_select": "Jūs esat izvēlējies {lime}{0}{default} kā savu nazi", "wp_knife_menu_select": "Jūs esat izvēlējies {lime}{0}{default} kā savu nazi",
"wp_knife_menu_kill": "", "wp_knife_menu_kill": "",
"wp_knife_menu_title": "Nazi Izvēlne", "wp_knife_menu_title": "Nazis Izvēlne",
"wp_glove_menu_select": "Jūs esat izvēlējies {lime}{0}{default} kā savu cimdu", "wp_glove_menu_select": "Jūs esat izvēlējies {lime}{0}{default} kā savus cimdus",
"wp_glove_menu_title": "Cimdu Izvēlne", "wp_glove_menu_title": "Cimdu Izvēlne",
"wp_agent_menu_select": "Jūs esat izvēlējies {lime}{0}{default} kā savu aģentu",
"wp_agent_menu_title": "Aģentu Izvēlne",
"wp_music_menu_title": "Mūzikas Izvēlne",
"wp_music_menu_select": "Jūs esat izvēlējies {lime}{0}{default} kā savu mūzikas komplektu",
"wp_skin_menu_weapon_title": "Ieroču Izvēlne", "wp_skin_menu_weapon_title": "Ieroču Izvēlne",
"wp_skin_menu_skin_title": "Izvēlieties ādu priekš {lime}{0}{default}", "wp_skin_menu_skin_title": "Izvēlieties ādu {lime}{0}{default}",
"wp_skin_menu_select": "Jūs esat izvēlējies {lime}{0}{default} kā savu ādu", "wp_skin_menu_select": "Jūs esat izvēlējies {lime}{0}{default} kā savu ādu"
}
"None": "Nav"
}

View File

@@ -3,23 +3,15 @@
"wp_info_website": "Odwiedź {lime}{0}{default}, gdzie możesz zmieniać skórki", "wp_info_website": "Odwiedź {lime}{0}{default}, gdzie możesz zmieniać skórki",
"wp_info_refresh": "Wpisz {lime}!wp{default}, aby zsynchronizować wybrane skórki", "wp_info_refresh": "Wpisz {lime}!wp{default}, aby zsynchronizować wybrane skórki",
"wp_info_knife": "Wpisz {lime}!knife{default}, aby otworzyć menu noży", "wp_info_knife": "Wpisz {lime}!knife{default}, aby otworzyć menu noży",
"wp_info_glove": "Wpisz {lime}!gloves{default}, aby otworzyć menu rękawic", "wp_info_glove": "Wpisz {lime}!gloves{default}, aby otworzyć menu rękawiczek",
"wp_info_agent": "Wpisz {lime}!agents{default}, aby otworzyć menu agentów", "wp_command_cooldown": "{lightred}Nie możesz teraz odświeżyć skórek broni",
"wp_info_music": "Wpisz {lime}!music{default}, aby otworzyć menu muzyczne", "wp_command_refresh_done": "{lime}Odświeżanie skórek broni",
"wp_command_cooldown": "{lightred}Nie możesz teraz odświeżyć kolorów broni",
"wp_command_refresh_done": "{lime}Odświeżanie kolorów broni",
"wp_knife_menu_select": "Wybrałeś {lime}{0}{default} jako swój nóż", "wp_knife_menu_select": "Wybrałeś {lime}{0}{default} jako swój nóż",
"wp_knife_menu_kill": "", "wp_knife_menu_kill": "{lime}Wybrane skiny będą ustawione dopiero po ponownym wejściu na serwer lub wpisaniu komendy {orange}!kill",
"wp_knife_menu_title": "Menu Noży", "wp_knife_menu_title": "Menu Noży",
"wp_glove_menu_select": "Wybrałeś {lime}{0}{default} jako swoją rękawiczkę", "wp_glove_menu_select": "Wybrałeś {lime}{0}{default} jako swoje rękawiczki",
"wp_glove_menu_title": "Menu Rękawiczek", "wp_glove_menu_title": "Menu Rękawiczek",
"wp_agent_menu_select": "Wybrałeś {lime}{0}{default} jako swojego agenta",
"wp_agent_menu_title": "Menu Agentów",
"wp_music_menu_title": "Menu Muzyczne",
"wp_music_menu_select": "Wybrałeś {lime}{0}{default} jako swój zestaw muzyczny",
"wp_skin_menu_weapon_title": "Menu Broni", "wp_skin_menu_weapon_title": "Menu Broni",
"wp_skin_menu_skin_title": "Wybierz skórkę dla {lime}{0}{default}", "wp_skin_menu_skin_title": "Wybierz skórkę dla {lime}{0}{default}",
"wp_skin_menu_select": "Wybrałeś {lime}{0}{default} jako swoją skórkę", "wp_skin_menu_select": "Wybrałeś {lime}{0}{default} jako swoją skórkę"
}
"None": "Brak"
}

View File

@@ -1,25 +1,17 @@
{ {
"wp_prefix": "{lightblue}[WeaponPaints] {default}", "wp_prefix": "{lightblue}[WeaponPaints] {default}",
"wp_info_website": "Visite {lime}{0}{default}, onde você pode alterar as skins", "wp_info_website": "Visite {lime}{0}{default}, onde você pode alterar skins",
"wp_info_refresh": "Digite {lime}!wp{default} para sincronizar as skins escolhidas", "wp_info_refresh": "Digite {lime}!wp{default} para sincronizar as skins selecionadas",
"wp_info_knife": "Digite {lime}!knife{default} para abrir o menu de facas", "wp_info_knife": "Digite {lime}!knife{default} para abrir o menu de facas",
"wp_info_glove": "Digite {lime}!gloves{default} para abrir o menu de luvas", "wp_info_glove": "Digite {lime}!gloves{default} para abrir o menu de luvas",
"wp_info_agent": "Digite {lime}!agents{default} para abrir o menu de agentes", "wp_command_cooldown": "{lightred}Você não pode atualizar as skins de arma agora",
"wp_info_music": "Digite {lime}!music{default} para abrir o menu de música", "wp_command_refresh_done": "{lime}Atualizando as skins de arma",
"wp_command_cooldown": "{lightred}Você não pode atualizar as skins de armas agora",
"wp_command_refresh_done": "{lime}Atualizando as skins de armas",
"wp_knife_menu_select": "Você escolheu {lime}{0}{default} como sua faca", "wp_knife_menu_select": "Você escolheu {lime}{0}{default} como sua faca",
"wp_knife_menu_kill": "", "wp_knife_menu_kill": "",
"wp_knife_menu_title": "Menu de Facas", "wp_knife_menu_title": "Menu de Facas",
"wp_glove_menu_select": "Você escolheu {lime}{0}{default} como sua luva", "wp_glove_menu_select": "Você escolheu {lime}{0}{default} como suas luvas",
"wp_glove_menu_title": "Menu de Luvas", "wp_glove_menu_title": "Menu de Luvas",
"wp_agent_menu_select": "Você escolheu {lime}{0}{default} como seu agente",
"wp_agent_menu_title": "Menu de Agentes",
"wp_music_menu_title": "Menu de Música",
"wp_music_menu_select": "Você escolheu {lime}{0}{default} como seu kit de música",
"wp_skin_menu_weapon_title": "Menu de Armas", "wp_skin_menu_weapon_title": "Menu de Armas",
"wp_skin_menu_skin_title": "Selecione a skin para {lime}{0}{default}", "wp_skin_menu_skin_title": "Selecione uma skin para {lime}{0}{default}",
"wp_skin_menu_select": "Você escolheu {lime}{0}{default} como sua skin", "wp_skin_menu_select": "Você escolheu {lime}{0}{default} como sua skin"
}
"None": "Nenhum"
}

View File

@@ -1,25 +1,17 @@
{ {
"wp_prefix": "{lightblue}[WeaponPaints] {default}", "wp_prefix": "{lightblue}[WeaponPaints] {default}",
"wp_info_website": "Visite {lime}{0}{default}, onde pode alterar as skins", "wp_info_website": "Visite {lime}{0}{default}, onde pode alterar skins",
"wp_info_refresh": "Digite {lime}!wp{default} para sincronizar as skins escolhidas", "wp_info_refresh": "Digite {lime}!wp{default} para sincronizar as skins selecionadas",
"wp_info_knife": "Digite {lime}!knife{default} para abrir o menu de facas", "wp_info_knife": "Digite {lime}!knife{default} para abrir o menu de facas",
"wp_info_glove": "Digite {lime}!gloves{default} para abrir o menu de luvas", "wp_info_glove": "Digite {lime}!gloves{default} para abrir o menu de luvas",
"wp_info_agent": "Digite {lime}!agents{default} para abrir o menu de agentes", "wp_command_cooldown": "{lightred}Você não pode atualizar as skins de arma agora",
"wp_info_music": "Digite {lime}!music{default} para abrir o menu de música", "wp_command_refresh_done": "{lime}Atualizando as skins de arma",
"wp_command_cooldown": "{lightred}Não pode atualizar as skins de armas de momento", "wp_knife_menu_select": "Você escolheu {lime}{0}{default} como sua faca",
"wp_command_refresh_done": "{lime}Atualizando as skins de armas",
"wp_knife_menu_select": "Escolheu {lime}{0}{default} como a sua faca",
"wp_knife_menu_kill": "", "wp_knife_menu_kill": "",
"wp_knife_menu_title": "Menu de Facas", "wp_knife_menu_title": "Menu de Facas",
"wp_glove_menu_select": "Escolheu {lime}{0}{default} como a sua luva", "wp_glove_menu_select": "Você escolheu {lime}{0}{default} como suas luvas",
"wp_glove_menu_title": "Menu de Luvas", "wp_glove_menu_title": "Menu de Luvas",
"wp_agent_menu_select": "Escolheu {lime}{0}{default} como o seu agente",
"wp_agent_menu_title": "Menu de Agentes",
"wp_music_menu_title": "Menu de Música",
"wp_music_menu_select": "Escolheu {lime}{0}{default} como o seu kit de música",
"wp_skin_menu_weapon_title": "Menu de Armas", "wp_skin_menu_weapon_title": "Menu de Armas",
"wp_skin_menu_skin_title": "Selecione a skin para {lime}{0}{default}", "wp_skin_menu_skin_title": "Selecione uma skin para {lime}{0}{default}",
"wp_skin_menu_select": "Escolheu {lime}{0}{default} como a sua skin", "wp_skin_menu_select": "Você escolheu {lime}{0}{default} como sua skin"
}
"None": "Nenhum"
}

View File

@@ -4,22 +4,14 @@
"wp_info_refresh": "Введите {lime}!wp{default}, чтобы синхронизировать выбранные скины", "wp_info_refresh": "Введите {lime}!wp{default}, чтобы синхронизировать выбранные скины",
"wp_info_knife": "Введите {lime}!knife{default}, чтобы открыть меню ножей", "wp_info_knife": "Введите {lime}!knife{default}, чтобы открыть меню ножей",
"wp_info_glove": "Введите {lime}!gloves{default}, чтобы открыть меню перчаток", "wp_info_glove": "Введите {lime}!gloves{default}, чтобы открыть меню перчаток",
"wp_info_agent": "Введите {lime}!agents{default}, чтобы открыть меню агентов", "wp_command_cooldown": "{lightred}Вы не можете обновить скины оружия сейчас",
"wp_info_music": "Введите {lime}!music{default}, чтобы открыть меню музыки", "wp_command_refresh_done": "{lime}Обновление скинов оружия",
"wp_command_cooldown": "{lightred}Вы не можете обновить раскраску оружия сейчас",
"wp_command_refresh_done": "{lime}Обновление раскраски оружия",
"wp_knife_menu_select": "Вы выбрали {lime}{0}{default} в качестве вашего ножа", "wp_knife_menu_select": "Вы выбрали {lime}{0}{default} в качестве вашего ножа",
"wp_knife_menu_kill": "", "wp_knife_menu_kill": "",
"wp_knife_menu_title": "Меню Ножей", "wp_knife_menu_title": "Меню Ножей",
"wp_glove_menu_select": "Вы выбрали {lime}{0}{default} в качестве ваших перчаток", "wp_glove_menu_select": "Вы выбрали {lime}{0}{default} в качестве ваших перчаток",
"wp_glove_menu_title": "Меню Перчаток", "wp_glove_menu_title": "Меню Перчаток",
"wp_agent_menu_select": "Вы выбрали {lime}{0}{default} в качестве вашего агента",
"wp_agent_menu_title": "Меню Агентов",
"wp_music_menu_title": "Меню Музыки",
"wp_music_menu_select": "Вы выбрали {lime}{0}{default} в качестве вашего музыкального набора",
"wp_skin_menu_weapon_title": "Меню Оружия", "wp_skin_menu_weapon_title": "Меню Оружия",
"wp_skin_menu_skin_title": "Выберите скин для {lime}{0}{default}", "wp_skin_menu_skin_title": "Выберите скин для {lime}{0}{default}",
"wp_skin_menu_select": "Вы выбрали {lime}{0}{default} в качестве вашего скина", "wp_skin_menu_select": "Вы выбрали {lime}{0}{default} в качестве вашего скина"
"None": "Нет"
} }

View File

@@ -1,25 +1,17 @@
{ {
"wp_prefix": "{lightblue}[WeaponPaints] {default}", "wp_prefix": "{lightblue}[WeaponPaints] {default}",
"wp_info_website": "Ziyaret edin {lime}{0}{default}, nerede derileri değiştirebilirsiniz", "wp_info_website": "Ziyaret edin {lime}{0}{default}, burada skinleri değiştirebilirsiniz",
"wp_info_refresh": "Senkronize etmek için {lime}!wp{default} yazın seçilen deriler", "wp_info_refresh": "Senkronize edilen skinleri görmek için {lime}!wp{default} yazın",
"wp_info_knife": "Bıçak menüsünü açmak için {lime}!knife{default} yazın", "wp_info_knife": "Bıçak menüsünü açmak için {lime}!knife{default} yazın",
"wp_info_glove": "Handskar menüsünü açmak için {lime}!gloves{default} yazın", "wp_info_glove": "Eldiven menüsünü açmak için {lime}!gloves{default} yazın",
"wp_info_agent": "Ajan menüsünü açmak için {lime}!agents{default} yazın", "wp_command_cooldown": "{lightred}Şu anda silah skinlerini yenileyemezsiniz",
"wp_info_music": "Müzik menüsünü açmak için {lime}!music{default} yazın", "wp_command_refresh_done": "{lime}Silah skinleri yenileniyor",
"wp_command_cooldown": "{lightred}Şu anda silah boyalarını yenileyemezsiniz",
"wp_command_refresh_done": "{lime}Silah boyaları yenileniyor",
"wp_knife_menu_select": "{lime}{0}{default} olarak bıçağınızı seçtiniz", "wp_knife_menu_select": "{lime}{0}{default} olarak bıçağınızı seçtiniz",
"wp_knife_menu_kill": "", "wp_knife_menu_kill": "",
"wp_knife_menu_title": "Bıçak Menüsü", "wp_knife_menu_title": "Bıçak Menüsü",
"wp_glove_menu_select": "{lime}{0}{default} olarak eldiveninizi seçtiniz", "wp_glove_menu_select": "{lime}{0}{default} olarak eldiveninizi seçtiniz",
"wp_glove_menu_title": "Eldiven Menüsü", "wp_glove_menu_title": "Eldiven Menüsü",
"wp_agent_menu_select": "{lime}{0}{default} olarak ajanınızı seçtiniz",
"wp_agent_menu_title": "Ajanlar Menüsü",
"wp_music_menu_title": "Müzik Menüsü",
"wp_music_menu_select": "{lime}{0}{default} olarak müzik setinizi seçtiniz",
"wp_skin_menu_weapon_title": "Silah Menüsü", "wp_skin_menu_weapon_title": "Silah Menüsü",
"wp_skin_menu_skin_title": "{lime}{0}{default} için cilt seçin", "wp_skin_menu_skin_title": "{lime}{0}{default} için bir skin seçin",
"wp_skin_menu_select": "{lime}{0}{default} olarak cildinizi seçtiniz", "wp_skin_menu_select": "{lime}{0}{default} olarak bir skin seçtiniz"
}
"None": "Hiçbiri"
}

View File

@@ -4,22 +4,14 @@
"wp_info_refresh": "Введіть {lime}!wp{default}, щоб синхронізувати обрані шкури", "wp_info_refresh": "Введіть {lime}!wp{default}, щоб синхронізувати обрані шкури",
"wp_info_knife": "Введіть {lime}!knife{default}, щоб відкрити меню ножів", "wp_info_knife": "Введіть {lime}!knife{default}, щоб відкрити меню ножів",
"wp_info_glove": "Введіть {lime}!gloves{default}, щоб відкрити меню рукавичок", "wp_info_glove": "Введіть {lime}!gloves{default}, щоб відкрити меню рукавичок",
"wp_info_agent": "Введіть {lime}!agents{default}, щоб відкрити меню агентів", "wp_command_cooldown": "{lightred}Наразі ви не можете оновлювати шкіри зброї",
"wp_info_music": "Введіть {lime}!music{default}, щоб відкрити меню музики", "wp_command_refresh_done": "{lime}Оновлення шкірок зброї",
"wp_command_cooldown": "{lightred}Ви не можете оновити фарби зброї зараз", "wp_knife_menu_select": "Ви вибрали {lime}{0}{default} як ваш ніж",
"wp_command_refresh_done": "{lime}Оновлення фарби зброї",
"wp_knife_menu_select": "Ви обрали {lime}{0}{default} як свій ніж",
"wp_knife_menu_kill": "", "wp_knife_menu_kill": "",
"wp_knife_menu_title": "Меню Ножів", "wp_knife_menu_title": "Меню Ножів",
"wp_glove_menu_select": "Ви обрали {lime}{0}{default} як свої рукавички", "wp_glove_menu_select": "Ви вибрали {lime}{0}{default} як ваші рукавички",
"wp_glove_menu_title": "Меню Рукавичок", "wp_glove_menu_title": "Меню Рукавичок",
"wp_agent_menu_select": "Ви обрали {lime}{0}{default} як свого агента",
"wp_agent_menu_title": "Меню Агентів",
"wp_music_menu_title": "Меню Музики",
"wp_music_menu_select": "Ви обрали {lime}{0}{default} як свій набір музики",
"wp_skin_menu_weapon_title": "Меню Зброї", "wp_skin_menu_weapon_title": "Меню Зброї",
"wp_skin_menu_skin_title": "Виберіть шкіру для {lime}{0}{default}", "wp_skin_menu_skin_title": "Виберіть шкіру для {lime}{0}{default}",
"wp_skin_menu_select": "Ви обрали {lime}{0}{default} як свою шкіру", "wp_skin_menu_select": "Ви вибрали {lime}{0}{default} як вашу шкіру"
"None": "Немає"
} }

View File

@@ -1,25 +1,17 @@
{ {
"wp_prefix": "{lightblue}[WeaponPaints] {default}", "wp_prefix": "{lightblue}[WeaponPaints] {default}",
"wp_info_website": "访问 {lime}{0}{default},您可以更改皮肤", "wp_info_website": "访问 {lime}{0}{default},您可以更改皮肤",
"wp_info_refresh": "输入 {lime}!wp{default} 同步选择的皮肤", "wp_info_refresh": "输入 {lime}!wp{default} 同步选择的皮肤",
"wp_info_knife": "输入 {lime}!knife{default} 打开刀具菜单", "wp_info_knife": "输入 {lime}!knife{default} 打开刀具菜单",
"wp_info_glove": "输入 {lime}!gloves{default} 打开手套菜单", "wp_info_glove": "输入 {lime}!gloves{default} 打开手套菜单",
"wp_info_agent": "输入 {lime}!agents{default} 打开代理菜单", "wp_command_cooldown": "{lightred}您现在无法刷新武器皮肤",
"wp_info_music": "输入 {lime}!music{default} 打开音乐菜单", "wp_command_refresh_done": "{lime}刷新武器皮肤",
"wp_command_cooldown": "{lightred}您现在无法刷新武器涂装", "wp_knife_menu_select": "您已选择 {lime}{0}{default} 作为您的刀具",
"wp_command_refresh_done": "{lime}正在刷新武器涂装",
"wp_knife_menu_select": "您选择了 {lime}{0}{default} 作为您的刀具",
"wp_knife_menu_kill": "", "wp_knife_menu_kill": "",
"wp_knife_menu_title": "刀具菜单", "wp_knife_menu_title": "刀具菜单",
"wp_glove_menu_select": "您选择 {lime}{0}{default} 作为您的手套", "wp_glove_menu_select": "您选择 {lime}{0}{default} 作为您的手套",
"wp_glove_menu_title": "手套菜单", "wp_glove_menu_title": "手套菜单",
"wp_agent_menu_select": "您选择了 {lime}{0}{default} 作为您的代理",
"wp_agent_menu_title": "代理菜单",
"wp_music_menu_title": "音乐菜单",
"wp_music_menu_select": "您选择了 {lime}{0}{default} 作为您的音乐包",
"wp_skin_menu_weapon_title": "武器菜单", "wp_skin_menu_weapon_title": "武器菜单",
"wp_skin_menu_skin_title": "选择 {lime}{0}{default} 皮肤", "wp_skin_menu_skin_title": " {lime}{0}{default} 选择皮肤",
"wp_skin_menu_select": "您选择 {lime}{0}{default} 作为您的皮肤", "wp_skin_menu_select": "您选择 {lime}{0}{default} 作为您的皮肤"
}
"None": "无"
}

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

1
website/data/gloves.json Normal file

File diff suppressed because one or more lines are too long

View File

@@ -1 +0,0 @@
[{"weapon_defindex":0,"paint":0,"image":"","paint_name":"Gloves | Default"}]

View File

@@ -1 +0,0 @@
[{"weapon_defindex":0,"paint":0,"image":"","paint_name":"Gloves | Default"}]

View File

@@ -1 +0,0 @@
[{"weapon_defindex":0,"paint":0,"image":"","paint_name":"Gloves | Default"}]

View File

@@ -1 +0,0 @@
[{"weapon_defindex":0,"paint":0,"image":"","paint_name":"Gloves | Default"}]

View File

@@ -1 +0,0 @@
[{"weapon_defindex":0,"paint":0,"image":"","paint_name":"Gloves | Default"}]

File diff suppressed because one or more lines are too long

View File

@@ -1 +0,0 @@
[{"weapon_defindex":0,"paint":0,"image":"","paint_name":"Gloves | Default"}]

View File

@@ -1 +0,0 @@
[{"weapon_defindex":0,"paint":0,"image":"","paint_name":"Gloves | Default"}]

View File

@@ -1 +0,0 @@
[{"weapon_defindex":0,"paint":0,"image":"","paint_name":"Gloves | Default"}]

View File

@@ -1 +0,0 @@
[{"weapon_defindex":0,"paint":0,"image":"","paint_name":"Gloves | Default"}]

View File

@@ -1 +0,0 @@
[{"weapon_defindex":0,"paint":0,"image":"","paint_name":"Gloves | Default"}]

View File

@@ -1 +0,0 @@
[{"weapon_defindex":0,"paint":0,"image":"","paint_name":"Gloves | Default"}]

View File

@@ -1 +0,0 @@
[{"weapon_defindex":0,"paint":0,"image":"","paint_name":"Gloves | Default"}]

View File

@@ -1 +0,0 @@
[{"weapon_defindex":0,"paint":0,"image":"","paint_name":"Gloves | Default"}]

View File

@@ -1 +0,0 @@
[{"weapon_defindex":0,"paint":0,"image":"","paint_name":"Gloves | Default"}]

View File

@@ -1 +0,0 @@
[{"weapon_defindex":0,"paint":0,"image":"","paint_name":"Gloves | Default"}]

View File

@@ -1 +0,0 @@
[{"weapon_defindex":0,"paint":0,"image":"","paint_name":"Gloves | Default"}]

View File

@@ -1 +0,0 @@
[{"weapon_defindex":0,"paint":0,"image":"","paint_name":"Gloves | Default"}]

View File

@@ -1 +0,0 @@
[{"weapon_defindex":0,"paint":0,"image":"","paint_name":"Gloves | Default"}]

View File

@@ -1 +0,0 @@
[{"weapon_defindex":0,"paint":0,"image":"","paint_name":"Gloves | Default"}]

View File

@@ -1 +0,0 @@
[{"weapon_defindex":0,"paint":0,"image":"","paint_name":"Gloves | Default"}]

View File

@@ -1 +0,0 @@
[{"weapon_defindex":0,"paint":0,"image":"","paint_name":"Gloves | Default"}]

View File

@@ -1 +0,0 @@
[{"weapon_defindex":0,"paint":0,"image":"","paint_name":"Gloves | Default"}]

View File

@@ -1 +0,0 @@
[{"weapon_defindex":0,"paint":0,"image":"","paint_name":"Gloves | Default"}]

View File

@@ -1 +0,0 @@
[{"weapon_defindex":0,"paint":0,"image":"","paint_name":"Gloves | Default"}]

View File

@@ -1 +0,0 @@
[{"weapon_defindex":0,"paint":0,"image":"","paint_name":"Gloves | Default"}]

View File

@@ -1 +0,0 @@
[{"weapon_defindex":0,"paint":0,"image":"","paint_name":"Gloves | Default"}]

View File

@@ -1 +0,0 @@
[{"weapon_defindex":0,"paint":0,"image":"","paint_name":"Gloves | Default"}]

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

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