From fc7660b6c8b18e2468080ed8ff0a1c7bc93b5e3f Mon Sep 17 00:00:00 2001 From: Jesse Date: Thu, 31 Jul 2025 00:23:29 +0200 Subject: [PATCH] Slight bundle & fileutil refactor: (#524) - Actually load the bundle cache file now - Don't read file as string with StreamReader, instead read with ReadAllTextAsync - Slight refactor to bundle hashing Co-authored-by: Chomp <27521899+chompDev@users.noreply.github.com> --- .../Callbacks/BundleCallbacks.cs | 6 +- .../Loaders/BundleLoader.cs | 9 +-- .../Services/BundleHashCacheService.cs | 59 +++++++++++++------ .../SPTarkov.Server.Core/Utils/FileUtil.cs | 14 ++++- .../SPTarkov.Server.Core/Utils/HashUtil.cs | 5 ++ .../Services/SptServerBackgroundService.cs | 2 +- 6 files changed, 67 insertions(+), 28 deletions(-) diff --git a/Libraries/SPTarkov.Server.Core/Callbacks/BundleCallbacks.cs b/Libraries/SPTarkov.Server.Core/Callbacks/BundleCallbacks.cs index 18dc9ffa..24594d07 100644 --- a/Libraries/SPTarkov.Server.Core/Callbacks/BundleCallbacks.cs +++ b/Libraries/SPTarkov.Server.Core/Callbacks/BundleCallbacks.cs @@ -2,6 +2,7 @@ using SPTarkov.Server.Core.Loaders; using SPTarkov.Server.Core.Models.Common; using SPTarkov.Server.Core.Models.Eft.Common; +using SPTarkov.Server.Core.Routers.Serializers; using SPTarkov.Server.Core.Utils; namespace SPTarkov.Server.Core.Callbacks; @@ -19,9 +20,10 @@ public class BundleCallbacks(HttpResponseUtil httpResponseUtil, BundleLoader bun } /// - /// TODO: what does it do + /// Handle requests to /files/bundle
+ ///
+ /// Makes sure the output is set to BUNDLE so that the BundleSerializer's can handle it. ///
- /// public ValueTask GetBundle(string url, object info, MongoId sessionID) { return new ValueTask("BUNDLE"); diff --git a/Libraries/SPTarkov.Server.Core/Loaders/BundleLoader.cs b/Libraries/SPTarkov.Server.Core/Loaders/BundleLoader.cs index 129abf9e..f5328aa2 100644 --- a/Libraries/SPTarkov.Server.Core/Loaders/BundleLoader.cs +++ b/Libraries/SPTarkov.Server.Core/Loaders/BundleLoader.cs @@ -39,6 +39,8 @@ public class BundleLoader(ISptLogger logger, JsonUtil jsonUtil, Bu public async Task LoadBundlesAsync(SptMod mod) { + await bundleHashCacheService.HydrateCache(); + var modPath = mod.GetModPath(); var modBundles = await jsonUtil.DeserializeFromFileAsync( @@ -59,12 +61,7 @@ public class BundleLoader(ISptLogger logger, JsonUtil jsonUtil, Bu continue; } - if (!bundleHashCacheService.CalculateAndMatchHash(bundleLocalPath)) - { - await bundleHashCacheService.CalculateAndStoreHash(bundleLocalPath); - } - - var bundleHash = bundleHashCacheService.GetStoredValue(bundleLocalPath); + var bundleHash = await bundleHashCacheService.CalculateMatchAndStoreHash(bundleLocalPath); AddBundle(bundleManifest.Key, new BundleInfo(relativeModPath, bundleManifest, bundleHash)); } diff --git a/Libraries/SPTarkov.Server.Core/Services/BundleHashCacheService.cs b/Libraries/SPTarkov.Server.Core/Services/BundleHashCacheService.cs index f9f37dd7..34d6f9f2 100644 --- a/Libraries/SPTarkov.Server.Core/Services/BundleHashCacheService.cs +++ b/Libraries/SPTarkov.Server.Core/Services/BundleHashCacheService.cs @@ -9,9 +9,27 @@ public class BundleHashCacheService(ISptLogger logger, J { protected const string _bundleHashCachePath = "./user/cache/"; protected const string _cacheName = "bundleHashCache.json"; - protected readonly Dictionary _bundleHashes = []; + protected Dictionary _bundleHashes = []; - public uint GetStoredValue(string key) + public async Task HydrateCache() + { + if (!Directory.Exists(_bundleHashCachePath)) + { + Directory.CreateDirectory(_bundleHashCachePath); + } + + var fullCachePath = Path.Join(_bundleHashCachePath, _cacheName); + + // File doesn't exist, assume this is the first time we're trying to load in bundles + if (!File.Exists(fullCachePath)) + { + return; + } + + _bundleHashes = await jsonUtil.DeserializeFromFileAsync>(fullCachePath) ?? []; + } + + protected uint GetStoredValue(string key) { if (!_bundleHashes.TryGetValue(key, out var value)) { @@ -21,37 +39,44 @@ public class BundleHashCacheService(ISptLogger logger, J return value; } - public async Task StoreValue(string bundlePath, uint hash) + protected async Task StoreValue(string bundlePath, uint hash) { _bundleHashes.Add(bundlePath, hash); - if (!Directory.Exists(_bundleHashCachePath)) + var bundleHashesSerialized = jsonUtil.Serialize(_bundleHashes); + + if (bundleHashesSerialized is null) { - Directory.CreateDirectory(_bundleHashCachePath); + return; } - await fileUtil.WriteFileAsync(Path.Join(_bundleHashCachePath, _cacheName), jsonUtil.Serialize(_bundleHashes)); + await fileUtil.WriteFileAsync(Path.Join(_bundleHashCachePath, _cacheName), bundleHashesSerialized); logger.Debug($"Bundle: {bundlePath} hash stored in: ${_bundleHashCachePath}"); } - public bool CalculateAndMatchHash(string BundlePath) + /// + /// Calculate, match the current hash and store the correct hash of the bundle + /// + /// The path to the bundle + public async Task CalculateMatchAndStoreHash(string BundlePath) { - return MatchWithStoredHash(BundlePath, CalculateHash(BundlePath)); + var hash = await CalculateHash(BundlePath); + + if (!MatchWithStoredHash(BundlePath, hash)) + { + await StoreValue(BundlePath, await CalculateHash(BundlePath)); + } + + return hash; } - public async Task CalculateAndStoreHash(string BundlePath) + protected async Task CalculateHash(string BundlePath) { - await StoreValue(BundlePath, CalculateHash(BundlePath)); + return hashUtil.GenerateCrc32ForData(await fileUtil.ReadFileAsBytesAsync(BundlePath)); } - public uint CalculateHash(string BundlePath) - { - var fileData = fileUtil.ReadFile(BundlePath); - return hashUtil.GenerateCrc32ForData(fileData); - } - - public bool MatchWithStoredHash(string BundlePath, uint hash) + protected bool MatchWithStoredHash(string BundlePath, uint hash) { return GetStoredValue(BundlePath) == hash; } diff --git a/Libraries/SPTarkov.Server.Core/Utils/FileUtil.cs b/Libraries/SPTarkov.Server.Core/Utils/FileUtil.cs index 87eb541d..1842fabe 100644 --- a/Libraries/SPTarkov.Server.Core/Utils/FileUtil.cs +++ b/Libraries/SPTarkov.Server.Core/Utils/FileUtil.cs @@ -1,3 +1,4 @@ +using System.Security.AccessControl; using SPTarkov.DI.Annotations; namespace SPTarkov.Server.Core.Utils; @@ -61,8 +62,17 @@ public class FileUtil public string ReadFile(string path) { - using var reader = new StreamReader(path); - return reader.ReadToEnd(); + return File.ReadAllText(path); + } + + public async Task ReadFileAsync(string path) + { + return await File.ReadAllTextAsync(path); + } + + public async Task ReadFileAsBytesAsync(string path) + { + return await File.ReadAllBytesAsync(path); } public void WriteFile(string filePath, string fileContent) diff --git a/Libraries/SPTarkov.Server.Core/Utils/HashUtil.cs b/Libraries/SPTarkov.Server.Core/Utils/HashUtil.cs index 16e4f2e8..d3b46ada 100644 --- a/Libraries/SPTarkov.Server.Core/Utils/HashUtil.cs +++ b/Libraries/SPTarkov.Server.Core/Utils/HashUtil.cs @@ -13,6 +13,11 @@ public class HashUtil(RandomUtil _randomUtil) return Crc32.HashToUInt32(new ArraySegment(Encoding.UTF8.GetBytes(data))); } + public uint GenerateCrc32ForData(ReadOnlySpan data) + { + return Crc32.HashToUInt32(data); + } + /// /// Create a hash for the data parameter /// diff --git a/SPTarkov.Server/Services/SptServerBackgroundService.cs b/SPTarkov.Server/Services/SptServerBackgroundService.cs index 66bb97c3..1195c4c8 100644 --- a/SPTarkov.Server/Services/SptServerBackgroundService.cs +++ b/SPTarkov.Server/Services/SptServerBackgroundService.cs @@ -13,7 +13,7 @@ public class SptServerBackgroundService(IReadOnlyList loadedMods, Bundle { foreach (var mod in loadedMods) { - if (mod.ModMetadata?.IsBundleMod == true) + if (mod.ModMetadata.IsBundleMod == true) { await bundleLoader.LoadBundlesAsync(mod); }