Compare commits

..

6 Commits

Author SHA1 Message Date
Nereziel
530a7d64c7 Update README.md 2023-11-19 20:45:12 +01:00
Nereziel
3453f4c505 Merge pull request #47 from daffyyyy/feature/global-share
EXPERIMENTAL (GlobalShare) - Website for all servers
2023-11-19 19:03:05 +01:00
Nereziel
25b466422b Update build.yml 2023-11-19 19:02:26 +01:00
daffyyyy
6baa59dd9b Update WeaponPaints.cs
New version
2023-11-19 12:30:47 +01:00
daffyyyy
10afe7ce1e Update WeaponPaints.cs 2023-11-19 12:24:57 +01:00
daffyyyy
5eeb0c5fec Initial 2023-11-19 02:07:29 +01:00
5 changed files with 683 additions and 524 deletions

View File

@@ -30,6 +30,7 @@ jobs:
run: dotnet build ${{ env.PROJECT_PATH }} -c WeaponPaints -o ${{ env.OUTPUT_PATH }} run: dotnet build ${{ env.PROJECT_PATH }} -c WeaponPaints -o ${{ env.OUTPUT_PATH }}
publish: publish:
if: github.event_name == 'push'
permissions: write-all permissions: write-all
runs-on: ubuntu-latest runs-on: ubuntu-latest
needs: build needs: build

View File

@@ -75,6 +75,9 @@ namespace WeaponPaints
[JsonPropertyName("DatabaseName")] [JsonPropertyName("DatabaseName")]
public string DatabaseName { get; set; } = ""; public string DatabaseName { get; set; } = "";
[JsonPropertyName("GlobalShare")]
public bool GlobalShare { get; set; } = false;
[JsonPropertyName("CmdRefreshCooldownSeconds")] [JsonPropertyName("CmdRefreshCooldownSeconds")]
public int CmdRefreshCooldownSeconds { get; set; } = 60; public int CmdRefreshCooldownSeconds { get; set; } = 60;

View File

