Skip to content

Commit cf43e45

Browse files
authored
Cleanup new game data system and provide fallbacks to default sigs (#177)
1 parent 1f1092c commit cf43e45

File tree

5 files changed

+153
-98
lines changed

5 files changed

+153
-98
lines changed

README.md

+18
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,22 @@ This plugin is made to run alongside B3none's retakes implementation: https://gi
2626
- `mp_afterroundmoney 65535`
2727
- More info about this in the "Buy Menu" section below
2828

29+
## Game Data/Signatures (Optional Information)
30+
This section is optional. If you dont care, you can skip it. The defaults are fine.
31+
32+
This plugin relies on some function signatures that:
33+
- Regularly break with game updates:
34+
- `GetCSWeaponDataFromKey`
35+
- `CCSPlayer_ItemServices_CanAcquire`
36+
- Are not included in the default CS# signatures:
37+
- `GiveNamedItem2`
38+
39+
Custom game data signatures are maintained in https://github.com/yonilerner/cs2-retakes-allocator/blob/main/Resources/RetakesAllocator_gamedata.json. There are a few ways to keep these up to date:
40+
- If you want the plugin to automatically download the signatures, you can do so by running the plugin with the `AutoUpdateSignatures` config set to `true`. **This is the recommended approach**. See more below in the "Configuration" section.
41+
- If you want to manually download the signatures, you can do so by downloading the `RetakesAllocator_gamedata.json` file from Github and placing it in the `RetakesAllocator/gamedata` folder in the plugin. You may have to create that folder if it does not exist.
42+
43+
If you do not want to use any custom game data/signatures, you can disable `AutoUpdateSignatures` and `CapabilityWeaponPaints`. If you do this (and if you previously had downloaded custom game data, make sure to delete the `RetakesAllocator/gamedata/RetakesAllocator_gamedata.json` file), the plugin will fallback to using the default CS# signatures. See more below in the "Configuration" section.
44+
2945
## Usage
3046

3147
### Round Types
@@ -311,6 +327,8 @@ room for it*.
311327
- `UseOnTickFeatures`: Set to false if you want better performance and dont want any OnTick features, including:
312328
- Bombsite center announcement
313329
- Advanced gun menu
330+
- `AutoUpdateSignatures `: When true, the plugin will always try to download the latest custom game data/signatures on startup. A game server restart may be required to apply the new signatures after they have been downloaded. If this is disabled, the plugin will fallback to using the default CS# game data/signatures.
331+
- `CapabilityWeaponPaints`: When true, will try to use the custom game data `GiveNamedItem2` that will maintain weapon paints in non-standard situations. This is enabled by default for backwards compatibility, but is less stable. If this option is enabled, `AutoUpdateSignatures` should also be enabled. If you dont want to use `AutoUpdateSignatures`, at least ensure that the custom game data/signatures are updated correctly, since this `GiveNamedItem2` is not in the default game data/signatures.
314332

315333
### Commands
316334

RetakesAllocator/CustomGameData.cs

+64-33
Original file line numberDiff line numberDiff line change
@@ -5,69 +5,95 @@
55
using RetakesAllocatorCore;
66
using CounterStrikeSharp.API.Modules.Memory.DynamicFunctions;
77
using System.Text.Json;
8+
// ReSharper disable InconsistentNaming
89

910
namespace RetakesAllocator;
1011

1112
public class CustomGameData
1213
{
13-
public static Dictionary<string, Dictionary<OSPlatform, string>> _customGameData = new();
14-
private readonly MemoryFunctionVoid<IntPtr, string, IntPtr, IntPtr, IntPtr, IntPtr, IntPtr, IntPtr> GiveNamedItem2;
15-
public readonly MemoryFunctionWithReturn<CCSPlayer_ItemServices, CEconItemView, AcquireMethod, NativeObject, AcquireResult> CCSPlayer_ItemServices_CanAcquireFunc;
16-
public readonly MemoryFunctionWithReturn<int, string, CCSWeaponBaseVData> GetCSWeaponDataFromKeyFunc;
14+
private static readonly Dictionary<string, Dictionary<OSPlatform, string>> _customGameData = new();
15+
private MemoryFunctionVoid<IntPtr, string, IntPtr, IntPtr, IntPtr, IntPtr, IntPtr, IntPtr>? GiveNamedItem2;
16+
public MemoryFunctionWithReturn<CCSPlayer_ItemServices, CEconItemView, AcquireMethod, NativeObject, AcquireResult>? CCSPlayer_ItemServices_CanAcquireFunc;
17+
public MemoryFunctionWithReturn<int, string, CCSWeaponBaseVData>? GetCSWeaponDataFromKeyFunc;
1718

1819
public CustomGameData()
1920
{
20-
LoadCustomGameDataFromJson();
21-
22-
GiveNamedItem2 = new(GetCustomGameDataKey("GiveNamedItem2"));
23-
CCSPlayer_ItemServices_CanAcquireFunc = new(GetCustomGameDataKey("CCSPlayer_ItemServices_CanAcquire"));
24-
GetCSWeaponDataFromKeyFunc = new(GetCustomGameDataKey("GetCSWeaponDataFromKey"));
21+
LoadCustomGameData();
2522
}
26-
public void LoadCustomGameDataFromJson()
23+
24+
public void LoadCustomGameData()
2725
{
28-
string jsonFilePath = $"{Configs.Shared.Module}/../../plugins/RetakesAllocator/gamedata/RetakesAllocator_gamedata.json";
29-
if (!File.Exists(jsonFilePath))
26+
if (Configs.Shared.Module == null)
3027
{
31-
Log.Debug($"JSON file does not exist at path: {jsonFilePath}. Returning without loading custom game data.");
28+
Log.Error("Module path is null. Returning without loading custom game data.");
3229
return;
3330
}
34-
35-
try
31+
var jsonFilePath = Path.Combine(Configs.Shared.Module, "gamedata/RetakesAllocator_gamedata.json");
32+
if (File.Exists(jsonFilePath))
3633
{
37-
var jsonData = File.ReadAllText(jsonFilePath);
38-
var jsonDocument = JsonDocument.Parse(jsonData);
39-
40-
foreach (var element in jsonDocument.RootElement.EnumerateObject())
34+
try
4135
{
42-
string key = element.Name;
36+
var jsonData = File.ReadAllText(jsonFilePath);
37+
var jsonDocument = JsonDocument.Parse(jsonData);
38+
39+
foreach (var element in jsonDocument.RootElement.EnumerateObject())
40+
{
41+
string key = element.Name;
4342

44-
var platformData = new Dictionary<OSPlatform, string>();
43+
var platformData = new Dictionary<OSPlatform, string>();
4544

46-
if (element.Value.TryGetProperty("signatures", out var signatures))
47-
{
48-
if (signatures.TryGetProperty("windows", out var windows))
45+
if (element.Value.TryGetProperty("signatures", out var signatures))
4946
{
50-
platformData[OSPlatform.Windows] = windows.GetString()!;
51-
}
47+
if (signatures.TryGetProperty("windows", out var windows))
48+
{
49+
platformData[OSPlatform.Windows] = windows.GetString()!;
50+
}
5251

53-
if (signatures.TryGetProperty("linux", out var linux))
54-
{
55-
platformData[OSPlatform.Linux] = linux.GetString()!;
52+
if (signatures.TryGetProperty("linux", out var linux))
53+
{
54+
platformData[OSPlatform.Linux] = linux.GetString()!;
55+
}
5656
}
57+
_customGameData[key] = platformData;
5758
}
58-
_customGameData[key] = platformData;
59+
}
60+
catch (Exception ex)
61+
{
62+
Log.Error($"Error loading custom game data: {ex.Message}");
5963
}
6064
}
61-
catch (Exception ex)
65+
else
6266
{
63-
Log.Debug($"Error loading custom game data: {ex.Message}");
67+
Log.Debug($"JSON file does not exist at path: {jsonFilePath}. Returning without loading custom game data.");
6468
}
69+
70+
try
71+
{
72+
GiveNamedItem2 = new(GetCustomGameDataKey("GiveNamedItem2"));
73+
}
74+
catch
75+
{
76+
// GiveNamedItem2 failing to load shouldnt crash because we will try to fallback to GiveNamedItem
77+
}
78+
GetCSWeaponDataFromKeyFunc = new(GetCustomGameDataKey("GetCSWeaponDataFromKey"));
79+
CCSPlayer_ItemServices_CanAcquireFunc = new(GetCustomGameDataKey("CCSPlayer_ItemServices_CanAcquire"));
6580
}
6681

6782
private string GetCustomGameDataKey(string key)
6883
{
6984
if (!_customGameData.TryGetValue(key, out var customGameData))
7085
{
86+
try
87+
{
88+
var defaultGameData = GameData.GetSignature(key);
89+
Log.Info($"Using default gamedata for {key} because no custom data was found.");
90+
return defaultGameData;
91+
}
92+
catch
93+
{
94+
// ignored
95+
}
96+
7197
throw new Exception($"Invalid key {key}");
7298
}
7399

@@ -90,6 +116,11 @@ private string GetCustomGameDataKey(string key)
90116
: throw new Exception($"Missing custom data for {key} on {platform}");
91117
}
92118

119+
public bool PlayerGiveNamedItemEnabled()
120+
{
121+
return GiveNamedItem2 != null;
122+
}
123+
93124
public void PlayerGiveNamedItem(CCSPlayerController player, string item)
94125
{
95126
if (!player.PlayerPawn.IsValid) return;
@@ -98,7 +129,7 @@ public void PlayerGiveNamedItem(CCSPlayerController player, string item)
98129
if (player.PlayerPawn.Value.ItemServices == null) return;
99130

100131
// Log.Debug("Using custom function for GiveNamedItem2");
101-
GiveNamedItem2.Invoke(player.PlayerPawn.Value.ItemServices.Handle, item, 0, 0, 0, 0, 0, 0);
132+
GiveNamedItem2?.Invoke(player.PlayerPawn.Value.ItemServices.Handle, item, 0, 0, 0, 0, 0, 0);
102133
}
103134
}
104135

RetakesAllocator/Helpers.cs

+19-21
Original file line numberDiff line numberDiff line change
@@ -203,18 +203,23 @@ public static bool IsWindows()
203203

204204
public static bool IsVip(CCSPlayerController player) => AdminManager.PlayerHasPermissions(player, "@css/vip");
205205

206-
public static async Task DownloadMissingFiles()
206+
public static async Task<bool> DownloadMissingFiles()
207207
{
208+
if (!Configs.GetConfigData().AutoUpdateSignatures)
209+
{
210+
return false;
211+
}
208212
string baseFolderPath = Configs.Shared.Module!;
209213

210214
string gamedataFileName = "gamedata/RetakesAllocator_gamedata.json";
211215
string gamedataGithubUrl = "https://raw.githubusercontent.com/yonilerner/cs2-retakes-allocator/main/Resources/RetakesAllocator_gamedata.json";
212216
string gamedataFilePath = Path.Combine(baseFolderPath, gamedataFileName);
213217
string gamedataDirectoryPath = Path.GetDirectoryName(gamedataFilePath)!;
214-
await CheckAndDownloadFile(gamedataFilePath, gamedataGithubUrl, gamedataDirectoryPath);
218+
219+
return await CheckAndDownloadFile(gamedataFilePath, gamedataGithubUrl, gamedataDirectoryPath);
215220
}
216221

217-
public static async Task<bool> CheckAndDownloadFile(string filePath, string githubUrl, string directoryPath)
222+
private static async Task<bool> CheckAndDownloadFile(string filePath, string githubUrl, string directoryPath)
218223
{
219224
if (!File.Exists(filePath))
220225
{
@@ -225,26 +230,19 @@ public static async Task<bool> CheckAndDownloadFile(string filePath, string gith
225230
await DownloadFileFromGithub(githubUrl, filePath);
226231
return true;
227232
}
228-
else
233+
234+
bool isFileDifferent = await IsFileDifferent(filePath, githubUrl);
235+
if (isFileDifferent)
229236
{
230-
if (Configs.GetConfigData().AutoUpdateSignatures)
231-
{
232-
bool isFileDifferent = await IsFileDifferent(filePath, githubUrl);
233-
if (isFileDifferent)
234-
{
235-
File.Delete(filePath);
236-
await DownloadFileFromGithub(githubUrl, filePath);
237-
return true;
238-
}
239-
}
240-
237+
File.Delete(filePath);
238+
await DownloadFileFromGithub(githubUrl, filePath);
239+
return true;
241240
}
242241

243242
return false;
244243
}
245244

246-
247-
public static async Task<bool> IsFileDifferent(string localFilePath, string githubUrl)
245+
private static async Task<bool> IsFileDifferent(string localFilePath, string githubUrl)
248246
{
249247
try
250248
{
@@ -260,12 +258,12 @@ public static async Task<bool> IsFileDifferent(string localFilePath, string gith
260258
}
261259
catch (Exception ex)
262260
{
263-
Log.Debug($"Error comparing files: {ex.Message}");
261+
Log.Warn($"Error comparing files: {ex.Message}");
264262
return false;
265263
}
266264
}
267265

268-
public static string GetFileHash(byte[] fileBytes)
266+
private static string GetFileHash(byte[] fileBytes)
269267
{
270268
using (var md5 = System.Security.Cryptography.MD5.Create())
271269
{
@@ -274,7 +272,7 @@ public static string GetFileHash(byte[] fileBytes)
274272
}
275273
}
276274

277-
public static async Task DownloadFileFromGithub(string url, string destinationPath)
275+
private static async Task DownloadFileFromGithub(string url, string destinationPath)
278276
{
279277
using (HttpClient client = new HttpClient())
280278
{
@@ -285,7 +283,7 @@ public static async Task DownloadFileFromGithub(string url, string destinationPa
285283
}
286284
catch (Exception ex)
287285
{
288-
Log.Debug($"Error downloading file: {ex.Message}");
286+
Log.Warn($"Error downloading file: {ex.Message}");
289287
}
290288
}
291289
}

0 commit comments

Comments
 (0)