diff --git a/Resources/RetakesAllocator_gamedata.json b/Resources/RetakesAllocator_gamedata.json new file mode 100644 index 0000000..a680ca3 --- /dev/null +++ b/Resources/RetakesAllocator_gamedata.json @@ -0,0 +1,9 @@ +{ + "GiveNamedItem2": { + "signatures": { + "library": "server", + "windows": "48 83 EC ? 48 C7 44 24 ? ? ? ? ? 45 33 C9 45 33 C0 C6 44 24 ? ? E8 ? ? ? ? 48 83 C4 ? C3 CC CC CC CC CC CC CC CC CC CC CC CC CC CC 48 83 EC", + "linux": "55 48 89 E5 41 57 41 56 41 55 41 54 53 48 83 EC ? 48 89 7D ? 44 89 45" + } + } +} diff --git a/RetakesAllocator/CustomGameData.cs b/RetakesAllocator/CustomGameData.cs index 640ace2..a2ade65 100644 --- a/RetakesAllocator/CustomGameData.cs +++ b/RetakesAllocator/CustomGameData.cs @@ -1,31 +1,16 @@ using System.Runtime.InteropServices; using CounterStrikeSharp.API; using CounterStrikeSharp.API.Core; +using RetakesAllocatorCore.Config; +using RetakesAllocatorCore; using CounterStrikeSharp.API.Modules.Memory.DynamicFunctions; +using System.Text.Json; namespace RetakesAllocator; public class CustomGameData { - private static Dictionary> _customGameData = new() - { - // Thank you to @Whaliin https://github.com/CS2Plugins/WeaponRestrict/blob/main/WeaponRestrict.json - { - "GiveNamedItem2", - new() - { - { - OSPlatform.Windows, - @"\x48\x83\xEC\x38\x48\xC7\x44\x24\x28\x00\x00\x00\x00\x45\x33\xC9\x45\x33\xC0\xC6\x44\x24\x20\x00\xE8\x2A\x2A\x2A\x2A\x48\x85" - }, - { - OSPlatform.Linux, - @"\x55\x48\x89\xE5\x41\x57\x41\x56\x4D\x89\xC6\x41\x55\x49\x89\xD5\x41\x54\x49\x89\xF4" - }, - } - } - }; - + public static Dictionary> _customGameData = new(); private readonly MemoryFunctionVoid GiveNamedItem2; public readonly @@ -36,8 +21,50 @@ public readonly public CustomGameData() { + LoadCustomGameDataFromJson(); + GiveNamedItem2 = new(GetCustomGameDataKey("GiveNamedItem2")); } + public void LoadCustomGameDataFromJson() + { + string jsonFilePath = $"{Configs.Shared.Module}/../../plugins/RetakesAllocator/gamedata/RetakesAllocator_gamedata.json"; + if (!File.Exists(jsonFilePath)) + { + Log.Debug($"JSON file does not exist at path: {jsonFilePath}. Returning without loading custom game data."); + return; + } + + try + { + var jsonData = File.ReadAllText(jsonFilePath); + var jsonDocument = JsonDocument.Parse(jsonData); + + foreach (var element in jsonDocument.RootElement.EnumerateObject()) + { + string key = element.Name; + + var platformData = new Dictionary(); + + if (element.Value.TryGetProperty("signatures", out var signatures)) + { + if (signatures.TryGetProperty("windows", out var windows)) + { + platformData[OSPlatform.Windows] = windows.GetString()!; + } + + if (signatures.TryGetProperty("linux", out var linux)) + { + platformData[OSPlatform.Linux] = linux.GetString()!; + } + } + _customGameData[key] = platformData; + } + } + catch (Exception ex) + { + Log.Debug($"Error loading custom game data: {ex.Message}"); + } + } private string GetCustomGameDataKey(string key) { diff --git a/RetakesAllocator/Helpers.cs b/RetakesAllocator/Helpers.cs index 0f077f9..d26c9e2 100644 --- a/RetakesAllocator/Helpers.cs +++ b/RetakesAllocator/Helpers.cs @@ -5,6 +5,7 @@ using CounterStrikeSharp.API.Modules.Commands; using CounterStrikeSharp.API.Modules.Entities.Constants; using CounterStrikeSharp.API.Modules.Utils; +using RetakesAllocatorCore.Config; using RetakesAllocatorCore; namespace RetakesAllocator; @@ -201,4 +202,91 @@ public static bool IsWindows() } public static bool IsVip(CCSPlayerController player) => AdminManager.PlayerHasPermissions(player, "@css/vip"); + + public static async Task DownloadMissingFiles() + { + string baseFolderPath = Configs.Shared.Module!; + + string gamedataFileName = "gamedata/RetakesAllocator_gamedata.json"; + string gamedataGithubUrl = "https://raw.githubusercontent.com/yonilerner/cs2-retakes-allocator/main/Resources/RetakesAllocator_gamedata.json"; + string gamedataFilePath = Path.Combine(baseFolderPath, gamedataFileName); + string gamedataDirectoryPath = Path.GetDirectoryName(gamedataFilePath)!; + await CheckAndDownloadFile(gamedataFilePath, gamedataGithubUrl, gamedataDirectoryPath); + } + + public static async Task CheckAndDownloadFile(string filePath, string githubUrl, string directoryPath) + { + if (!File.Exists(filePath)) + { + if (!Directory.Exists(directoryPath)) + { + Directory.CreateDirectory(directoryPath); + } + await DownloadFileFromGithub(githubUrl, filePath); + return true; + } + else + { + if (Configs.GetConfigData().AutoUpdateSignatures) + { + bool isFileDifferent = await IsFileDifferent(filePath, githubUrl); + if (isFileDifferent) + { + File.Delete(filePath); + await DownloadFileFromGithub(githubUrl, filePath); + return true; + } + } + + } + + return false; + } + + + public static async Task IsFileDifferent(string localFilePath, string githubUrl) + { + try + { + byte[] localFileBytes = await File.ReadAllBytesAsync(localFilePath); + string localFileHash = GetFileHash(localFileBytes); + + using (HttpClient client = new HttpClient()) + { + byte[] githubFileBytes = await client.GetByteArrayAsync(githubUrl); + string githubFileHash = GetFileHash(githubFileBytes); + return localFileHash != githubFileHash; + } + } + catch (Exception ex) + { + Log.Debug($"Error comparing files: {ex.Message}"); + return false; + } + } + + public static string GetFileHash(byte[] fileBytes) + { + using (var md5 = System.Security.Cryptography.MD5.Create()) + { + byte[] hashBytes = md5.ComputeHash(fileBytes); + return BitConverter.ToString(hashBytes).Replace("-", "").ToLowerInvariant(); + } + } + + public static async Task DownloadFileFromGithub(string url, string destinationPath) + { + using (HttpClient client = new HttpClient()) + { + try + { + byte[] fileBytes = await client.GetByteArrayAsync(url); + await File.WriteAllBytesAsync(destinationPath, fileBytes); + } + catch (Exception ex) + { + Log.Debug($"Error downloading file: {ex.Message}"); + } + } + } } diff --git a/RetakesAllocator/RetakesAllocator.cs b/RetakesAllocator/RetakesAllocator.cs index f731006..720dbff 100644 --- a/RetakesAllocator/RetakesAllocator.cs +++ b/RetakesAllocator/RetakesAllocator.cs @@ -49,6 +49,8 @@ public class RetakesAllocator : BasePlugin public override void Load(bool hotReload) { + Configs.Shared.Module = ModuleDirectory; + Log.Debug($"Loaded. Hot reload: {hotReload}"); ResetState(); Batteries.Init(); @@ -60,6 +62,11 @@ public override void Load(bool hotReload) RoundTypeManager.Instance.SetMap(mapName); }); + _ = Task.Run(async () => + { + await Helpers.DownloadMissingFiles(); + }); + if (Configs.GetConfigData().UseOnTickFeatures) { RegisterListener(OnTick); @@ -949,7 +956,14 @@ private void AllocateItemsForPlayer(CCSPlayerController player, ICollection Validate() { diff --git a/RetakesAllocatorCore/PluginInfo.cs b/RetakesAllocatorCore/PluginInfo.cs index f53c7ad..f678887 100644 --- a/RetakesAllocatorCore/PluginInfo.cs +++ b/RetakesAllocatorCore/PluginInfo.cs @@ -5,7 +5,7 @@ namespace RetakesAllocatorCore; public static class PluginInfo { - public const string Version = "2.3.19"; + public const string Version = "2.4.0"; public static readonly string LogPrefix = $"[RetakesAllocator {Version}] ";