Merge branch 'develop' of https://github.com/sp-tarkov/server-csharp into develop
This commit is contained in:
@@ -478,7 +478,7 @@ public class GameController(
|
||||
{
|
||||
if (
|
||||
fullProfile.SptData.Mods.Any(m =>
|
||||
m.Author == mod.PackageJson.Author && m.Version == mod.PackageJson.Version && m.Name == mod.PackageJson.Name
|
||||
m.Author == mod.ModMetadata.Author && m.Version == mod.ModMetadata.Version && m.Name == mod.ModMetadata.Name
|
||||
)
|
||||
)
|
||||
{
|
||||
@@ -489,10 +489,10 @@ public class GameController(
|
||||
fullProfile.SptData.Mods.Add(
|
||||
new ModDetails
|
||||
{
|
||||
Author = mod.PackageJson.Author,
|
||||
Version = mod.PackageJson.Version,
|
||||
Name = mod.PackageJson.Name,
|
||||
Url = mod.PackageJson.Url,
|
||||
Author = mod.ModMetadata.Author,
|
||||
Version = mod.ModMetadata.Version,
|
||||
Name = mod.ModMetadata.Name,
|
||||
Url = mod.ModMetadata.Url,
|
||||
DateAdded = _timeUtil.GetTimeStamp()
|
||||
}
|
||||
);
|
||||
|
||||
@@ -240,14 +240,14 @@ public class LauncherController(
|
||||
/// Get the mods the server has currently loaded
|
||||
/// </summary>
|
||||
/// <returns>Dictionary of mod name and mod details</returns>
|
||||
public Dictionary<string, PackageJsonData> GetLoadedServerMods()
|
||||
public Dictionary<string, AbstractModMetadata> GetLoadedServerMods()
|
||||
{
|
||||
var mods = _applicationContext?.GetLatestValue(ContextVariableType.LOADED_MOD_ASSEMBLIES).GetValue<List<SptMod>>();
|
||||
var result = new Dictionary<string, PackageJsonData>();
|
||||
var result = new Dictionary<string, AbstractModMetadata>();
|
||||
|
||||
foreach (var sptMod in mods)
|
||||
{
|
||||
result.Add(sptMod.PackageJson.Name, sptMod.PackageJson);
|
||||
result.Add(sptMod.ModMetadata.Name, sptMod.ModMetadata);
|
||||
}
|
||||
|
||||
return result;
|
||||
|
||||
@@ -156,14 +156,14 @@ public class LauncherV2Controller(
|
||||
/// Gets the Servers loaded mods.
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public Dictionary<string, PackageJsonData> LoadedMods()
|
||||
public Dictionary<string, AbstractModMetadata> LoadedMods()
|
||||
{
|
||||
var mods = _applicationContext?.GetLatestValue(ContextVariableType.LOADED_MOD_ASSEMBLIES).GetValue<List<SptMod>>();
|
||||
var result = new Dictionary<string, PackageJsonData>();
|
||||
var result = new Dictionary<string, AbstractModMetadata>();
|
||||
|
||||
foreach (var sptMod in mods)
|
||||
{
|
||||
result.Add(sptMod.PackageJson.Name, sptMod.PackageJson);
|
||||
result.Add(sptMod.ModMetadata.Name, sptMod.ModMetadata);
|
||||
}
|
||||
|
||||
return result;
|
||||
|
||||
@@ -5,7 +5,7 @@ namespace SPTarkov.Server.Core.Models.Spt.Launcher;
|
||||
|
||||
public class LauncherV2ModsResponse : IRequestData
|
||||
{
|
||||
public required Dictionary<string, PackageJsonData> Response
|
||||
public required Dictionary<string, AbstractModMetadata> Response
|
||||
{
|
||||
get;
|
||||
set;
|
||||
|
||||
@@ -0,0 +1,119 @@
|
||||
namespace SPTarkov.Server.Core.Models.Spt.Mod;
|
||||
|
||||
/// <summary>
|
||||
/// Represents a collection of metadata used to determine things such as author, version,
|
||||
/// pre-defined load order and incompatibilities. This record is required to be overriden by all mods.
|
||||
/// All properties must be overridden. For properties, that you don't need, just assign null.
|
||||
/// </summary>
|
||||
public abstract record AbstractModMetadata
|
||||
{
|
||||
/// <summary>
|
||||
/// Name of this mod
|
||||
/// </summary>
|
||||
public abstract string? Name
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Your username
|
||||
/// </summary>
|
||||
public abstract string? Author
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// People who have contributed to this mod
|
||||
/// </summary>
|
||||
public abstract List<string>? Contributors
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Semantic version of this mod, this uses the semver standard
|
||||
/// </summary>
|
||||
public abstract string? Version
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// SPT version this mod was built for
|
||||
/// </summary>
|
||||
public abstract string? SptVersion
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// List of mods this mod should load before
|
||||
/// </summary>
|
||||
public abstract List<string>? LoadBefore
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// List of mods this mod should load after
|
||||
/// </summary>
|
||||
public abstract List<string>? LoadAfter
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// List of mods not compatible with this mod
|
||||
/// </summary>
|
||||
public abstract List<string>? Incompatibilities
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Dictionary of mods this mod depends on.
|
||||
///
|
||||
/// Mod dependency is the key, version is the value
|
||||
/// </summary>
|
||||
public abstract Dictionary<string, string>? ModDependencies
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Link to this mod's mod page, or GitHub page
|
||||
/// </summary>
|
||||
public abstract string? Url
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Does this mod load bundles
|
||||
/// </summary>
|
||||
public abstract bool? IsBundleMod
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Name of the license this mod uses
|
||||
/// </summary>
|
||||
public abstract string? Licence
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
}
|
||||
@@ -1,90 +0,0 @@
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace SPTarkov.Server.Core.Models.Spt.Mod;
|
||||
|
||||
public record PackageJsonData
|
||||
{
|
||||
[JsonPropertyName("name")]
|
||||
public string? Name
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
[JsonPropertyName("author")]
|
||||
public string? Author
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
[JsonPropertyName("contributors")]
|
||||
public List<string>? Contributors
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
[JsonPropertyName("version")]
|
||||
public string? Version
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
[JsonPropertyName("sptVersion")]
|
||||
public string? SptVersion
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
[JsonPropertyName("loadBefore")]
|
||||
public List<string>? LoadBefore
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
[JsonPropertyName("loadAfter")]
|
||||
public List<string>? LoadAfter
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
[JsonPropertyName("incompatibilities")]
|
||||
public List<string>? Incompatibilities
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
[JsonPropertyName("modDependencies")]
|
||||
public Dictionary<string, string>? ModDependencies
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
[JsonPropertyName("url")]
|
||||
public string? Url
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
[JsonPropertyName("isBundleMod")]
|
||||
public bool? IsBundleMod
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
[JsonPropertyName("licence")]
|
||||
public string? Licence
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
}
|
||||
@@ -12,8 +12,8 @@ public class SptMod
|
||||
set;
|
||||
}
|
||||
|
||||
[JsonPropertyName("packageJson")]
|
||||
public PackageJsonData? PackageJson
|
||||
[JsonPropertyName("modMetadata")]
|
||||
public AbstractModMetadata? ModMetadata
|
||||
{
|
||||
get;
|
||||
set;
|
||||
|
||||
@@ -316,7 +316,7 @@ public class BackupService
|
||||
|
||||
foreach (var mod in mods)
|
||||
{
|
||||
result.Add($"{mod.PackageJson.Author} - {mod.PackageJson.Version ?? ""}");
|
||||
result.Add($"{mod.ModMetadata.Author} - {mod.ModMetadata.Version ?? ""}");
|
||||
}
|
||||
|
||||
return result;
|
||||
|
||||
@@ -908,7 +908,7 @@ public class LocationLifecycleService
|
||||
{
|
||||
// Exclude completed quests
|
||||
var activeQuestIdsInProfile = profileQuests
|
||||
.Where(quest => quest.Status is QuestStatusEnum.AvailableForStart or QuestStatusEnum.Success)
|
||||
.Where(quest => quest.Status is not QuestStatusEnum.AvailableForStart and not QuestStatusEnum.Success)
|
||||
.Select(status => status.QId);
|
||||
|
||||
// Get db details of quests we found above
|
||||
|
||||
@@ -28,7 +28,7 @@ public partial class HashUtil(RandomUtil _randomUtil)
|
||||
objectId[3] = (byte) timestamp;
|
||||
|
||||
// Random value (5 bytes)
|
||||
_randomUtil.Random.NextBytes(objectId.Slice(4, 5));
|
||||
_randomUtil.NextBytes(objectId.Slice(4, 5));
|
||||
|
||||
// Incrementing counter (3 bytes)
|
||||
// 24-bit counter
|
||||
|
||||
+13
@@ -18,6 +18,7 @@ namespace SPTarkov.Server.Core.Utils.Json.Converters;
|
||||
|
||||
public class BaseInteractionRequestDataConverter : JsonConverter<BaseInteractionRequestData>
|
||||
{
|
||||
private static Dictionary<string, Func<string, BaseInteractionRequestData?>> _modHandlers = new();
|
||||
public override BaseInteractionRequestData? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
|
||||
{
|
||||
using var jsonDocument = JsonDocument.ParseValue(ref reader);
|
||||
@@ -177,12 +178,24 @@ public class BaseInteractionRequestDataConverter : JsonConverter<BaseInteraction
|
||||
case ItemEventActions.PIN_LOCK:
|
||||
return JsonSerializer.Deserialize<PinOrLockItemRequest>(jsonText);
|
||||
default:
|
||||
if (_modHandlers.TryGetValue(action, out var handler))
|
||||
{
|
||||
return handler(jsonText);
|
||||
}
|
||||
throw new Exception(
|
||||
$"Unhandled action type {action}, make sure the BaseInteractionRequestDataConverter has the deserialization for this action handled."
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
public static void RegisterModDataHandler(string action, Func<string, BaseInteractionRequestData?> handler)
|
||||
{
|
||||
if (!_modHandlers.TryAdd(action, handler))
|
||||
{
|
||||
throw new Exception($"Unable to register action {action} to BaseInteractionRequestDataConverter as it already exists.");
|
||||
}
|
||||
}
|
||||
|
||||
public override void Write(Utf8JsonWriter writer, BaseInteractionRequestData value, JsonSerializerOptions options)
|
||||
{
|
||||
JsonSerializer.Serialize(writer, value, options);
|
||||
|
||||
@@ -35,7 +35,7 @@ public class RandomUtil(ISptLogger<RandomUtil> _logger, ICloner _cloner)
|
||||
max -= 1;
|
||||
}
|
||||
|
||||
return max > min ? Random.Next(min, exclusive ? max : max + 1) : min;
|
||||
return max > min ? Random.Shared.Next(min, exclusive ? max : max + 1) : min;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -64,6 +64,11 @@ public class RandomUtil(ISptLogger<RandomUtil> _logger, ICloner _cloner)
|
||||
return Random.Next(0, 2) == 1;
|
||||
}
|
||||
|
||||
public void NextBytes(Span<byte> bytes)
|
||||
{
|
||||
Random.Shared.NextBytes(bytes);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Calculates the percentage of a given number and returns the result.
|
||||
/// </summary>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
using System.Reflection;
|
||||
using System.Runtime.Loader;
|
||||
using System.Text.Json;
|
||||
using SPTarkov.Server.Core.Models.Spt.Mod;
|
||||
using SPTarkov.Server.Core.Utils;
|
||||
|
||||
@@ -25,9 +25,8 @@ public class ModDllLoader
|
||||
|
||||
// foreach directory in /user/mods/
|
||||
// treat this as the MOD
|
||||
// should contain a dll and Package.json
|
||||
// load both
|
||||
// if either is missing Throw Warning and skip
|
||||
// should contain a dll
|
||||
// if dll is missing Throw Warning and skip
|
||||
|
||||
var modDirectories = Directory.GetDirectories(ModPath);
|
||||
|
||||
@@ -48,7 +47,7 @@ public class ModDllLoader
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Check the provided directory path for a dll and package.json file, load into memory
|
||||
/// Check the provided directory path for a dll, load into memory
|
||||
/// </summary>
|
||||
/// <param name="path">Directory path that contains mod files</param>
|
||||
/// <returns>SptMod</returns>
|
||||
@@ -60,24 +59,8 @@ public class ModDllLoader
|
||||
Assemblies = []
|
||||
};
|
||||
var assemblyCount = 0;
|
||||
var packageJsonCount = 0;
|
||||
foreach (var file in new DirectoryInfo(path).GetFiles()) // Only search top level
|
||||
{
|
||||
if (string.Equals(file.Name, "package.json", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
packageJsonCount++;
|
||||
|
||||
// Handle package.json
|
||||
var rawJson = File.ReadAllText(file.FullName);
|
||||
result.PackageJson = JsonSerializer.Deserialize<PackageJsonData>(rawJson);
|
||||
if (packageJsonCount > 1)
|
||||
{
|
||||
throw new Exception($"More than one package.json file found in path: {path}");
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
if (string.Equals(file.Extension, ".dll", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
assemblyCount++;
|
||||
@@ -85,26 +68,55 @@ public class ModDllLoader
|
||||
}
|
||||
}
|
||||
|
||||
if (assemblyCount == 0 && packageJsonCount == 0)
|
||||
{
|
||||
throw new Exception($"No Assembly or package.json found in: {Path.GetFullPath(path)}");
|
||||
}
|
||||
|
||||
if (packageJsonCount == 0)
|
||||
{
|
||||
throw new Exception($"No package.json found in path: {Path.GetFullPath(path)}");
|
||||
}
|
||||
|
||||
if (assemblyCount == 0)
|
||||
{
|
||||
throw new Exception($"No Assemblies found in path: {Path.GetFullPath(path)}");
|
||||
}
|
||||
|
||||
if (result.PackageJson?.Name == null || result.PackageJson?.Author == null ||
|
||||
result.PackageJson?.Version == null || result.PackageJson?.Licence == null ||
|
||||
result.PackageJson?.SptVersion == null)
|
||||
result.ModMetadata = LoadModMetadata(result.Assemblies, path);
|
||||
|
||||
if (result.ModMetadata == null)
|
||||
{
|
||||
throw new Exception($"The package.json file for: {path} is missing one of these properties: name, author, licence, version or sptVersion");
|
||||
throw new Exception($"Failed to load mod metadata for: {Path.GetFullPath(path)} \ndid you override `AbstractModMetadata`?");
|
||||
}
|
||||
|
||||
if (result.ModMetadata?.Name == null || result.ModMetadata?.Author == null ||
|
||||
result.ModMetadata?.Version == null || result.ModMetadata?.Licence == null ||
|
||||
result.ModMetadata?.SptVersion == null)
|
||||
{
|
||||
throw new Exception($"The mod metadata for: {Path.GetFullPath(path)} is missing one of these properties: name, author, licence, version or sptVersion");
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Finds and returns the mod metadata for this mod
|
||||
/// </summary>
|
||||
/// <param name="assemblies">All mod assemblies</param>
|
||||
/// <param name="path">Path of the mod directory</param>
|
||||
/// <returns>Mod metadata</returns>
|
||||
/// <exception cref="Exception">Thrown if duplicate metadata implementations are found</exception>
|
||||
private static AbstractModMetadata? LoadModMetadata(List<Assembly> assemblies, string path)
|
||||
{
|
||||
AbstractModMetadata? result = null;
|
||||
|
||||
foreach (var allAsmModules in assemblies.Select(a => a.Modules))
|
||||
{
|
||||
foreach (var module in allAsmModules)
|
||||
{
|
||||
var modMetadata = module.GetTypes().SingleOrDefault(t => typeof(AbstractModMetadata).IsAssignableFrom(t));
|
||||
|
||||
if (result != null && modMetadata != null)
|
||||
{
|
||||
throw new Exception($"Duplicate mod metadata found for mod at path: {Path.GetFullPath(path)}");
|
||||
}
|
||||
|
||||
if (modMetadata != null)
|
||||
{
|
||||
result = (AbstractModMetadata)Activator.CreateInstance(modMetadata)!;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
|
||||
@@ -5,15 +5,15 @@ namespace SPTarkov.Server.Modding;
|
||||
|
||||
public class ModLoadOrder(ICloner cloner)
|
||||
{
|
||||
protected Dictionary<string, PackageJsonData> loadOrder = new();
|
||||
protected Dictionary<string, PackageJsonData> mods = new();
|
||||
protected Dictionary<string, PackageJsonData> modsAvailable = new();
|
||||
protected Dictionary<string, AbstractModMetadata> loadOrder = new();
|
||||
protected Dictionary<string, AbstractModMetadata> mods = new();
|
||||
protected Dictionary<string, AbstractModMetadata> modsAvailable = new();
|
||||
|
||||
public Dictionary<string, PackageJsonData> SetModList(Dictionary<string, PackageJsonData> mods)
|
||||
public Dictionary<string, AbstractModMetadata> SetModList(Dictionary<string, AbstractModMetadata> mods)
|
||||
{
|
||||
this.mods = mods;
|
||||
modsAvailable = cloner.Clone(this.mods);
|
||||
loadOrder = new Dictionary<string, PackageJsonData>();
|
||||
loadOrder = new Dictionary<string, AbstractModMetadata>();
|
||||
|
||||
var visited = new HashSet<string>();
|
||||
|
||||
|
||||
@@ -32,7 +32,7 @@ public class ModValidator(
|
||||
{
|
||||
ValidateMods(mods);
|
||||
|
||||
var sortedModLoadOrder = modLoadOrder.SetModList(imported.ToDictionary(m => m.Key, m => m.Value.PackageJson));
|
||||
var sortedModLoadOrder = modLoadOrder.SetModList(imported.ToDictionary(m => m.Key, m => m.Value.ModMetadata));
|
||||
var finalList = new List<SptMod>();
|
||||
foreach (var orderMod in SortModsLoadOrder())
|
||||
{
|
||||
@@ -90,7 +90,7 @@ public class ModValidator(
|
||||
// Validate and remove broken mods from mod list
|
||||
var validMods = GetValidMods(mods);
|
||||
|
||||
var modPackageData = validMods.ToDictionary(m => m.PackageJson!.Name!, m => m.PackageJson!);
|
||||
var modPackageData = validMods.ToDictionary(m => m.ModMetadata!.Name!, m => m.ModMetadata!);
|
||||
CheckForDuplicateMods(modPackageData);
|
||||
|
||||
// Used to check all errors before stopping the load execution
|
||||
@@ -145,7 +145,7 @@ public class ModValidator(
|
||||
// add mods
|
||||
foreach (var mod in validMods)
|
||||
{
|
||||
if (ShouldSkipMod(mod.PackageJson))
|
||||
if (ShouldSkipMod(mod.ModMetadata))
|
||||
{
|
||||
logger.Warning(localisationService.GetText("modloader-skipped_mod", new
|
||||
{
|
||||
@@ -161,15 +161,15 @@ public class ModValidator(
|
||||
protected int SortMods(SptMod prev, SptMod next, Dictionary<string, bool> missingFromOrderJson)
|
||||
{
|
||||
// mod is not on the list, move the mod to last
|
||||
if (!order.TryGetValue(prev.PackageJson!.Name!, out var previndex))
|
||||
if (!order.TryGetValue(prev.ModMetadata!.Name!, out var previndex))
|
||||
{
|
||||
missingFromOrderJson[prev.PackageJson.Name!] = true;
|
||||
missingFromOrderJson[prev.ModMetadata.Name!] = true;
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (!order.TryGetValue(next.PackageJson!.Name!, out var nextindex))
|
||||
if (!order.TryGetValue(next.ModMetadata!.Name!, out var nextindex))
|
||||
{
|
||||
missingFromOrderJson[next.PackageJson.Name!] = true;
|
||||
missingFromOrderJson[next.ModMetadata.Name!] = true;
|
||||
return -1;
|
||||
}
|
||||
|
||||
@@ -180,9 +180,9 @@ public class ModValidator(
|
||||
/// Check for duplicate mods loaded, show error if any
|
||||
/// </summary>
|
||||
/// <param name="modPackageData">Dictionary of mod package.json data</param>
|
||||
protected void CheckForDuplicateMods(Dictionary<string, PackageJsonData> modPackageData)
|
||||
protected void CheckForDuplicateMods(Dictionary<string, AbstractModMetadata> modPackageData)
|
||||
{
|
||||
var groupedMods = new Dictionary<string, List<PackageJsonData>>();
|
||||
var groupedMods = new Dictionary<string, List<AbstractModMetadata>>();
|
||||
|
||||
foreach (var mod in modPackageData.Values)
|
||||
{
|
||||
@@ -219,7 +219,7 @@ public class ModValidator(
|
||||
/// </summary>
|
||||
/// <param name="mod">Mod to check compatibility with SPT</param>
|
||||
/// <returns>True if compatible</returns>
|
||||
protected bool IsModCompatibleWithSpt(PackageJsonData mod)
|
||||
protected bool IsModCompatibleWithSpt(AbstractModMetadata mod)
|
||||
{
|
||||
var sptVersion = ProgramStatics.SPT_VERSION() ?? sptConfig.SptVersion;
|
||||
var modName = $"{mod.Author}-{mod.Name}";
|
||||
@@ -272,13 +272,13 @@ public class ModValidator(
|
||||
protected void AddMod(SptMod mod)
|
||||
{
|
||||
// Add mod to imported list
|
||||
imported.Add(mod.PackageJson.Name, mod);
|
||||
imported.Add(mod.ModMetadata.Name, mod);
|
||||
logger.Info(
|
||||
localisationService.GetText("modloader-loaded_mod", new
|
||||
{
|
||||
name = mod.PackageJson.Name,
|
||||
version = mod.PackageJson.Version,
|
||||
author = mod.PackageJson.Author
|
||||
name = mod.ModMetadata.Name,
|
||||
version = mod.ModMetadata.Version,
|
||||
author = mod.ModMetadata.Author
|
||||
})
|
||||
);
|
||||
}
|
||||
@@ -288,12 +288,12 @@ public class ModValidator(
|
||||
/// </summary>
|
||||
/// <param name="pkg">mod package.json data</param>
|
||||
/// <returns></returns>
|
||||
protected bool ShouldSkipMod(PackageJsonData pkg)
|
||||
protected bool ShouldSkipMod(AbstractModMetadata pkg)
|
||||
{
|
||||
return skippedMods.Contains($"{pkg.Author}-{pkg.Name}");
|
||||
}
|
||||
|
||||
protected bool AreModDependenciesFulfilled(PackageJsonData pkg, Dictionary<string, PackageJsonData> loadedMods)
|
||||
protected bool AreModDependenciesFulfilled(AbstractModMetadata pkg, Dictionary<string, AbstractModMetadata> loadedMods)
|
||||
{
|
||||
if (pkg.ModDependencies == null)
|
||||
{
|
||||
@@ -336,7 +336,7 @@ public class ModValidator(
|
||||
return true;
|
||||
}
|
||||
|
||||
protected bool IsModCompatible(PackageJsonData mod, Dictionary<string, PackageJsonData> loadedMods)
|
||||
protected bool IsModCompatible(AbstractModMetadata mod, Dictionary<string, AbstractModMetadata> loadedMods)
|
||||
{
|
||||
var incompatbileModsList = mod.Incompatibilities;
|
||||
if (incompatbileModsList == null)
|
||||
@@ -371,7 +371,7 @@ public class ModValidator(
|
||||
/// <returns>true if valid</returns>
|
||||
protected bool ValidMod(SptMod mod)
|
||||
{
|
||||
var modName = mod.PackageJson.Name;
|
||||
var modName = mod.ModMetadata.Name;
|
||||
var modPath = GetModPath(modName);
|
||||
|
||||
var modIsCalledBepinEx = string.Equals(modName, "bepinex", StringComparison.OrdinalIgnoreCase);
|
||||
@@ -402,7 +402,7 @@ public class ModValidator(
|
||||
}
|
||||
|
||||
// Validate mod
|
||||
var config = mod.PackageJson;
|
||||
var config = mod.ModMetadata;
|
||||
var issue = false;
|
||||
|
||||
if (!semVer.IsValid(config.Version))
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
using System.Collections.Concurrent;
|
||||
using System.Diagnostics;
|
||||
using SPTarkov.Server.Core.Utils;
|
||||
using SPTarkov.Server.Core.Utils.Cloners;
|
||||
using UnitTests.Mock;
|
||||
@@ -63,4 +65,30 @@ public class HashUtilTests
|
||||
failMessage
|
||||
);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void MultiThreadedMongoIDGenerationTest()
|
||||
{
|
||||
var concurrentBag = new ConcurrentBag<string>();
|
||||
var random = new Random();
|
||||
var stopwatch = new Stopwatch();
|
||||
stopwatch.Start();
|
||||
|
||||
Parallel.For(0, 1000, i =>
|
||||
{
|
||||
Thread.Sleep(random.Next(0, 10));
|
||||
var mongoId = _hashUtil.Generate();
|
||||
concurrentBag.Add(mongoId);
|
||||
});
|
||||
|
||||
stopwatch.Stop();
|
||||
Console.WriteLine($"Elapsed time: {stopwatch.ElapsedMilliseconds} ms");
|
||||
var uniqueCount = concurrentBag.Distinct().Count();
|
||||
var totalCount = concurrentBag.Count;
|
||||
Assert.AreEqual(
|
||||
totalCount,
|
||||
uniqueCount,
|
||||
$"Expected all generated MongoId's to be unique, but found {totalCount - uniqueCount} duplicates."
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user