Merge tag '4.0.8'
This commit is contained in:
+1
-1
@@ -1,7 +1,7 @@
|
||||
<Project>
|
||||
<PropertyGroup>
|
||||
<!-- SPT specific -->
|
||||
<SptVersion Condition="'$(SptVersion)' == ''">4.0.7</SptVersion>
|
||||
<SptVersion Condition="'$(SptVersion)' == ''">4.0.8</SptVersion>
|
||||
<SptCommit Condition="'$(SptCommit)' == ''">a12b34</SptCommit>
|
||||
<SptBuildTime Condition="'$(SptBuildTime)' == ''">0000000000</SptBuildTime>
|
||||
<SptBuildType Condition="'$(SptBuildType)' == ''">LOCAL</SptBuildType>
|
||||
|
||||
@@ -9929,9 +9929,10 @@
|
||||
"settings": {
|
||||
"adjustBotAppearances": true,
|
||||
"enableChristmasHideout": true,
|
||||
"enableSanta": true
|
||||
"enableSanta": true,
|
||||
"enableRundansEvent": true
|
||||
},
|
||||
"startDay": "12",
|
||||
"startDay": "13",
|
||||
"startMonth": "12",
|
||||
"type": "CHRISTMAS"
|
||||
},
|
||||
|
||||
@@ -287,13 +287,13 @@
|
||||
"name": "AUTUMN_LATE",
|
||||
"startDay": "1",
|
||||
"startMonth": "11",
|
||||
"endDay": "21",
|
||||
"endDay": "13",
|
||||
"endMonth": "12"
|
||||
},
|
||||
{
|
||||
"seasonType": 2,
|
||||
"name": "WINTER_START",
|
||||
"startDay": "21",
|
||||
"startDay": "13",
|
||||
"startMonth": "12",
|
||||
"endDay": "31",
|
||||
"endMonth": "12"
|
||||
|
||||
@@ -3215,7 +3215,6 @@
|
||||
"5ede474b0c226a66f5402622": 1,
|
||||
"5ede475339ee016e8c534742": 1,
|
||||
"5ede475b549eed7c6d5c18fb": 1,
|
||||
"5ede47641cf3836a88318df1": 1,
|
||||
"5f0c892565703e5c461894e9": 1
|
||||
},
|
||||
"Caliber46x30": {
|
||||
|
||||
@@ -2808,6 +2808,32 @@
|
||||
"6575ea6760703324250610de"
|
||||
]
|
||||
},
|
||||
"674d91ce6e862d5a95059ed6": {
|
||||
"Back_plate": [
|
||||
"656efaf54772930db4031ff5"
|
||||
],
|
||||
"Collar": [
|
||||
"6575ea719c7cad336508e418"
|
||||
],
|
||||
"Front_plate": [
|
||||
"656f611f94b480b8a500c0db"
|
||||
],
|
||||
"Groin": [
|
||||
"6575ea7c60703324250610e2"
|
||||
],
|
||||
"Soft_armor_back": [
|
||||
"6575ea4cf6a13a7b7100adc4"
|
||||
],
|
||||
"Soft_armor_front": [
|
||||
"6575ea3060703324250610da"
|
||||
],
|
||||
"Soft_armor_left": [
|
||||
"6575ea5cf6a13a7b7100adc8"
|
||||
],
|
||||
"soft_armor_right": [
|
||||
"6575ea6760703324250610de"
|
||||
]
|
||||
},
|
||||
"5c0e874186f7745dc7616606": {
|
||||
"Helmet_back": [
|
||||
"6571138e818110db4600aa71"
|
||||
@@ -2822,6 +2848,20 @@
|
||||
"5c0e842486f77443a74d2976"
|
||||
]
|
||||
},
|
||||
"6759af0f9c8a538dd70bfae6": {
|
||||
"Helmet_back": [
|
||||
"6571138e818110db4600aa71"
|
||||
],
|
||||
"Helmet_ears": [
|
||||
"657112fa818110db4600aa6b"
|
||||
],
|
||||
"Helmet_top": [
|
||||
"6571133d22996eaf11088200"
|
||||
],
|
||||
"mod_equipment": [
|
||||
"5c0e842486f77443a74d2976"
|
||||
]
|
||||
},
|
||||
"65ae4f57e343f0acc00824da": {
|
||||
"mod_foregrip": [
|
||||
"588226d124597767ad33f787",
|
||||
|
||||
@@ -3206,7 +3206,6 @@
|
||||
"5ede474b0c226a66f5402622": 1,
|
||||
"5ede475339ee016e8c534742": 1,
|
||||
"5ede475b549eed7c6d5c18fb": 1,
|
||||
"5ede47641cf3836a88318df1": 1,
|
||||
"5f0c892565703e5c461894e9": 1
|
||||
},
|
||||
"Caliber46x30": {
|
||||
|
||||
@@ -1626,7 +1626,7 @@
|
||||
"slotId": "hideout",
|
||||
"upd": {
|
||||
"MedKit": {
|
||||
"HpResource": 6
|
||||
"HpResource": 3
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -4784,7 +4784,7 @@
|
||||
"slotId": "hideout",
|
||||
"upd": {
|
||||
"MedKit": {
|
||||
"HpResource": 6
|
||||
"HpResource": 3
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -7798,7 +7798,7 @@
|
||||
"slotId": "hideout",
|
||||
"upd": {
|
||||
"MedKit": {
|
||||
"HpResource": 6
|
||||
"HpResource": 3
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -8396,7 +8396,7 @@
|
||||
"slotId": "hideout",
|
||||
"upd": {
|
||||
"MedKit": {
|
||||
"HpResource": 5
|
||||
"HpResource": 3
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -11638,7 +11638,7 @@
|
||||
"slotId": "hideout",
|
||||
"upd": {
|
||||
"MedKit": {
|
||||
"HpResource": 6
|
||||
"HpResource": 3
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -12127,7 +12127,7 @@
|
||||
"slotId": "hideout",
|
||||
"upd": {
|
||||
"MedKit": {
|
||||
"HpResource": 5
|
||||
"HpResource": 3
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -15071,7 +15071,7 @@
|
||||
"slotId": "hideout",
|
||||
"upd": {
|
||||
"MedKit": {
|
||||
"HpResource": 6
|
||||
"HpResource": 3
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -15664,7 +15664,7 @@
|
||||
"slotId": "hideout",
|
||||
"upd": {
|
||||
"MedKit": {
|
||||
"HpResource": 5
|
||||
"HpResource": 3
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -16089,7 +16089,7 @@
|
||||
"slotId": "hideout",
|
||||
"upd": {
|
||||
"MedKit": {
|
||||
"HpResource": 5
|
||||
"HpResource": 3
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -16106,7 +16106,7 @@
|
||||
"slotId": "hideout",
|
||||
"upd": {
|
||||
"MedKit": {
|
||||
"HpResource": 5
|
||||
"HpResource": 3
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -16138,7 +16138,7 @@
|
||||
"slotId": "hideout",
|
||||
"upd": {
|
||||
"MedKit": {
|
||||
"HpResource": 5
|
||||
"HpResource": 3
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -19394,7 +19394,7 @@
|
||||
"slotId": "hideout",
|
||||
"upd": {
|
||||
"MedKit": {
|
||||
"HpResource": 6
|
||||
"HpResource": 3
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -19980,7 +19980,7 @@
|
||||
"slotId": "hideout",
|
||||
"upd": {
|
||||
"MedKit": {
|
||||
"HpResource": 5
|
||||
"HpResource": 3
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -20056,7 +20056,7 @@
|
||||
"slotId": "hideout",
|
||||
"upd": {
|
||||
"MedKit": {
|
||||
"HpResource": 5
|
||||
"HpResource": 3
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -20073,7 +20073,7 @@
|
||||
"slotId": "hideout",
|
||||
"upd": {
|
||||
"MedKit": {
|
||||
"HpResource": 5
|
||||
"HpResource": 3
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -20238,7 +20238,7 @@
|
||||
"slotId": "hideout",
|
||||
"upd": {
|
||||
"MedKit": {
|
||||
"HpResource": 5
|
||||
"HpResource": 3
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
@@ -18,6 +18,7 @@ public class RagfairAssortGenerator(
|
||||
DatabaseService databaseService,
|
||||
PresetHelper presetHelper,
|
||||
SeasonalEventService seasonalEventService,
|
||||
ItemFilterService itemFilterService,
|
||||
ConfigServer configServer,
|
||||
ICloner cloner
|
||||
)
|
||||
@@ -44,7 +45,10 @@ public class RagfairAssortGenerator(
|
||||
IEnumerable<List<Item>> results = [];
|
||||
|
||||
// Get cloned items from db
|
||||
var dbItems = databaseService.GetItems().Where(item => !string.Equals(item.Value.Type, "Node", StringComparison.OrdinalIgnoreCase));
|
||||
var blacklist = itemFilterService.GetBlacklistedItems();
|
||||
var dbItems = databaseService
|
||||
.GetItems()
|
||||
.Where(item => !string.Equals(item.Value.Type, "Node", StringComparison.OrdinalIgnoreCase) && !blacklist.Contains(item.Key));
|
||||
|
||||
// Store processed preset tpls so we don't add them when processing non-preset items
|
||||
HashSet<MongoId> processedArmorItems = [];
|
||||
|
||||
@@ -125,6 +125,17 @@ public class PrestigeHelper(
|
||||
);
|
||||
}
|
||||
|
||||
// Copy over existing unlocked hideout customisation unlocks to new profile that player doesn't already have
|
||||
newProfile.CustomisationUnlocks ??= [];
|
||||
foreach (var oldUnlock in oldProfile.CustomisationUnlocks ?? [])
|
||||
{
|
||||
if (newProfile.CustomisationUnlocks.FirstOrDefault(unlock => unlock.Id == oldUnlock.Id) is null)
|
||||
{
|
||||
newProfile.CustomisationUnlocks.Add(oldUnlock);
|
||||
}
|
||||
}
|
||||
|
||||
// Set prestige level on new profile
|
||||
newProfile.CharacterData!.PmcData!.Info!.PrestigeLevel = prestige.PrestigeLevel;
|
||||
}
|
||||
|
||||
|
||||
@@ -65,6 +65,8 @@ public class BundleLoader(ISptLogger<BundleLoader> logger, JsonUtil jsonUtil, Bu
|
||||
|
||||
AddBundle(bundleManifest.Key, new BundleInfo(relativeModPath, bundleManifest, bundleHash));
|
||||
}
|
||||
|
||||
await bundleHashCacheService.WriteCache();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -140,6 +140,9 @@ public record SeasonalEventSettings
|
||||
|
||||
[JsonPropertyName("disableWaves")]
|
||||
public List<string>? DisableWaves { get; set; }
|
||||
|
||||
[JsonPropertyName("enableRundansEvent")]
|
||||
public bool? EnableRundansEvent { get; set; }
|
||||
}
|
||||
|
||||
public record ZombieSettings
|
||||
|
||||
@@ -30,6 +30,16 @@ public class BackupService
|
||||
protected readonly TimeUtil TimeUtil;
|
||||
protected readonly IReadOnlyList<SptMod> LoadedMods;
|
||||
|
||||
private static readonly CultureInfo[] Cultures =
|
||||
[
|
||||
CultureInfo.InvariantCulture,
|
||||
new CultureInfo("fa-IR") { DateTimeFormat = { Calendar = new PersianCalendar() } },
|
||||
new CultureInfo("ar-SA") { DateTimeFormat = { Calendar = new HijriCalendar() } },
|
||||
new CultureInfo("he-IL") { DateTimeFormat = { Calendar = new HebrewCalendar() } },
|
||||
new CultureInfo("th-TH") { DateTimeFormat = { Calendar = new ThaiBuddhistCalendar() } },
|
||||
new CultureInfo("ja-JP") { DateTimeFormat = { Calendar = new JapaneseCalendar() } },
|
||||
];
|
||||
|
||||
public BackupService(
|
||||
ISptLogger<BackupService> logger,
|
||||
IReadOnlyList<SptMod> loadedMods,
|
||||
@@ -233,9 +243,9 @@ public class BackupService
|
||||
}
|
||||
}
|
||||
|
||||
protected SortedDictionary<long, string> GetBackupPathsWithCreationTimestamp(IEnumerable<string> backupPaths)
|
||||
protected SortedDictionary<DateTime, string> GetBackupPathsWithCreationTimestamp(IEnumerable<string> backupPaths)
|
||||
{
|
||||
var result = new SortedDictionary<long, string>();
|
||||
var result = new SortedDictionary<DateTime, string>();
|
||||
foreach (var backupPath in backupPaths)
|
||||
{
|
||||
var date = ExtractDateFromFolderName(backupPath);
|
||||
@@ -244,7 +254,7 @@ public class BackupService
|
||||
continue;
|
||||
}
|
||||
|
||||
result.Add(date.Value.ToFileTimeUtc(), backupPath);
|
||||
result.Add(date.Value, backupPath);
|
||||
}
|
||||
|
||||
return result;
|
||||
@@ -297,7 +307,7 @@ public class BackupService
|
||||
return 0; // Skip comparison if either date is invalid.
|
||||
}
|
||||
|
||||
return (int)(dateA.Value.ToFileTimeUtc() - dateB.Value.ToFileTimeUtc());
|
||||
return dateA.Value.CompareTo(dateB.Value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -308,11 +318,29 @@ public class BackupService
|
||||
protected DateTime? ExtractDateFromFolderName(string folderPath)
|
||||
{
|
||||
var folderName = Path.GetFileName(folderPath);
|
||||
|
||||
const string format = "yyyy-MM-dd_HH-mm-ss";
|
||||
if (DateTime.TryParseExact(folderName, format, CultureInfo.InvariantCulture, DateTimeStyles.None, out var dateTime))
|
||||
|
||||
var now = DateTime.UtcNow;
|
||||
var minDate = new DateTime(1900, 1, 1, 0, 0, 0, DateTimeKind.Utc);
|
||||
var maxDate = now.AddYears(5);
|
||||
|
||||
foreach (var culture in Cultures)
|
||||
{
|
||||
return dateTime;
|
||||
if (
|
||||
DateTime.TryParseExact(
|
||||
folderName,
|
||||
format,
|
||||
culture,
|
||||
DateTimeStyles.AssumeUniversal | DateTimeStyles.AdjustToUniversal,
|
||||
out var dt
|
||||
)
|
||||
)
|
||||
{
|
||||
if (dt >= minDate && dt <= maxDate)
|
||||
{
|
||||
return DateTime.SpecifyKind(dt, DateTimeKind.Utc);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Logger.Warning($"Invalid backup folder name format: {folderPath}, [{folderName}]");
|
||||
|
||||
@@ -270,7 +270,7 @@ public class BotEquipmentFilterService(
|
||||
/// <returns>Filtered bot file</returns>
|
||||
protected void FilterCartridges(BotType baseBotNode, EquipmentFilterDetails? blacklist, EquipmentFilterDetails? whitelist)
|
||||
{
|
||||
if (whitelist is not null)
|
||||
if (whitelist is not null && whitelist.Cartridge is not null)
|
||||
{
|
||||
// Loop over each caliber + cartridges of that type
|
||||
foreach (var (caliber, cartridges) in baseBotNode.BotInventory.Ammo)
|
||||
@@ -281,16 +281,13 @@ public class BotEquipmentFilterService(
|
||||
continue;
|
||||
}
|
||||
|
||||
// Loop over each cartridge + weight
|
||||
// Clear all cartridges ready for whitelist to be added
|
||||
foreach (var ammoKvP in cartridges)
|
||||
// Cartridge not on whitelist
|
||||
// Get all cartridges that aren't on the whitelist
|
||||
var cartridgesToRemove = cartridges.Keys.Where(cartridge => !matchingWhitelist.Contains(cartridge)).ToList();
|
||||
|
||||
// Remove said cartridges from the original dictionary
|
||||
foreach (var cartridge in cartridgesToRemove)
|
||||
{
|
||||
if (!matchingWhitelist.Contains(ammoKvP.Key))
|
||||
// Remove
|
||||
{
|
||||
cartridges.Remove(ammoKvP.Key);
|
||||
}
|
||||
cartridges.Remove(cartridge);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using SPTarkov.DI.Annotations;
|
||||
using SPTarkov.Server.Core.Models.Eft.Profile;
|
||||
using SPTarkov.Server.Core.Models.Utils;
|
||||
using SPTarkov.Server.Core.Utils;
|
||||
|
||||
@@ -10,6 +11,7 @@ public class BundleHashCacheService(ISptLogger<BundleHashCacheService> logger, J
|
||||
protected const string _bundleHashCachePath = "./user/cache/";
|
||||
protected const string _cacheName = "bundleHashCache.json";
|
||||
protected Dictionary<string, uint> _bundleHashes = [];
|
||||
private readonly SemaphoreSlim _writeLock = new(1, 1);
|
||||
|
||||
public async Task HydrateCache()
|
||||
{
|
||||
@@ -29,6 +31,27 @@ public class BundleHashCacheService(ISptLogger<BundleHashCacheService> logger, J
|
||||
_bundleHashes = await jsonUtil.DeserializeFromFileAsync<Dictionary<string, uint>>(fullCachePath) ?? [];
|
||||
}
|
||||
|
||||
public async Task WriteCache()
|
||||
{
|
||||
await _writeLock.WaitAsync();
|
||||
|
||||
try
|
||||
{
|
||||
var bundleHashesSerialized = jsonUtil.Serialize(_bundleHashes);
|
||||
|
||||
if (bundleHashesSerialized is null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
await fileUtil.WriteFileAsync(Path.Join(_bundleHashCachePath, _cacheName), bundleHashesSerialized);
|
||||
}
|
||||
finally
|
||||
{
|
||||
_writeLock.Release();
|
||||
}
|
||||
}
|
||||
|
||||
protected uint GetStoredValue(string key)
|
||||
{
|
||||
if (!_bundleHashes.TryGetValue(key, out var value))
|
||||
@@ -43,16 +66,7 @@ public class BundleHashCacheService(ISptLogger<BundleHashCacheService> logger, J
|
||||
{
|
||||
_bundleHashes[bundlePath] = hash;
|
||||
|
||||
var bundleHashesSerialized = jsonUtil.Serialize(_bundleHashes);
|
||||
|
||||
if (bundleHashesSerialized is null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
await fileUtil.WriteFileAsync(Path.Join(_bundleHashCachePath, _cacheName), bundleHashesSerialized);
|
||||
|
||||
logger.Debug($"Bundle: {bundlePath} hash stored in: ${_bundleHashCachePath}");
|
||||
logger.Debug($"Bundle: {bundlePath} hash stored in cache");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -514,9 +514,20 @@ public class SeasonalEventService(
|
||||
AdjustBotAppearanceValues(eventType.Type);
|
||||
}
|
||||
|
||||
if (eventType.Settings?.EnableRundansEvent ?? false)
|
||||
{
|
||||
EnableRunnansEvent(databaseService.GetGlobals());
|
||||
}
|
||||
|
||||
ChangeBtrToTarColaSkin();
|
||||
}
|
||||
|
||||
protected void EnableRunnansEvent(Globals globals)
|
||||
{
|
||||
globals.Configuration.RunddansSettings.Active = true;
|
||||
globals.Configuration.RunddansSettings.ActivePVE = true;
|
||||
}
|
||||
|
||||
private void ChangeBtrToTarColaSkin()
|
||||
{
|
||||
var btrSettings = databaseService.GetGlobals().Configuration.BTRSettings;
|
||||
|
||||
@@ -55,6 +55,11 @@ public class DatabaseImporter(
|
||||
|
||||
var bsgPath = $"/{newBasePath}/{filePathNoExtension}".Replace("\\", "/");
|
||||
imageRouter.AddRoute(bsgPath, fileNameWithPath);
|
||||
|
||||
if (fileNameWithNoSPTPath.Contains("icon.ico"))
|
||||
{
|
||||
imageRouter.AddRoute("/favicon", fileNameWithPath);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -128,6 +128,7 @@ In Rider, after installing the CSharpier plugin:
|
||||
|
||||
We have a number of tests that are run automatically when you submit a pull request. You can run these tests locally by running The unit test sub-project. If you're adding a new feature or fixing a bug, please consider adding tests to cover your changes so that we can ensure they don't break in the future.
|
||||
|
||||
|
||||
## License
|
||||
|
||||
This project is licensed under the NCSA Open Source License. See the [LICENSE](LICENSE) file for details.
|
||||
|
||||
@@ -59,6 +59,7 @@ public static class Program
|
||||
"========================================================================================================="
|
||||
);
|
||||
|
||||
Console.ReadLine();
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user