Bundle loader refactor (#502)
* Bundle loader refactor - Made async - Validate if bundle actually exists, if not throw warning into the console - Updated mod example * Cleanup unused var
This commit is contained in:
@@ -0,0 +1,23 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using SPTarkov.Server.Core.Models.Spt.Mod;
|
||||
|
||||
namespace SPTarkov.Server.Core.Extensions
|
||||
{
|
||||
public static class SptModExtensions
|
||||
{
|
||||
public static string GetModPath(this SptMod sptMod)
|
||||
{
|
||||
var relativeModPath = Path.GetRelativePath(
|
||||
Directory.GetCurrentDirectory(),
|
||||
sptMod.Directory
|
||||
)
|
||||
.Replace('\\', '/');
|
||||
|
||||
return relativeModPath;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,9 +1,10 @@
|
||||
using System.Text.Json.Serialization;
|
||||
using SPTarkov.DI.Annotations;
|
||||
using SPTarkov.Server.Core.Extensions;
|
||||
using SPTarkov.Server.Core.Models.Spt.Mod;
|
||||
using SPTarkov.Server.Core.Models.Utils;
|
||||
using SPTarkov.Server.Core.Services;
|
||||
using SPTarkov.Server.Core.Utils;
|
||||
using SPTarkov.Server.Core.Utils.Cloners;
|
||||
|
||||
namespace SPTarkov.Server.Core.Loaders;
|
||||
|
||||
@@ -18,40 +19,66 @@ namespace SPTarkov.Server.Core.Loaders;
|
||||
"Crc" : 1030040371,
|
||||
"Dependencies" : [ ]
|
||||
} */
|
||||
public class BundleInfo
|
||||
public class BundleInfo(string modPath, BundleManifestEntry bundle, uint bundleHash)
|
||||
{
|
||||
public BundleInfo() { }
|
||||
public string ModPath { get; private set; } = modPath;
|
||||
|
||||
public BundleInfo(string modPath, BundleManifestEntry bundle, uint bundleHash)
|
||||
{
|
||||
ModPath = modPath;
|
||||
FileName = bundle.Key;
|
||||
Bundle = bundle;
|
||||
Crc = bundleHash;
|
||||
Dependencies = bundle?.DependencyKeys ?? [];
|
||||
}
|
||||
public string FileName { get; private set; } = bundle.Key;
|
||||
|
||||
public string? ModPath { get; set; }
|
||||
public BundleManifestEntry Bundle { get; private set; } = bundle;
|
||||
|
||||
public string FileName { get; set; }
|
||||
public uint Crc { get; private set; } = bundleHash;
|
||||
|
||||
public BundleManifestEntry Bundle { get; set; }
|
||||
|
||||
public uint Crc { get; set; }
|
||||
|
||||
public List<string> Dependencies { get; set; }
|
||||
public List<string> Dependencies { get; private set; } = bundle?.DependencyKeys ?? [];
|
||||
}
|
||||
|
||||
[Injectable(InjectionType.Singleton)]
|
||||
public class BundleLoader(
|
||||
ISptLogger<BundleLoader> logger,
|
||||
JsonUtil jsonUtil,
|
||||
FileUtil fileUtil,
|
||||
BundleHashCacheService bundleHashCacheService,
|
||||
ICloner cloner
|
||||
BundleHashCacheService bundleHashCacheService
|
||||
)
|
||||
{
|
||||
private readonly Dictionary<string, BundleInfo> _bundles = new();
|
||||
private readonly Dictionary<string, BundleInfo> _bundles = [];
|
||||
|
||||
public async Task LoadBundlesAsync(SptMod mod)
|
||||
{
|
||||
var modPath = mod.GetModPath();
|
||||
|
||||
var modBundles = await jsonUtil.DeserializeFromFileAsync<BundleManifest>(
|
||||
Path.Join(Directory.GetCurrentDirectory(), modPath, "bundles.json")
|
||||
);
|
||||
|
||||
var bundleManifests = modBundles?.Manifest ?? [];
|
||||
|
||||
foreach (var bundleManifest in bundleManifests)
|
||||
{
|
||||
var relativeModPath = modPath.Replace('\\', '/');
|
||||
|
||||
var bundleLocalPath = Path.Join(relativeModPath, "bundles", bundleManifest.Key)
|
||||
.Replace('\\', '/');
|
||||
|
||||
if (!File.Exists(bundleLocalPath))
|
||||
{
|
||||
logger.Warning(
|
||||
$"Could not find bundle {bundleManifest.Key} for mod {mod.ModMetadata.Name}"
|
||||
);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!bundleHashCacheService.CalculateAndMatchHash(bundleLocalPath))
|
||||
{
|
||||
await bundleHashCacheService.CalculateAndStoreHash(bundleLocalPath);
|
||||
}
|
||||
|
||||
var bundleHash = bundleHashCacheService.GetStoredValue(bundleLocalPath);
|
||||
|
||||
AddBundle(
|
||||
bundleManifest.Key,
|
||||
new BundleInfo(relativeModPath, bundleManifest, bundleHash)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handle singleplayer/bundles
|
||||
@@ -71,39 +98,7 @@ public class BundleLoader(
|
||||
|
||||
public BundleInfo? GetBundle(string bundleKey)
|
||||
{
|
||||
return cloner.Clone(_bundles.GetValueOrDefault(bundleKey));
|
||||
}
|
||||
|
||||
public void AddBundles(string modPath)
|
||||
{
|
||||
// modPath should be relative to the server exe - ./user/mods/Mod3
|
||||
// TODO: make sure the mod is passing a path that is relative from the server exe
|
||||
|
||||
var modBundlesJson = fileUtil.ReadFile(
|
||||
Path.Join(Directory.GetCurrentDirectory(), modPath, "bundles.json")
|
||||
);
|
||||
var modBundles = jsonUtil.Deserialize<BundleManifest>(modBundlesJson);
|
||||
var bundleManifestArr = modBundles?.Manifest;
|
||||
|
||||
foreach (var bundleManifest in bundleManifestArr)
|
||||
{
|
||||
var relativeModPath = modPath.Replace('\\', '/');
|
||||
|
||||
var bundleLocalPath = Path.Join(relativeModPath, "bundles", bundleManifest.Key)
|
||||
.Replace('\\', '/');
|
||||
|
||||
if (!bundleHashCacheService.CalculateAndMatchHash(bundleLocalPath))
|
||||
{
|
||||
bundleHashCacheService.CalculateAndStoreHash(bundleLocalPath);
|
||||
}
|
||||
|
||||
var bundleHash = bundleHashCacheService.GetStoredValue(bundleLocalPath);
|
||||
|
||||
AddBundle(
|
||||
bundleManifest.Key,
|
||||
new BundleInfo(relativeModPath, bundleManifest, bundleHash)
|
||||
);
|
||||
}
|
||||
return _bundles.GetValueOrDefault(bundleKey);
|
||||
}
|
||||
|
||||
public void AddBundle(string key, BundleInfo bundle)
|
||||
@@ -119,13 +114,13 @@ public class BundleLoader(
|
||||
public record BundleManifest
|
||||
{
|
||||
[JsonPropertyName("manifest")]
|
||||
public List<BundleManifestEntry> Manifest { get; set; }
|
||||
public List<BundleManifestEntry>? Manifest { get; set; }
|
||||
}
|
||||
|
||||
public record BundleManifestEntry
|
||||
{
|
||||
[JsonPropertyName("key")]
|
||||
public string Key { get; set; }
|
||||
public required string Key { get; set; }
|
||||
|
||||
[JsonPropertyName("dependencyKeys")]
|
||||
public List<string>? DependencyKeys { get; set; }
|
||||
|
||||
@@ -14,7 +14,7 @@ public class BundleHashCacheService(
|
||||
{
|
||||
protected const string _bundleHashCachePath = "./user/cache/";
|
||||
protected const string _cacheName = "bundleHashCache.json";
|
||||
protected readonly Dictionary<string, uint> _bundleHashes = new();
|
||||
protected readonly Dictionary<string, uint> _bundleHashes = [];
|
||||
|
||||
public uint GetStoredValue(string key)
|
||||
{
|
||||
@@ -26,7 +26,7 @@ public class BundleHashCacheService(
|
||||
return value;
|
||||
}
|
||||
|
||||
public void StoreValue(string bundlePath, uint hash)
|
||||
public async Task StoreValue(string bundlePath, uint hash)
|
||||
{
|
||||
_bundleHashes.Add(bundlePath, hash);
|
||||
|
||||
@@ -35,7 +35,7 @@ public class BundleHashCacheService(
|
||||
Directory.CreateDirectory(_bundleHashCachePath);
|
||||
}
|
||||
|
||||
fileUtil.WriteFile(
|
||||
await fileUtil.WriteFileAsync(
|
||||
Path.Join(_bundleHashCachePath, _cacheName),
|
||||
jsonUtil.Serialize(_bundleHashes)
|
||||
);
|
||||
@@ -48,9 +48,9 @@ public class BundleHashCacheService(
|
||||
return MatchWithStoredHash(BundlePath, CalculateHash(BundlePath));
|
||||
}
|
||||
|
||||
public void CalculateAndStoreHash(string BundlePath)
|
||||
public async Task CalculateAndStoreHash(string BundlePath)
|
||||
{
|
||||
StoreValue(BundlePath, CalculateHash(BundlePath));
|
||||
await StoreValue(BundlePath, CalculateHash(BundlePath));
|
||||
}
|
||||
|
||||
public uint CalculateHash(string BundlePath)
|
||||
|
||||
@@ -19,14 +19,7 @@ public class SptServerBackgroundService(
|
||||
{
|
||||
if (mod.ModMetadata?.IsBundleMod == true)
|
||||
{
|
||||
// Convert to relative path
|
||||
var relativeModPath = Path.GetRelativePath(
|
||||
Directory.GetCurrentDirectory(),
|
||||
mod.Directory
|
||||
)
|
||||
.Replace('\\', '/');
|
||||
|
||||
bundleLoader.AddBundles(relativeModPath);
|
||||
await bundleLoader.LoadBundlesAsync(mod);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user