@@ -11,7 +11,7 @@ There will be a lot of frequent changes which may break functionality or compati
### Features ### Features
- changes only paint, seed and wear on weapons and knives - changes only paint, seed and wear on weapons and knives
- mysql based - mysql based or global website at [weaponpaints.fun](https://weaponpaints.fun/), so you dont need mysql/website
- data sync on player connect - data sync on player connect
- Added command `!wp` to refresh skins (with cooldown in second can be configured) - Added command `!wp` to refresh skins (with cooldown in second can be configured)
- Added command `!ws` to show website - Added command `!ws` to show website
@@ -20,10 +20,12 @@ There will be a lot of frequent changes which may break functionality or compati
### CS2 server: ### CS2 server:
- compile and copy plugin to plugins. Info here [https://docs.cssharp.dev/guides/hello-world-plugin/](https://docs.cssharp.dev/guides/hello-world-plugin/) - compile and copy plugin to plugins. Info here [https://docs.cssharp.dev/guides/hello-world-plugin/](https://docs.cssharp.dev/guides/hello-world-plugin/)
- setup `addons/counterstrikesharp/configs/plugins/WeaponPaints/WeaponPaints.json` with database credentials - setup `addons/counterstrikesharp/configs/plugins/WeaponPaints/WeaponPaints.json`
set `GlobalShare` to true for gloval, or include database credentials
- in `addons/counterstrikesharp/configs/core.json` set **FollowCS2ServerGuidelines** to **false** - in `addons/counterstrikesharp/configs/core.json` set **FollowCS2ServerGuidelines** to **false**
### Web install: ### Web install:
- not needed if config `GlobalShare = true`
- requires PHP min v7.3 (tested on php ver `8.2.3` and nginx webserver) - requires PHP min v7.3 (tested on php ver `8.2.3` and nginx webserver)
- copy website to web server (img folder not needed) - copy website to web server (img folder not needed)
- import `database.sql` to mysql - import `database.sql` to mysql
@@ -31,6 +33,9 @@ There will be a lot of frequent changes which may break functionality or compati
- fill in database credentials and api key in `class/config.php` - fill in database credentials and api key in `class/config.php`
- visit website and login via steam - visit website and login via steam
### Known issues
- Issue on Windows servers, no knives are given.
### Use this plugin at your own risk! Using this may lead to GSLT ban or something else Valve come with. [Valve Server guidelines](https://blog.counter-strike.net/index.php/server_guidelines/) ### 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/)
### Preview ### Preview

View File

@@ -1,7 +1,6 @@
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.Core.Attributes.Registration;
using CounterStrikeSharp.API.Modules.Commands; using CounterStrikeSharp.API.Modules.Commands;
using CounterStrikeSharp.API.Modules.Entities; using CounterStrikeSharp.API.Modules.Entities;
using CounterStrikeSharp.API.Modules.Memory; using CounterStrikeSharp.API.Modules.Memory;
@@ -12,6 +11,8 @@ using Dapper;
using System.Runtime.ExceptionServices; using System.Runtime.ExceptionServices;
using System.Reflection; using System.Reflection;
using CounterStrikeSharp.API.Modules.Cvars; using CounterStrikeSharp.API.Modules.Cvars;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
namespace WeaponPaints; namespace WeaponPaints;
[MinimumApiVersion(55)] [MinimumApiVersion(55)]
@@ -20,12 +21,14 @@ public class WeaponPaints : BasePlugin, IPluginConfig<WeaponPaintsConfig>
public override string ModuleName => "WeaponPaints"; public override string ModuleName => "WeaponPaints";
public override string ModuleDescription => "Connector for web-based player chosen wepaon paints, and standalone for knife."; public override string ModuleDescription => "Connector for web-based player chosen wepaon paints, and standalone for knife.";
public override string ModuleAuthor => "Nereziel"; public override string ModuleAuthor => "Nereziel";
public override string ModuleVersion => "0.9"; public override string ModuleVersion => "1.0";
public WeaponPaintsConfig Config { get; set; } = new(); public WeaponPaintsConfig Config { get; set; } = new();
private string DatabaseConnectionString = string.Empty; private string DatabaseConnectionString = string.Empty;
private Uri GlobalShareApi = new Uri("https://weaponpaints.fun/api.php");
public bool IsMatchZy = false; public bool IsMatchZy = false;
public int GlobalShareServerId = 0;
private DateTime[] commandCooldown = new DateTime[Server.MaxPlayers]; private DateTime[] commandCooldown = new DateTime[Server.MaxPlayers];
private Dictionary<ulong, Dictionary<nint, int>> gPlayerWeaponPaints = new(); private Dictionary<ulong, Dictionary<nint, int>> gPlayerWeaponPaints = new();
@@ -60,8 +63,11 @@ public class WeaponPaints : BasePlugin, IPluginConfig<WeaponPaintsConfig>
public override void Load(bool hotReload) public override void Load(bool hotReload)
{ {
SetGlobalExceptionHandler(); SetGlobalExceptionHandler();
if (!Config.GlobalShare)
{
BuildDatabaseConnectionString(); BuildDatabaseConnectionString();
TestDatabaseConnection(); TestDatabaseConnection();
}
RegisterListener<Listeners.OnEntitySpawned>(OnEntitySpawned); RegisterListener<Listeners.OnEntitySpawned>(OnEntitySpawned);
RegisterListener<Listeners.OnClientPutInServer>(OnClientPutInServer); RegisterListener<Listeners.OnClientPutInServer>(OnClientPutInServer);
RegisterListener<Listeners.OnClientDisconnect>(OnClientDisconnect); RegisterListener<Listeners.OnClientDisconnect>(OnClientDisconnect);
@@ -91,11 +97,14 @@ public class WeaponPaints : BasePlugin, IPluginConfig<WeaponPaintsConfig>
RegisterCommands(); RegisterCommands();
} }
public void OnConfigParsed(WeaponPaintsConfig config) public void OnConfigParsed(WeaponPaintsConfig config)
{
if (!config.GlobalShare)
{ {
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)
{ {
throw new Exception("You need to setup Database credentials in config!"); throw new Exception("You need to setup Database credentials in config!");
} }
}
Config = config; Config = config;
} }
@@ -192,8 +201,10 @@ public override void Unload(bool hotReload)
{ {
AddCommand($"css_{Config.Additional.CommandSkin}", "Skins info", (player, info) => { if (player == null) return; OnCommandWS(player, info); }); AddCommand($"css_{Config.Additional.CommandSkin}", "Skins info", (player, info) => { if (player == null) return; OnCommandWS(player, info); });
AddCommand($"css_{Config.Additional.CommandRefresh}", "Skins refresh", (player, info) => { if (player == null) return; OnCommandRefresh(player, info); }); AddCommand($"css_{Config.Additional.CommandRefresh}", "Skins refresh", (player, info) => { if (player == null) return; OnCommandRefresh(player, info); });
if (Config.Additional.CommandKillEnabled) { if (Config.Additional.CommandKillEnabled)
AddCommand($"css_{Config.Additional.CommandKill}", "kill yourself", (player, info) => { {
AddCommand($"css_{Config.Additional.CommandKill}", "kill yourself", (player, info) =>
{
if (player == null || !player.IsValid || !player.PlayerPawn.IsValid) if (player == null || !player.IsValid || !player.PlayerPawn.IsValid)
return; return;
@@ -216,11 +227,48 @@ public override void Unload(bool hotReload)
if (!Config.Additional.KnifeEnabled) return; if (!Config.Additional.KnifeEnabled) return;
// TODO // TODO
// needed for now // needed for now
AddTimer(2.0f, () => { AddTimer(2.0f, () =>
{
NativeAPI.IssueServerCommand("mp_t_default_melee \"\""); NativeAPI.IssueServerCommand("mp_t_default_melee \"\"");
NativeAPI.IssueServerCommand("mp_ct_default_melee \"\""); NativeAPI.IssueServerCommand("mp_ct_default_melee \"\"");
IncompatibilityCheck(); IncompatibilityCheck();
}); });
if (Config.GlobalShare)
GlobalShareConnect();
}
private void GlobalShareConnect()
{
if (!Config.GlobalShare) return;
var values = new Dictionary<string, string>
{
{ "server_address", $"{ConVar.Find("ip")!.StringValue}:{ConVar.Find("hostport")!.GetPrimitiveValue<int>().ToString()}" },
{ "server_hostname", ConVar.Find("hostname")!.StringValue }
};
using (var httpClient = new HttpClient())
{
httpClient.BaseAddress = GlobalShareApi;
var formContent = new FormUrlEncodedContent(values);
Task<HttpResponseMessage> responseTask = httpClient.PostAsync("", formContent);
responseTask.Wait();
HttpResponseMessage response = responseTask.Result;
if (response.IsSuccessStatusCode)
{
Task<string> responseBodyTask = response.Content.ReadAsStringAsync();
responseBodyTask.Wait();
string responseBody = responseBodyTask.Result;
GlobalShareServerId = Int32.Parse(responseBody);
}
else
{
throw new Exception("Unable to retrieve serverid from GlobalShare!");
}
}
Console.WriteLine("[WeaponPaints] GlobalShare ONLINE");
} }
private void OnClientPutInServer(int playerSlot) private void OnClientPutInServer(int playerSlot)
@@ -253,10 +301,12 @@ public override void Unload(bool hotReload)
return HookResult.Continue; return HookResult.Continue;
} }
if (Config.Additional.KnifeEnabled) { if (Config.Additional.KnifeEnabled)
{
GiveKnifeToPlayer(player); GiveKnifeToPlayer(player);
} }
return HookResult.Continue; return HookResult.Continue;
} }
private HookResult OnRoundStart(EventRoundStart @event, GameEventInfo info) private HookResult OnRoundStart(EventRoundStart @event, GameEventInfo info)
@@ -279,9 +329,11 @@ public override void Unload(bool hotReload)
if (g_playersKnife.ContainsKey((int)player.EntityIndex!.Value.Value) if (g_playersKnife.ContainsKey((int)player.EntityIndex!.Value.Value)
&& &&
g_playersKnife[(int)player.EntityIndex!.Value.Value] != "weapon_knife") g_playersKnife[(int)player.EntityIndex!.Value.Value] != "weapon_knife")
{
RefreshPlayerKnife(player, true); RefreshPlayerKnife(player, true);
} }
} }
}
return HookResult.Continue; return HookResult.Continue;
} }
@@ -299,7 +351,8 @@ public override void Unload(bool hotReload)
} }
Server.NextFrame(() => Server.NextFrame(() =>
{ {
try { try
{
if (!weapon.IsValid) return; if (!weapon.IsValid) return;
if (weapon.OwnerEntity.Value == null) return; if (weapon.OwnerEntity.Value == null) return;
if (!weapon.OwnerEntity.Value.EntityIndex.HasValue) return; if (!weapon.OwnerEntity.Value.EntityIndex.HasValue) return;
@@ -330,7 +383,8 @@ public override void Unload(bool hotReload)
var skeleton = GetSkeletonInstance(weapon.CBodyComponent.SceneNode); var skeleton = GetSkeletonInstance(weapon.CBodyComponent.SceneNode);
skeleton.ModelState.MeshGroupMask = 2; skeleton.ModelState.MeshGroupMask = 2;
} }
} catch(Exception) {} }
catch (Exception) { }
}); });
} }
public void GiveKnifeToPlayer(CCSPlayerController? player) public void GiveKnifeToPlayer(CCSPlayerController? player)
@@ -380,7 +434,8 @@ public override void Unload(bool hotReload)
if (remove == true) if (remove == true)
RemoveKnifeFromPlayer(player); RemoveKnifeFromPlayer(player);
AddTimer(0.1f, () => { AddTimer(0.2f, () =>
{
if (!PlayerHasKnife(player)) if (!PlayerHasKnife(player))
GiveKnifeToPlayer(player); GiveKnifeToPlayer(player);
}); });
@@ -388,8 +443,8 @@ public override void Unload(bool hotReload)
if (Config.Additional.SkinVisibilityFix) if (Config.Additional.SkinVisibilityFix)
{ {
AddTimer(0.2f, () => NativeAPI.IssueClientCommand((int)player.EntityIndex!.Value.Value - 1, "slot3")); AddTimer(0.2f, () => NativeAPI.IssueClientCommand((int)player.EntityIndex!.Value.Value - 1, "slot3"));
AddTimer(0.32f, () => NativeAPI.IssueClientCommand((int)player.EntityIndex!.Value.Value - 1, "slot2")); AddTimer(0.3f, () => NativeAPI.IssueClientCommand((int)player.EntityIndex!.Value.Value - 1, "slot2"));
AddTimer(0.42f, () => NativeAPI.IssueClientCommand((int)player.EntityIndex!.Value.Value - 1, "slot1")); AddTimer(0.36f, () => NativeAPI.IssueClientCommand((int)player.EntityIndex!.Value.Value - 1, "slot1"));
} }
} }
public bool PlayerHasKnife(CCSPlayerController? player) public bool PlayerHasKnife(CCSPlayerController? player)
@@ -425,11 +480,13 @@ public override void Unload(bool hotReload)
if (knifeTypes.TryGetValue(option.Text, out var knife)) if (knifeTypes.TryGetValue(option.Text, out var knife))
{ {
g_playersKnife[(int)player.EntityIndex!.Value.Value] = knifeTypes[option.Text]; g_playersKnife[(int)player.EntityIndex!.Value.Value] = knifeTypes[option.Text];
if (!string.IsNullOrEmpty(Config.Messages.ChosenKnifeMenu)) { if (!string.IsNullOrEmpty(Config.Messages.ChosenKnifeMenu))
{
temp = $"{Config.Prefix} {Config.Messages.ChosenKnifeMenu}".Replace("{KNIFE}", option.Text); temp = $"{Config.Prefix} {Config.Messages.ChosenKnifeMenu}".Replace("{KNIFE}", option.Text);
player.PrintToChat(ReplaceTags(temp)); player.PrintToChat(ReplaceTags(temp));
} }
if (!string.IsNullOrEmpty(Config.Messages.ChosenKnifeMenuKill) && Config.Additional.CommandKillEnabled) { if (!string.IsNullOrEmpty(Config.Messages.ChosenKnifeMenuKill) && Config.Additional.CommandKillEnabled)
{
temp = $"{Config.Prefix} {Config.Messages.ChosenKnifeMenuKill}"; temp = $"{Config.Prefix} {Config.Messages.ChosenKnifeMenuKill}";
player.PrintToChat(ReplaceTags(temp)); player.PrintToChat(ReplaceTags(temp));
} }
@@ -451,7 +508,7 @@ public override void Unload(bool hotReload)
if (player == null) return; if (player == null) return;
string temp = ""; string temp = "";
int playerIndex = (int)player.EntityIndex!.Value.Value; int playerIndex = (int)player.EntityIndex!.Value.Value;
if (DateTime.UtcNow >= commandCooldown[playerIndex].AddSeconds(Config.CmdRefreshCooldownSeconds)) if (commandCooldown != null && DateTime.UtcNow >= commandCooldown[playerIndex].AddSeconds(Config.CmdRefreshCooldownSeconds))
{ {
commandCooldown[playerIndex] = DateTime.UtcNow; commandCooldown[playerIndex] = DateTime.UtcNow;
Task.Run(async () => await GetWeaponPaintsFromDatabase(playerIndex)); Task.Run(async () => await GetWeaponPaintsFromDatabase(playerIndex));
@@ -459,15 +516,17 @@ public override void Unload(bool hotReload)
{ {
Task.Run(async () => await GetKnifeFromDatabase(playerIndex)); Task.Run(async () => await GetKnifeFromDatabase(playerIndex));
RemoveKnifeFromPlayer(player); RemoveKnifeFromPlayer(player);
AddTimer(0.3f, () => GiveKnifeToPlayer(player)); AddTimer(0.2f, () => GiveKnifeToPlayer(player));
} }
if (!string.IsNullOrEmpty(Config.Messages.SuccessRefreshCommand)) { if (!string.IsNullOrEmpty(Config.Messages.SuccessRefreshCommand))
{
temp = $"{Config.Prefix} {Config.Messages.SuccessRefreshCommand}"; temp = $"{Config.Prefix} {Config.Messages.SuccessRefreshCommand}";
player.PrintToChat(ReplaceTags(temp)); player.PrintToChat(ReplaceTags(temp));
} }
return; return;
} }
if (!string.IsNullOrEmpty(Config.Messages.CooldownRefreshCommand)) { if (!string.IsNullOrEmpty(Config.Messages.CooldownRefreshCommand))
{
temp = $"{Config.Prefix} {Config.Messages.CooldownRefreshCommand}"; temp = $"{Config.Prefix} {Config.Messages.CooldownRefreshCommand}";
player.PrintToChat(ReplaceTags(temp)); player.PrintToChat(ReplaceTags(temp));
} }
@@ -480,16 +539,19 @@ public override void Unload(bool hotReload)
string temp = ""; string temp = "";
if (!string.IsNullOrEmpty(Config.Messages.WebsiteMessageCommand)) { if (!string.IsNullOrEmpty(Config.Messages.WebsiteMessageCommand))
{
temp = $"{Config.Prefix} {Config.Messages.WebsiteMessageCommand}"; temp = $"{Config.Prefix} {Config.Messages.WebsiteMessageCommand}";
player.PrintToChat(ReplaceTags(temp)); player.PrintToChat(ReplaceTags(temp));
} }
if (!string.IsNullOrEmpty(Config.Messages.SynchronizeMessageCommand)) { if (!string.IsNullOrEmpty(Config.Messages.SynchronizeMessageCommand))
{
temp = $"{Config.Prefix} {Config.Messages.SynchronizeMessageCommand}"; temp = $"{Config.Prefix} {Config.Messages.SynchronizeMessageCommand}";
player.PrintToChat(ReplaceTags(temp)); player.PrintToChat(ReplaceTags(temp));
} }
if (!Config.Additional.KnifeEnabled) return; if (!Config.Additional.KnifeEnabled) return;
if (!string.IsNullOrEmpty(Config.Messages.KnifeMessageCommand)) { if (!string.IsNullOrEmpty(Config.Messages.KnifeMessageCommand))
{
temp = $"{Config.Prefix} {Config.Messages.KnifeMessageCommand}"; temp = $"{Config.Prefix} {Config.Messages.KnifeMessageCommand}";
player.PrintToChat(ReplaceTags(temp)); player.PrintToChat(ReplaceTags(temp));
} }
@@ -502,12 +564,61 @@ public override void Unload(bool hotReload)
private async Task GetWeaponPaintsFromDatabase(int playerIndex) private async Task GetWeaponPaintsFromDatabase(int playerIndex)
{ {
if (!Config.Additional.SkinEnabled) return; if (!Config.Additional.SkinEnabled) return;
try try
{ {
CCSPlayerController player = Utilities.GetPlayerFromIndex(playerIndex); CCSPlayerController player = Utilities.GetPlayerFromIndex(playerIndex);
if (player == null || !player.IsValid || player.IsBot) return; if (player == null || !player.IsValid || player.IsBot) return;
var steamId = new SteamID(player.SteamID); var steamId = new SteamID(player.SteamID);
if (Config.GlobalShare)
{
var values = new Dictionary<string, string>
{
{ "server_id", GlobalShareServerId.ToString() },
{ "steamid", steamId.SteamId64.ToString() },
{ "skins", "1" }
};
UriBuilder builder = new UriBuilder(GlobalShareApi);
builder.Query = string.Join("&", values.Select(p => $"{Uri.EscapeDataString(p.Key)}={Uri.EscapeDataString(p.Value)}"));
using (var httpClient = new HttpClient())
{
httpClient.BaseAddress = GlobalShareApi;
var formContent = new FormUrlEncodedContent(values);
HttpResponseMessage response = await httpClient.GetAsync(builder.Uri);
if (response.IsSuccessStatusCode)
{
string responseBody = await response.Content.ReadAsStringAsync();
JArray jsonArray = JArray.Parse(responseBody);
if (jsonArray.Count > 0)
{
gPlayerWeaponPaints[steamId.SteamId64] = new Dictionary<nint, int>();
gPlayerWeaponWear[steamId.SteamId64] = new Dictionary<nint, float>();
gPlayerWeaponSeed[steamId.SteamId64] = new Dictionary<nint, int>();
foreach (var weapon in jsonArray)
{
int weaponDefIndex = weapon["weapon_defindex"].Value<int>();
int weaponPaintId = weapon["weapon_paint_id"].Value<int>();
float weaponWear = weapon["weapon_wear"].Value<float>();
int weaponSeed = weapon["weapon_seed"].Value<int>();
gPlayerWeaponPaints[steamId.SteamId64][weaponDefIndex] = weaponPaintId;
gPlayerWeaponWear[steamId.SteamId64][weaponDefIndex] = weaponWear;
gPlayerWeaponSeed[steamId.SteamId64][weaponDefIndex] = weaponSeed;
}
}
return;
}
else
{
return;
}
}
}
using (var connection = new MySqlConnection(DatabaseConnectionString)) using (var connection = new MySqlConnection(DatabaseConnectionString))
{ {
await connection.OpenAsync(); await connection.OpenAsync();
@@ -555,6 +666,44 @@ public override void Unload(bool hotReload)
if (player == null || !player.IsValid || player.IsBot) return; if (player == null || !player.IsValid || player.IsBot) return;
var steamId = new SteamID(player.SteamID); var steamId = new SteamID(player.SteamID);
if (Config.GlobalShare)
{
var values = new Dictionary<string, string>
{
{ "server_id", GlobalShareServerId.ToString() },
{ "steamid", steamId.SteamId64.ToString() },
{ "knife", "1" }
};
UriBuilder builder = new UriBuilder(GlobalShareApi);
builder.Query = string.Join("&", values.Select(p => $"{Uri.EscapeDataString(p.Key)}={Uri.EscapeDataString(p.Value)}"));
using (var httpClient = new HttpClient())
{
httpClient.BaseAddress = GlobalShareApi;
var formContent = new FormUrlEncodedContent(values);
HttpResponseMessage response = await httpClient.GetAsync(builder.Uri);
if (response.IsSuccessStatusCode)
{
string result = await response.Content.ReadAsStringAsync();
if (!string.IsNullOrEmpty(result))
{
g_playersKnife[playerIndex] = result;
}
else
{
return;
}
}
else
{
return;
}
}
return;
}
using (var connection = new MySqlConnection(DatabaseConnectionString)) using (var connection = new MySqlConnection(DatabaseConnectionString))
{ {
await connection.OpenAsync(); await connection.OpenAsync();

View File

@@ -11,6 +11,7 @@
<PackageReference Include="CounterStrikeSharp.API" Version="*" /> <PackageReference Include="CounterStrikeSharp.API" Version="*" />
<PackageReference Include="Dapper" Version="2.1.21" /> <PackageReference Include="Dapper" Version="2.1.21" />
<PackageReference Include="MySqlConnector" Version="2.3.1" /> <PackageReference Include="MySqlConnector" Version="2.3.1" />
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>