diff --git a/.dockerignore b/.dockerignore
index bbbfc1d2a..a47351064 100644
--- a/.dockerignore
+++ b/.dockerignore
@@ -24,3 +24,4 @@
LICENSE
README.md
**/appsettings.local.json
+**/wwwroot/lib
diff --git a/.editorconfig b/.editorconfig
index 3ed917775..071ab9e50 100644
--- a/.editorconfig
+++ b/.editorconfig
@@ -43,7 +43,7 @@ dotnet_naming_symbols.private_static_readonly_symbols.required_modifiers = stati
csharp_new_line_before_members_in_object_initializers = false
csharp_preferred_modifier_order = public, private, protected, internal, new, static, abstract, virtual, sealed, readonly, override, extern, unsafe, volatile, async:suggestion
-csharp_style_var_elsewhere = false:suggestion
+csharp_style_var_elsewhere = true:suggestion
csharp_style_var_for_built_in_types = true:suggestion
csharp_style_var_when_type_is_apparent = true:suggestion
dotnet_style_parentheses_in_arithmetic_binary_operators = never_if_unnecessary:suggestion
@@ -71,8 +71,8 @@ resharper_csharp_keep_blank_lines_in_code = 1
resharper_csharp_keep_blank_lines_in_declarations = 1
resharper_csharp_wrap_after_declaration_lpar = true
resharper_csharp_wrap_parameters_style = chop_if_long
-resharper_for_built_in_types = use_var_when_evident
-resharper_for_other_types = use_var_when_evident
+resharper_for_built_in_types = use_var
+resharper_for_other_types = use_var
resharper_for_simple_types = use_var
resharper_object_creation_when_type_not_evident = target_typed
resharper_parentheses_redundancy_style = remove_if_not_clarifies_precedence
@@ -149,6 +149,7 @@ dotnet_diagnostic.CA1304.severity = suggestion # Specify cultureInfo
dotnet_diagnostic.CA1309.severity = suggestion # Use ordinal StringComparison
dotnet_diagnostic.CA1311.severity = suggestion # Specify a culture or use an invariant version
dotnet_diagnostic.CA1822.severity = suggestion # Mark member as static
+dotnet_diagnostic.CA1859.severity = suggestion # Use concrete types when possible for improved performance
[*.axaml]
max_line_length = 160
diff --git a/Directory.Build.props b/Directory.Build.props
index b3e429f57..cba5fd1ff 100644
--- a/Directory.Build.props
+++ b/Directory.Build.props
@@ -1,8 +1,13 @@
- net7.0
+ net8.0
+ net8.0-windows
+
+
+
+ $(CommonTargetFramework)
enable
- 11
+ 12
Recommended
diff --git a/README.md b/README.md
index 4c20e0169..47b40c1c7 100644
--- a/README.md
+++ b/README.md
@@ -1,6 +1,6 @@
# WoWs-ShipBuilder
-
+
## General Information
@@ -24,7 +24,7 @@ Update checks only run on application startup so it won't mess up your system wi
## Telemetry data and error reports
-We do not collect any personal data in our application because it's simply not necessary for the app to work.
+We do not collect any personal data in our application because it's simply not necessary for the app to work.
However, we do automatically collect error reports.
If the application encounters an error, it automatically sends a report to [Sentry](https://sentry.io/) containing the error data. This data does not contain IP addresses or other personal information.
An error is not always visible for you as most errors should be caught internally and handled using fallback actions.
@@ -39,7 +39,7 @@ If you want to see the program translate in your language, you can follow the gu
Hosting the server where we store the data used by the program has a monthly cost. If you like the program and would like to help us out, you can donate at https://ko-fi.com/wowsshipbuilder. The app will always be free and with no ads. When you donate, you will also get a special role in our discord server.
## Discord
-We have a discord server that you can join by clicking [here](https://discord.gg/C8EaepZJDY) .
+We have a discord server that you can join by clicking [here](https://discord.gg/C8EaepZJDY) .
## Sponsorships and Support
diff --git a/WoWsShipBuilder.Common/Features/BallisticCharts/DispersionPlotHelper.cs b/WoWsShipBuilder.Common/Features/BallisticCharts/DispersionPlotHelper.cs
index a5314140b..2d140a737 100644
--- a/WoWsShipBuilder.Common/Features/BallisticCharts/DispersionPlotHelper.cs
+++ b/WoWsShipBuilder.Common/Features/BallisticCharts/DispersionPlotHelper.cs
@@ -32,7 +32,7 @@ private static (double waterLineProjection, double perpendicularToWaterProjectio
{
double impactAngle;
List> ballistic = BallisticHelper.CalculateBallistic(shell, maxRange, shell.Penetration).Where(x => x.Key >= aimingRange).ToList();
- if (ballistic.Any())
+ if (ballistic.Count != 0)
{
impactAngle = ballistic[0].Value.ImpactAngle;
}
diff --git a/WoWsShipBuilder.Common/Features/Builds/BuildValidation.cs b/WoWsShipBuilder.Common/Features/Builds/BuildValidation.cs
index 4fb9f166c..465393671 100644
--- a/WoWsShipBuilder.Common/Features/Builds/BuildValidation.cs
+++ b/WoWsShipBuilder.Common/Features/Builds/BuildValidation.cs
@@ -62,7 +62,7 @@ public static async Task ValidateBuildString(string
List invalidChars = Path.GetInvalidFileNameChars().ToList();
invalidChars.Add(';');
List invalidCharsInBuildName = invalidChars.FindAll(buildName.Contains);
- return invalidCharsInBuildName.Any() ? $"Invalid characters {string.Join(' ', invalidCharsInBuildName)}" : null;
+ return invalidCharsInBuildName.Count != 0 ? $"Invalid characters {string.Join(' ', invalidCharsInBuildName)}" : null;
}
public static async Task RetrieveLongUrlFromShortLink(string shortUrl)
diff --git a/WoWsShipBuilder.Common/Features/DataContainers/Aircraft/CvAircraftDataContainer.cs b/WoWsShipBuilder.Common/Features/DataContainers/Aircraft/CvAircraftDataContainer.cs
index 07a6f4c88..ef687f15a 100644
--- a/WoWsShipBuilder.Common/Features/DataContainers/Aircraft/CvAircraftDataContainer.cs
+++ b/WoWsShipBuilder.Common/Features/DataContainers/Aircraft/CvAircraftDataContainer.cs
@@ -110,7 +110,7 @@ public partial record CvAircraftDataContainer : DataContainerBase
public static List? FromShip(Ship ship, List shipConfiguration, List modifiers)
{
- if (!ship.CvPlanes.Any())
+ if (ship.CvPlanes.IsEmpty)
{
return null;
}
diff --git a/WoWsShipBuilder.Common/Features/DataContainers/Armament/PingerGunDataContainer.cs b/WoWsShipBuilder.Common/Features/DataContainers/Armament/PingerGunDataContainer.cs
index f0395c8cc..a4c1a86fb 100644
--- a/WoWsShipBuilder.Common/Features/DataContainers/Armament/PingerGunDataContainer.cs
+++ b/WoWsShipBuilder.Common/Features/DataContainers/Armament/PingerGunDataContainer.cs
@@ -38,7 +38,7 @@ public partial record PingerGunDataContainer : DataContainerBase
public static PingerGunDataContainer? FromShip(Ship ship, IEnumerable shipConfiguration, List modifiers)
{
- if (!ship.PingerGunList.Any())
+ if (ship.PingerGunList.IsEmpty)
{
return null;
}
diff --git a/WoWsShipBuilder.Common/Features/DataContainers/DataContainerUtility.cs b/WoWsShipBuilder.Common/Features/DataContainers/DataContainerUtility.cs
index 1870938c2..69fb678e9 100644
--- a/WoWsShipBuilder.Common/Features/DataContainers/DataContainerUtility.cs
+++ b/WoWsShipBuilder.Common/Features/DataContainers/DataContainerUtility.cs
@@ -28,8 +28,13 @@ public static int ApplyModifiers(this List modifierList, string proper
public static void UpdateConsumableModifierValue(this List consumableModifierList, List modifierList, string propertySelector, string modifierName)
{
- var modifier = consumableModifierList.Find(x => x.Name.Equals(modifierName))!;
- var newValue = (float)modifierList.ApplyModifiers(propertySelector, (decimal)modifier.Value);
+ var modifier = consumableModifierList.Find(x => x.Name.Equals(modifierName));
+ var newValue = (float)modifierList.ApplyModifiers(propertySelector, (decimal)(modifier?.Value ?? 0));
+ if (modifier == null)
+ {
+ return;
+ }
+
consumableModifierList.Remove(modifier);
consumableModifierList.Add(new Modifier(modifier.Name, newValue, "", modifier));
}
diff --git a/WoWsShipBuilder.Common/Features/DataContainers/Ship/ConsumableDataContainer.cs b/WoWsShipBuilder.Common/Features/DataContainers/Ship/ConsumableDataContainer.cs
index b0c32dc13..83d444090 100644
--- a/WoWsShipBuilder.Common/Features/DataContainers/Ship/ConsumableDataContainer.cs
+++ b/WoWsShipBuilder.Common/Features/DataContainers/Ship/ConsumableDataContainer.cs
@@ -52,7 +52,7 @@ private static ConsumableDataContainer FromTypeAndVariant(string name, string va
{
var consumableIdentifier = $"{name} {variant}";
var usingFallback = false;
- if (!(AppData.ConsumableList?.TryGetValue(consumableIdentifier, out var consumable) ?? false))
+ if (!AppData.ConsumableList.TryGetValue(consumableIdentifier, out var consumable))
{
Logging.Logger.LogError("Consumable {Identifier} not found in cached consumable list. Using dummy consumable instead", consumableIdentifier);
usingFallback = true;
@@ -99,7 +99,7 @@ private static ConsumableDataContainer FromTypeAndVariant(string name, string va
consumableModifiers.UpdateConsumableModifierValue(modifiers, "ConsumableDataContainer.TimeDelayAttack.PCY035", "timeDelayAttack");
consumableModifiers.UpdateConsumableModifierValue(modifiers, "ConsumableDataContainer.TimeDelayAppear.PCY035", "timeFromHeaven");
- var plane = AppData.FindAircraft(consumable.PlaneName[..consumable.PlaneName.IndexOf("_", StringComparison.Ordinal)]);
+ var plane = AppData.FindAircraft(consumable.PlaneName[..consumable.PlaneName.IndexOf('_', StringComparison.Ordinal)]);
var oldCruisingSpeed = consumableModifiers.Find(x => x.Name.Equals("cruisingSpeed", StringComparison.Ordinal));
if (oldCruisingSpeed is not null)
{
@@ -257,7 +257,7 @@ private static ConsumableDataContainer FromTypeAndVariant(string name, string va
consumableModifiers.UpdateConsumableModifierValue(modifiers, "ConsumableDataContainer.ExtraFighters.PCY012.PCY03", "fightersNum");
var maxKills = consumableModifiers.First(x => x.Name.Equals("fightersNum", StringComparison.Ordinal)).Value;
- var plane = AppData.FindAircraft(consumable.PlaneName[..consumable.PlaneName.IndexOf("_", StringComparison.Ordinal)]);
+ var plane = AppData.FindAircraft(consumable.PlaneName[..consumable.PlaneName.IndexOf('_', StringComparison.Ordinal)]);
var oldCruisingModifier = consumableModifiers.Find(x => x.Name.Equals("cruisingSpeed", StringComparison.Ordinal));
if (oldCruisingModifier is not null)
@@ -303,12 +303,15 @@ private static ConsumableDataContainer FromTypeAndVariant(string name, string va
else if (name.Contains("PCY045", StringComparison.InvariantCultureIgnoreCase))
{
// Hydrophone
+ // used prior to 13.1
consumableModifiers.UpdateConsumableModifierValue(modifiers, "ConsumableDataContainer.HydrophoneUpdateFrequency.PCY045", "hydrophoneUpdateFrequency");
+ cooldown = modifiers.ApplyModifiers("ConsumableDataContainer.Reload.PCY045", cooldown);
}
else if (name.Contains("PCY048", StringComparison.InvariantCultureIgnoreCase))
{
// Submarine Surveillance
prepTime = modifiers.ApplyModifiers("ConsumableDataContainer.PrepTime.PCY048", prepTime);
+ cooldown = modifiers.ApplyModifiers("ConsumableDataContainer.Reload.PCY048", cooldown);
}
}
else if (usingFallback)
diff --git a/WoWsShipBuilder.Common/Features/ShipComparison/ShipComparisonViewModel.cs b/WoWsShipBuilder.Common/Features/ShipComparison/ShipComparisonViewModel.cs
index 2e83616cf..4adc728a5 100644
--- a/WoWsShipBuilder.Common/Features/ShipComparison/ShipComparisonViewModel.cs
+++ b/WoWsShipBuilder.Common/Features/ShipComparison/ShipComparisonViewModel.cs
@@ -71,7 +71,7 @@ private Dictionary FilteredShipList
public Dictionary PinnedShipList { get; } = new();
- public List DataSections { get; private set; } = new() { ShipComparisonDataSections.General };
+ public List DataSections { get; private set; } = [ShipComparisonDataSections.General];
public ShipComparisonDataSections SelectedDataSection { get; set; } = ShipComparisonDataSections.General;
@@ -163,11 +163,7 @@ public void ToggleShowPinnedShipOnly()
public async Task ToggleTierSelection(int value)
{
- if (this.SelectedTiers.Contains(value))
- {
- this.SelectedTiers.Remove(value);
- }
- else
+ if (!this.SelectedTiers.Remove(value))
{
this.SelectedTiers.Add(value);
}
@@ -177,11 +173,7 @@ public async Task ToggleTierSelection(int value)
public async Task ToggleClassSelection(ShipClass value)
{
- if (this.SelectedClasses.Contains(value))
- {
- this.SelectedClasses.Remove(value);
- }
- else
+ if (!this.SelectedClasses.Remove(value))
{
this.SelectedClasses.Add(value);
}
@@ -191,11 +183,7 @@ public async Task ToggleClassSelection(ShipClass value)
public async Task ToggleNationSelection(Nation value)
{
- if (this.SelectedNations.Contains(value))
- {
- this.SelectedNations.Remove(value);
- }
- else
+ if (!this.SelectedNations.Remove(value))
{
this.SelectedNations.Add(value);
}
@@ -205,11 +193,7 @@ public async Task ToggleNationSelection(Nation value)
public async Task ToggleCategorySelection(ShipCategory value)
{
- if (this.SelectedCategories.Contains(value))
- {
- this.SelectedCategories.Remove(value);
- }
- else
+ if (!this.SelectedCategories.Remove(value))
{
this.SelectedCategories.Add(value);
}
@@ -293,7 +277,7 @@ public Dictionary RemoveBuilds(IEnumerable x.Key, x => x.Value);
foreach (var wrapper in buildList)
{
- if (this.FilteredShipList.Count(x => x.Value.Ship.Index.Equals(wrapper.Value.Ship.Index)) > 1)
+ if (this.FilteredShipList.Count(x => x.Value.Ship.Index.Equals(wrapper.Value.Ship.Index, StringComparison.Ordinal)) > 1)
{
this.FilteredShipList.Remove(wrapper.Key);
@@ -341,11 +325,7 @@ public void ResetAllBuilds()
public async Task AddPinnedShip(GridDataWrapper wrapper)
{
- if (!this.PinnedShipList.ContainsKey(wrapper.Id))
- {
- this.PinnedShipList.Add(wrapper.Id, wrapper);
- }
- else
+ if (!this.PinnedShipList.TryAdd(wrapper.Id, wrapper))
{
await this.RemovePinnedShip(wrapper);
}
@@ -355,11 +335,7 @@ public async Task AddPinnedShip(GridDataWrapper wrapper)
public void AddSelectedShip(GridDataWrapper wrapper)
{
- if (!this.SelectedShipList.ContainsKey(wrapper.Id))
- {
- this.SelectedShipList.Add(wrapper.Id, wrapper);
- }
- else
+ if (!this.SelectedShipList.TryAdd(wrapper.Id, wrapper))
{
this.RemoveSelectedShip(wrapper);
}
@@ -438,14 +414,14 @@ public void DuplicateSelectedShips()
this.PinnedShipList.Add(newWrapper.Id, newWrapper);
}
- if (this.MainBatteryDispersionCache.ContainsKey(selectedShip.Key))
+ if (this.MainBatteryDispersionCache.TryGetValue(selectedShip.Key, out var value))
{
- this.MainBatteryDispersionCache[newWrapper.Id] = this.MainBatteryDispersionCache[selectedShip.Key];
+ this.MainBatteryDispersionCache[newWrapper.Id] = value;
}
- if (this.SecondaryBatteryDispersionCache.ContainsKey(selectedShip.Key))
+ if (this.SecondaryBatteryDispersionCache.TryGetValue(selectedShip.Key, out var secondaryValue))
{
- this.SecondaryBatteryDispersionCache[newWrapper.Id] = this.SecondaryBatteryDispersionCache[selectedShip.Key];
+ this.SecondaryBatteryDispersionCache[newWrapper.Id] = secondaryValue;
}
}
@@ -515,7 +491,7 @@ public void SetFiringRange(double value, bool isMainBattery)
private Dictionary GetShipsToBeDisplayed(bool disableHideShipsIfNoSelectedSection)
{
- Dictionary list = this.ShowPinnedShipsOnly ? this.PinnedShipList : this.FilteredShipList;
+ var list = this.ShowPinnedShipsOnly ? this.PinnedShipList : this.FilteredShipList;
if (!disableHideShipsIfNoSelectedSection)
{
@@ -582,7 +558,7 @@ private void ChangeModulesBatch()
private List GetShipConfiguration(Ship ship)
{
- List shipConfiguration = this.UseUpgradedModules
+ var shipConfiguration = this.UseUpgradedModules
? ShipModuleHelper.GroupAndSortUpgrades(ship.ShipUpgradeInfo.ShipUpgrades)
.OrderBy(entry => entry.Key)
.Select(entry => entry.Value)
@@ -608,7 +584,7 @@ private Dictionary HideShipsIfNoSelectedSection(IEnumerab
return list.ToDictionary(x => x.Key, x => x.Value);
}
- Dictionary newList = this.SelectedDataSection switch
+ var newList = this.SelectedDataSection switch
{
ShipComparisonDataSections.MainBattery => list.Where(x => x.Value.ShipDataContainer.MainBatteryDataContainer is not null).ToDictionary(x => x.Key, x => x.Value),
ShipComparisonDataSections.He => list.Where(x => x.Value.HeShell?.Damage is not null).ToDictionary(x => x.Key, x => x.Value),
@@ -637,7 +613,7 @@ private Dictionary HideShipsIfNoSelectedSection(IEnumerab
private void GetDataSectionsToDisplay()
{
var displayedShipList = this.GetShipsToBeDisplayed(true);
- this.DataSections = !displayedShipList.Any() ? new() { ShipComparisonDataSections.General } : this.HideEmptyDataSections(displayedShipList);
+ this.DataSections = displayedShipList.Count == 0 ? [ShipComparisonDataSections.General] : this.HideEmptyDataSections(displayedShipList);
}
[SuppressMessage("Performance", "CA1822", Justification = "not static to preserve file structure")]
diff --git a/WoWsShipBuilder.Common/Features/ShipStats/ViewModels/CaptainSkillSelectorViewModel.cs b/WoWsShipBuilder.Common/Features/ShipStats/ViewModels/CaptainSkillSelectorViewModel.cs
index 2d4b3909a..7bec2be6f 100644
--- a/WoWsShipBuilder.Common/Features/ShipStats/ViewModels/CaptainSkillSelectorViewModel.cs
+++ b/WoWsShipBuilder.Common/Features/ShipStats/ViewModels/CaptainSkillSelectorViewModel.cs
@@ -105,7 +105,7 @@ public Captain? SelectedCaptain
this.SkillList = this.ConvertSkillToViewModel(this.currentClass, newCaptain);
this.CaptainTalentsList.Clear();
- if (newCaptain!.UniqueSkills.Any())
+ if (!newCaptain!.UniqueSkills.IsEmpty)
{
this.CaptainWithTalents = true;
foreach ((string _, UniqueSkill talent) in newCaptain.UniqueSkills)
@@ -265,7 +265,7 @@ public void AddSkill(Skill skill)
public List GetModifiersList()
{
var modifiers = this.SkillOrderList.ToList()
- .Where(skill => skill.Modifiers.Any() && skill.SkillNumber != ArSkillNumber && skill.SkillNumber != ArSkillNumberSubs && skill.SkillNumber != FuriousSkillNumber && skill.SkillNumber != ImprovedRepairPartyReadinessSkillNumber && skill.SkillNumber != ManualSecondaryBatteryAimingSkillNumber)
+ .Where(skill => !skill.Modifiers.IsEmpty && skill.SkillNumber != ArSkillNumber && skill.SkillNumber != ArSkillNumberSubs && skill.SkillNumber != FuriousSkillNumber && skill.SkillNumber != ImprovedRepairPartyReadinessSkillNumber && skill.SkillNumber != ManualSecondaryBatteryAimingSkillNumber)
.SelectMany(m => m.Modifiers)
.ToList();
@@ -414,7 +414,7 @@ private IEnumerable CollectTalentModifiers()
.SelectMany(talent => talent.Modifiers.Select(modifier => new Modifier(modifier.Name, float.Pow(modifier.Value, talent.ActivationNumbers), "", modifier)));
modifiers.AddRange(talentMultipleActivationModifiers);
- var talentFireChanceModifier = this.CaptainTalentsList.Where(talent => talent.Status && talent.Modifiers.Any(modifier => modifier.Name.Equals("burnProbabilityBonus", StringComparison.Ordinal)))
+ var talentFireChanceModifier = this.CaptainTalentsList.Where(talent => talent.Status && talent.Modifiers.Exists(modifier => modifier.Name.Equals("burnProbabilityBonus", StringComparison.Ordinal)))
.SelectMany(talent => talent.Modifiers.Select(modifier => new Modifier(modifier.Name, float.Round(modifier.Value * talent.ActivationNumbers, 2), "", modifier)));
modifiers.AddRange(talentFireChanceModifier);
diff --git a/WoWsShipBuilder.Common/Features/ShipStats/ViewModels/SignalSelectorViewModel.cs b/WoWsShipBuilder.Common/Features/ShipStats/ViewModels/SignalSelectorViewModel.cs
index 5160117a2..929f13fb2 100644
--- a/WoWsShipBuilder.Common/Features/ShipStats/ViewModels/SignalSelectorViewModel.cs
+++ b/WoWsShipBuilder.Common/Features/ShipStats/ViewModels/SignalSelectorViewModel.cs
@@ -49,9 +49,8 @@ private static List> LoadSignalList()
public void SignalCommandExecute(Exterior flag)
{
- if (this.SelectedSignals.Contains(flag))
+ if (this.SelectedSignals.Remove(flag))
{
- this.SelectedSignals.Remove(flag);
this.SignalsNumber--;
}
else
diff --git a/WoWsShipBuilder.Common/WoWsShipBuilder.Common.csproj b/WoWsShipBuilder.Common/WoWsShipBuilder.Common.csproj
index fe9486dfa..93c6caea1 100644
--- a/WoWsShipBuilder.Common/WoWsShipBuilder.Common.csproj
+++ b/WoWsShipBuilder.Common/WoWsShipBuilder.Common.csproj
@@ -11,7 +11,7 @@
-
+
@@ -63,4 +63,12 @@
+
+
+
+
+
+
+
+
diff --git a/WoWsShipBuilder.Data.Generator.Test/DataElementGeneratorTests/DataElementGeneratorTest.cs b/WoWsShipBuilder.Data.Generator.Test/DataElementGeneratorTests/DataElementGeneratorTest.cs
index 45f6ae3c7..2221ad118 100644
--- a/WoWsShipBuilder.Data.Generator.Test/DataElementGeneratorTests/DataElementGeneratorTest.cs
+++ b/WoWsShipBuilder.Data.Generator.Test/DataElementGeneratorTests/DataElementGeneratorTest.cs
@@ -50,7 +50,7 @@ protected static bool ShouldAdd(object? value)
(typeof(DataElementGenerator.DataElementGenerator), "DataElementFilteringAttribute.g.cs", AttributeHelper.DataElementFilteringAttribute),
(typeof(DataElementGenerator.DataElementGenerator), "TestRecord.g.cs", expected),
},
- ReferenceAssemblies = ReferenceAssemblies.Net.Net70,
+ ReferenceAssemblies = ReferenceAssemblies.Net.Net80,
AdditionalReferences = { MetadataReference.CreateFromFile(typeof(IDataElement).GetTypeInfo().Assembly.Location) },
},
};
diff --git a/WoWsShipBuilder.Desktop.Test/WoWsShipBuilder.Desktop.Test.csproj b/WoWsShipBuilder.Desktop.Test/WoWsShipBuilder.Desktop.Test.csproj
index 2fced84e8..dbf78b23e 100644
--- a/WoWsShipBuilder.Desktop.Test/WoWsShipBuilder.Desktop.Test.csproj
+++ b/WoWsShipBuilder.Desktop.Test/WoWsShipBuilder.Desktop.Test.csproj
@@ -1,7 +1,7 @@
- net7.0-windows
+ $(DesktopTargetFramework)
enable
enable
diff --git a/WoWsShipBuilder.Desktop/App.axaml.cs b/WoWsShipBuilder.Desktop/App.axaml.cs
index 4e4fffec6..c0a2b3cb7 100644
--- a/WoWsShipBuilder.Desktop/App.axaml.cs
+++ b/WoWsShipBuilder.Desktop/App.axaml.cs
@@ -1,6 +1,5 @@
using System;
using System.Diagnostics.CodeAnalysis;
-using System.Linq;
using System.Reflection;
using System.Runtime.Versioning;
using System.Threading;
@@ -140,7 +139,7 @@ private async Task UpdateCheck(AppNotificationService notificationService)
{
// Can throw a null-reference-exception, no idea why.
var updateInfo = await updateManager.CheckForUpdate();
- if (!updateInfo.ReleasesToApply.Any())
+ if (updateInfo.ReleasesToApply.Count == 0)
{
this.logger.LogInformation("No app update found");
return;
diff --git a/WoWsShipBuilder.Desktop/Features/Updater/LocalDataUpdater.cs b/WoWsShipBuilder.Desktop/Features/Updater/LocalDataUpdater.cs
index a5620aa8f..b3174db60 100644
--- a/WoWsShipBuilder.Desktop/Features/Updater/LocalDataUpdater.cs
+++ b/WoWsShipBuilder.Desktop/Features/Updater/LocalDataUpdater.cs
@@ -206,7 +206,7 @@ public async Task CheckJsonFileVersions(ServerType serverType
shouldLocalizationUpdate = false;
}
- if (filesToDownload.Any())
+ if (filesToDownload.Count != 0)
{
filesToDownload.Add((string.Empty, "VersionInfo.json"));
}
@@ -267,7 +267,7 @@ public async Task ValidateData(ServerType serverType, string d
}
}
- if (!missingFiles.Any())
+ if (missingFiles.Count == 0)
{
return new(true);
}
@@ -290,12 +290,12 @@ public async Task ShouldUpdaterRun(ServerType serverType)
public async Task CheckInstalledLocalizations(ServerType serverType)
{
- List installedLocales = await this.appDataService.GetInstalledLocales(serverType, false);
+ var installedLocales = await this.appDataService.GetInstalledLocales(serverType, false);
if (!installedLocales.Contains(this.appSettings.SelectedLanguage.LocalizationFileName))
{
this.logger.LogInformation("Selected localization is not installed. Downloading file...");
string localizationFile = this.appSettings.SelectedLanguage.LocalizationFileName + ".json";
- await this.awsClient.DownloadFiles(serverType, new() { ("Localization", localizationFile) });
+ await this.awsClient.DownloadFiles(serverType, [("Localization", localizationFile)]);
this.logger.LogInformation("Downloaded localization file for selected localization. Updating localizer data...");
}
else
@@ -311,8 +311,8 @@ public async Task CheckInstalledLocalizations(ServerType serverType)
/// An used to monitor the progress of the update.
private async Task CheckFilesAndDownloadUpdates(ServerType serverType, IProgress<(int, string)> progressTracker)
{
- UpdateCheckResult checkResult = await this.CheckJsonFileVersions(serverType);
- if (checkResult.AvailableFileUpdates.Any())
+ var checkResult = await this.CheckJsonFileVersions(serverType);
+ if (checkResult.AvailableFileUpdates.Count != 0)
{
this.logger.LogInformation("Updating {AvailableUpdateCount} files...", checkResult.AvailableFileUpdates.Count);
progressTracker.Report((1, nameof(Translation.SplashScreen_Json)));
@@ -342,7 +342,7 @@ private async Task ImageUpdate(IProgress<(int, string)> progressTracker, bool ca
{
string imageBasePath = this.appDataService.AppDataImageDirectory;
var shipImageDirectory = this.fileSystem.DirectoryInfo.New(this.fileSystem.Path.Combine(imageBasePath, "Ships"));
- if (!shipImageDirectory.Exists || !shipImageDirectory.GetFiles().Any() || !canDeltaUpdate)
+ if (!shipImageDirectory.Exists || shipImageDirectory.GetFiles().Length == 0 || !canDeltaUpdate)
{
progressTracker.Report((2, nameof(Translation.SplashScreen_ShipImages)));
await this.awsClient.DownloadImages(this.fileSystem);
diff --git a/WoWsShipBuilder.Desktop/Infrastructure/Data/DesktopAppDataService.cs b/WoWsShipBuilder.Desktop/Infrastructure/Data/DesktopAppDataService.cs
index 2a2ed1e29..4d11bd745 100644
--- a/WoWsShipBuilder.Desktop/Infrastructure/Data/DesktopAppDataService.cs
+++ b/WoWsShipBuilder.Desktop/Infrastructure/Data/DesktopAppDataService.cs
@@ -99,21 +99,22 @@ public async Task LoadLocalFilesAsync(ServerType serverType)
var localVersionInfo = await this.GetCurrentVersionInfo(serverType) ?? throw new InvalidOperationException("No local data found");
AppData.DataVersion = localVersionInfo.CurrentVersion.MainVersion.ToString(3) + "#" + localVersionInfo.CurrentVersion.DataIteration;
- var dataRootInfo = this.fileSystem.DirectoryInfo.New(this.GetDataPath(serverType));
- IDirectoryInfo[] categories = dataRootInfo.GetDirectories();
+ var dataRootPath = this.GetDataPath(serverType);
// Multiple categories can be loaded simultaneously without concurrency issues because every cache is only used by one category.
- await Parallel.ForEachAsync(categories, async (category, ct) =>
+ await Parallel.ForEachAsync(localVersionInfo.Categories, async (category, ct) =>
{
- if (category.Name.Contains("Localization", StringComparison.InvariantCultureIgnoreCase))
+ if (category.Key.Contains("Localization", StringComparison.InvariantCultureIgnoreCase))
{
return;
}
- foreach (var file in category.GetFiles())
+ var categoryPath = this.fileSystem.Path.Combine(dataRootPath, category.Key);
+ foreach (var file in category.Value.Select(file => file.FileName))
{
- string content = await this.fileSystem.File.ReadAllTextAsync(file.FullName, ct);
- await DataCacheHelper.AddToCache(file.Name, category.Name, content);
+ var filePath = this.fileSystem.Path.Combine(categoryPath, file);
+ string content = await this.fileSystem.File.ReadAllTextAsync(filePath, ct);
+ await DataCacheHelper.AddToCache(file, category.Key, content);
}
});
diff --git a/WoWsShipBuilder.Desktop/Infrastructure/HostApplicationBuilderExtensions.cs b/WoWsShipBuilder.Desktop/Infrastructure/HostApplicationBuilderExtensions.cs
index 2869dde6e..8675fc8e6 100644
--- a/WoWsShipBuilder.Desktop/Infrastructure/HostApplicationBuilderExtensions.cs
+++ b/WoWsShipBuilder.Desktop/Infrastructure/HostApplicationBuilderExtensions.cs
@@ -27,7 +27,7 @@ public static HostApplicationBuilder UseShipBuilderDesktop(this HostApplicationB
builder.Services.AddSingleton();
builder.Services.AddSingleton();
builder.Services.AddSingleton();
- builder.Services.AddSingleton();
+ builder.Services.AddScoped();
builder.Services.AddSingleton();
builder.Services.AddSingleton(x => x.GetRequiredService());
builder.Services.AddSingleton(x => x.GetRequiredService());
diff --git a/WoWsShipBuilder.Desktop/Infrastructure/LocalizeConverter.cs b/WoWsShipBuilder.Desktop/Infrastructure/LocalizeConverter.cs
index 901ef4e6d..9bd385cb7 100644
--- a/WoWsShipBuilder.Desktop/Infrastructure/LocalizeConverter.cs
+++ b/WoWsShipBuilder.Desktop/Infrastructure/LocalizeConverter.cs
@@ -98,10 +98,7 @@ public object ConvertBack(object? value, Type targetType, object? parameter, Cul
private static string ToSnakeCase(string camelCaseString)
{
- if (camelCaseString == null)
- {
- throw new ArgumentNullException(nameof(camelCaseString));
- }
+ ArgumentNullException.ThrowIfNull(camelCaseString);
if (camelCaseString.Length < 2)
{
diff --git a/WoWsShipBuilder.Desktop/Infrastructure/WebView/BlazorWebView.cs b/WoWsShipBuilder.Desktop/Infrastructure/WebView/BlazorWebView.cs
index 856af3be5..f92b7ba27 100644
--- a/WoWsShipBuilder.Desktop/Infrastructure/WebView/BlazorWebView.cs
+++ b/WoWsShipBuilder.Desktop/Infrastructure/WebView/BlazorWebView.cs
@@ -2,6 +2,7 @@
using System.Diagnostics.CodeAnalysis;
using Avalonia;
using Avalonia.Controls;
+using Avalonia.Interactivity;
using Avalonia.Platform;
using DynamicData;
using Microsoft.AspNetCore.Components.WebView.WindowsForms;
@@ -184,17 +185,8 @@ protected override IPlatformHandle CreateNativeControlCore(IPlatformHandle paren
return base.CreateNativeControlCore(parent);
}
- private void CoreWebView2OnIsDefaultDownloadDialogOpenChanged(object? sender, object e)
- {
- if (this.blazorWebView?.WebView.CoreWebView2.IsDefaultDownloadDialogOpen == true)
- {
- this.blazorWebView.WebView.CoreWebView2.CloseDefaultDownloadDialog();
- }
- }
-
private void WebViewOnCoreWebView2InitializationCompleted(object? sender, CoreWebView2InitializationCompletedEventArgs e)
{
- // blazorWebView!.WebView.CoreWebView2.IsDefaultDownloadDialogOpenChanged += CoreWebView2OnIsDefaultDownloadDialogOpenChanged;
this.DefaultDownloadFolderPath = this.defaultDownloadPath;
this.blazorWebView!.WebView.CoreWebView2InitializationCompleted -= this.WebViewOnCoreWebView2InitializationCompleted;
}
@@ -203,7 +195,6 @@ protected override void DestroyNativeControlCore(IPlatformHandle control)
{
if (OperatingSystem.IsWindows())
{
- // blazorWebView!.WebView.CoreWebView2.IsDefaultDownloadDialogOpenChanged -= CoreWebView2OnIsDefaultDownloadDialogOpenChanged;
this.blazorWebView?.Dispose();
this.blazorWebView = null;
}
@@ -213,14 +204,14 @@ protected override void DestroyNativeControlCore(IPlatformHandle control)
}
}
- protected override void OnDetachedFromVisualTree(VisualTreeAttachmentEventArgs e)
+ protected override void OnUnloaded(RoutedEventArgs e)
{
- base.OnDetachedFromVisualTree(e);
if (OperatingSystem.IsWindows())
{
- // Do not use until dotnet 8 because disposing the webview will deadlock. see https://github.com/dotnet/maui/issues/7997#issuecomment-1258681003
- // blazorWebView?.Dispose();
+ this.blazorWebView?.Dispose();
this.blazorWebView = null;
}
+
+ base.OnUnloaded(e);
}
}
diff --git a/WoWsShipBuilder.Desktop/Properties/PublishProfiles/PublishWindows.pubxml b/WoWsShipBuilder.Desktop/Properties/PublishProfiles/PublishWindows.pubxml
index c319c4a44..030c0bca6 100644
--- a/WoWsShipBuilder.Desktop/Properties/PublishProfiles/PublishWindows.pubxml
+++ b/WoWsShipBuilder.Desktop/Properties/PublishProfiles/PublishWindows.pubxml
@@ -7,7 +7,7 @@ https://go.microsoft.com/fwlink/?LinkID=208121.
Any CPU
bin\$(Configuration)\publish\
FileSystem
- net7.0-windows
+ net8.0-windows
true
win-x64
true
diff --git a/WoWsShipBuilder.Desktop/WoWsShipBuilder.Desktop.csproj b/WoWsShipBuilder.Desktop/WoWsShipBuilder.Desktop.csproj
index 5d552172f..500bec051 100644
--- a/WoWsShipBuilder.Desktop/WoWsShipBuilder.Desktop.csproj
+++ b/WoWsShipBuilder.Desktop/WoWsShipBuilder.Desktop.csproj
@@ -1,7 +1,7 @@
WinExe
- net7.0-windows
+ $(DesktopTargetFramework)
Assets/ShipBuilderIcon_bg.ico
false
@@ -33,13 +33,14 @@
-
+
+
diff --git a/WoWsShipBuilder.Web/Dockerfile b/WoWsShipBuilder.Web/Dockerfile
index 6dbfb13ed..39f73d740 100644
--- a/WoWsShipBuilder.Web/Dockerfile
+++ b/WoWsShipBuilder.Web/Dockerfile
@@ -1,30 +1,35 @@
-FROM mcr.microsoft.com/dotnet/aspnet:7.0 AS base
-WORKDIR /app
-EXPOSE 80
-EXPOSE 443
-
-FROM mcr.microsoft.com/dotnet/sdk:7.0 AS build
+FROM mcr.microsoft.com/dotnet/sdk:8.0-alpine AS build-env
+ARG TARGETARCH
WORKDIR /src
-COPY . .
-RUN dotnet restore "WoWsShipBuilder.Web/WoWsShipBuilder.Web.csproj"
-WORKDIR "/src/WoWsShipBuilder.Web"
-RUN dotnet build "WoWsShipBuilder.Web.csproj" -c Release -o /app/build
-FROM build AS publish
-RUN dotnet publish "WoWsShipBuilder.Web.csproj" -c Release -o /app/publish /p:UseAppHost=false
+# Copy all project files
+COPY . ./
+
+# Restore only projects needed for ShipBuilder Web
+RUN dotnet restore "WoWsShipBuilder.Web/WoWsShipBuilder.Web.csproj" -a $TARGETARCH
+
+# Publish ShipBuilder Web
+RUN dotnet publish "WoWsShipBuilder.Web/WoWsShipBuilder.Web.csproj" -a $TARGETARCH --no-restore -c Release -o /app
-FROM base AS final
-ENV APPLICATION_USER_ID 1001
-ENV APPLICATION_USER appuser
-LABEL org.opencontainers.image.source=https://github.com/WoWs-Builder-Team/WoWs-ShipBuilder
+FROM mcr.microsoft.com/dotnet/aspnet:8.0-alpine AS deploy-env
+EXPOSE 8080
+
+# Enable globalization and time zones:
+# https://github.com/dotnet/dotnet-docker/blob/main/samples/enable-globalization.md
+ENV \
+ DOTNET_SYSTEM_GLOBALIZATION_INVARIANT=false \
+ LC_ALL=en_US.UTF-8 \
+ LANG=en_US.UTF-8
+RUN apk add --no-cache \
+ icu-data-full \
+ icu-libs
+
+LABEL org.opencontainers.image.source="https://github.com/WoWs-Builder-Team/WoWs-ShipBuilder"
LABEL org.opencontainers.image.description="Container image for ShipBuilder Web"
LABEL org.opencontainers.image.licenses=MIT
-
-RUN groupadd --gid $APPLICATION_USER_ID $APPLICATION_USER \
- && useradd --uid $APPLICATION_USER_ID --gid $APPLICATION_USER_ID -m $APPLICATION_USER
+LABEL org.opencontainers.image.authors="WoWs Builder Team"
WORKDIR /app
-COPY --from=publish --chown=$APPLICATION_USER /app/publish .
-
-USER $APPLICATION_USER
-ENTRYPOINT ["dotnet", "WoWsShipBuilder.Web.dll"]
+COPY --from=build-env /app .
+USER $APP_UID
+ENTRYPOINT ["./WoWsShipBuilder.Web"]
diff --git a/WoWsShipBuilder.Web/Features/Authentication/AuthenticationService.cs b/WoWsShipBuilder.Web/Features/Authentication/AuthenticationService.cs
index 93bcc7221..26a97c4a2 100644
--- a/WoWsShipBuilder.Web/Features/Authentication/AuthenticationService.cs
+++ b/WoWsShipBuilder.Web/Features/Authentication/AuthenticationService.cs
@@ -39,11 +39,11 @@ public async Task VerifyToken(string accountId, string accessToken)
if (response.IsSuccessStatusCode)
{
var responseData = await response.Content.ReadFromJsonAsync();
- if (responseData is not null && responseData.Status.Equals("ok"))
+ if (responseData is not null && responseData.Status.Equals("ok", StringComparison.Ordinal))
{
- Dictionary? privateData = responseData.Data.FirstOrDefault().Value?.Private;
+ var privateData = responseData.Data.FirstOrDefault().Value?.Private;
this.logger.LogInformation("Token-verification for account {} successful", accountId);
- return privateData is not null && privateData.Any();
+ return privateData is not null && privateData.Count != 0;
}
}
diff --git a/WoWsShipBuilder.Web/Infrastructure/Data/WebUserDataService.cs b/WoWsShipBuilder.Web/Infrastructure/Data/WebUserDataService.cs
index 532f5d0ff..ece6a8bc6 100644
--- a/WoWsShipBuilder.Web/Infrastructure/Data/WebUserDataService.cs
+++ b/WoWsShipBuilder.Web/Infrastructure/Data/WebUserDataService.cs
@@ -176,6 +176,7 @@ private async Task AddOrUpdateBuilds(List buildsList)
var buildsUpdated = 0;
var buildsNotNeedingUpdate = 0;
+ var buildAdded = false;
foreach (var build in buildsList)
{
@@ -210,10 +211,11 @@ private async Task AddOrUpdateBuilds(List buildsList)
else
{
this.savedBuilds.Insert(0, build);
+ buildAdded = true;
}
}
- if (buildsUpdated == 0 || buildsNotNeedingUpdate == buildsList.Count)
+ if (!buildAdded && (buildsUpdated == 0 || buildsNotNeedingUpdate == buildsList.Count))
{
return -1;
}
diff --git a/WoWsShipBuilder.sln b/WoWsShipBuilder.sln
index b171abfb2..c3f3d79dc 100644
--- a/WoWsShipBuilder.sln
+++ b/WoWsShipBuilder.sln
@@ -13,6 +13,7 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Configs", "Configs", "{0F12
WoWsShipBuilder.sln.DotSettings = WoWsShipBuilder.sln.DotSettings
global.json = global.json
Directory.Build.props = Directory.Build.props
+ .dockerignore = .dockerignore
EndProjectSection
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Docs", "Docs", "{43EE144D-CA52-44D5-B761-A27C0A37E1C5}"
diff --git a/global.json b/global.json
index b69e1fe54..88c19ffc0 100644
--- a/global.json
+++ b/global.json
@@ -1,6 +1,6 @@
{
"sdk": {
- "version": "7.0.400",
+ "version": "8.0.100",
"rollForward": "latestFeature",
"allowPrerelease": false
},
diff --git a/installer/Tools/SquirrelBuildAndRelease.ps1 b/installer/Tools/SquirrelBuildAndRelease.ps1
index 69fd2ac33..d638ccf84 100644
--- a/installer/Tools/SquirrelBuildAndRelease.ps1
+++ b/installer/Tools/SquirrelBuildAndRelease.ps1
@@ -5,7 +5,7 @@
[string][Parameter(Mandatory=$false)]$signingPassword
)
-$frameworkVersion="net7.0-windows"
+$frameworkVersion="net8.0-windows"
if ($skipBuild) {
Write-Output "Skipping build"