diff --git a/FASTER/FASTER.csproj b/FASTER/FASTER.csproj
index 8f1e513..4ef605b 100644
--- a/FASTER/FASTER.csproj
+++ b/FASTER/FASTER.csproj
@@ -11,7 +11,7 @@
True
FASTERKey.snk
Keelah Fox, Jupster, Canno.n
- 1.9.7.2
+ 1.9.7.3
FoxliCorp.
Fox's Arma Server Tool Extended Rewrite
Copyright © 2019
diff --git a/FASTER/Models/AdvancedOptions.cs b/FASTER/Models/AdvancedOptions.cs
new file mode 100644
index 0000000..a9e1c7f
--- /dev/null
+++ b/FASTER/Models/AdvancedOptions.cs
@@ -0,0 +1,100 @@
+using System;
+using System.ComponentModel;
+
+namespace FASTER.Models
+{
+ [Serializable]
+ public class AdvancedOptions : INotifyPropertyChanged
+ {
+ private bool logObjectNotFound = true; // logging enabled
+ private bool skipDescriptionParsing = false; // Parse description.ext
+ private bool ignoreMissionLoadErrors = false; // Do not ignore errors
+ private int queueSizeLogG = 0; // If a specific players message queue is larger than and '#monitor' is running, dump the messages to a logfile for analysis
+ private string advancedOptionsContent;
+
+ public bool LogObjectNotFound
+ {
+ get => logObjectNotFound;
+ set
+ {
+ logObjectNotFound = value;
+ RaisePropertyChanged(nameof(LogObjectNotFound));
+ }
+ }
+
+ public bool SkipDescriptionParsing
+ {
+ get => skipDescriptionParsing;
+ set
+ {
+ skipDescriptionParsing = value;
+ RaisePropertyChanged(nameof(SkipDescriptionParsing));
+ }
+ }
+
+ public bool IgnoreMissionLoadErrors
+ {
+ get => ignoreMissionLoadErrors;
+ set
+ {
+ ignoreMissionLoadErrors = value;
+ RaisePropertyChanged(nameof(IgnoreMissionLoadErrors));
+ }
+ }
+
+ public int QueueSizeLogG
+ {
+ get => queueSizeLogG;
+ set
+ {
+ queueSizeLogG = value;
+ RaisePropertyChanged(nameof(QueueSizeLogG));
+ }
+ }
+
+ public string AdvancedOptionsContent
+ {
+ get => advancedOptionsContent;
+ set
+ {
+ advancedOptionsContent = value;
+ RaisePropertyChanged(nameof(AdvancedOptionsContent));
+ }
+ }
+
+ public AdvancedOptions()
+ {
+ if(string.IsNullOrWhiteSpace(AdvancedOptionsContent))
+ {
+ AdvancedOptionsContent = ProcessFile();
+ }
+ }
+
+ public string ProcessFile()
+ {
+ string output = "//\r\n"
+ + "// AdvancedOptions\r\n"
+ + "//\r\n"
+ + "// comments are written with \"//\" in front of them.\r\n"
+ + "\r\n"
+ + "\r\n"
+ + $"queueSizeLogG = {QueueSizeLogG};\t\t\t// If a specific players message queue is larger than Value number and #monitor is running, dump his messages to a logfile for analysis\r\n"
+ + $"LogObjectNotFound = {LogObjectNotFound};\t\t// When false to skip logging 'Server: Object not found messages'.\r\n"
+ + $"SkipDescriptionParsing = {SkipDescriptionParsing};\t\t// When true to skip parsing of description.ext/mission.sqm. Will show pbo filename instead of configured missionName. OverviewText and such won't work, but loading the mission list is a lot faster when there are many missions\r\n"
+ + $"ignoreMissionLoadErrors = {IgnoreMissionLoadErrors};\t\t// When set to true, the mission will load no matter the amount of loading errors. If set to false, the server will abort mission's loading and return to mission selection.\r\n"
+ + "\r\n"
+ + "\r\n";
+ return output;
+ }
+ public event PropertyChangedEventHandler? PropertyChanged;
+
+ private void RaisePropertyChanged(string property)
+ {
+ PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(property));
+ if (property != nameof(AdvancedOptionsContent))
+ {
+ AdvancedOptionsContent = ProcessFile();
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/FASTER/Models/Arma3Profile.cs b/FASTER/Models/Arma3Profile.cs
index 66c0c67..2ffcce7 100644
--- a/FASTER/Models/Arma3Profile.cs
+++ b/FASTER/Models/Arma3Profile.cs
@@ -41,7 +41,7 @@ public class Arma3Profile : INotifyPropertyChanged
private ushort mapContentMines = 1;
private ushort autoReport = 0;
private ushort multipleSaves = 0;
- private int tacticalPing = 1;
+ private int tacticalPing = 1;
private ushort aiLevelPreset = 3;
private double skillAi = 0.5;
diff --git a/FASTER/Models/BasicCfg.cs b/FASTER/Models/BasicCfg.cs
index fa3cf7f..4ed760a 100644
--- a/FASTER/Models/BasicCfg.cs
+++ b/FASTER/Models/BasicCfg.cs
@@ -14,8 +14,8 @@ public static class BasicCfgArrays
[Serializable]
public class BasicCfg : INotifyPropertyChanged
{
- private uint viewDistance = 2000;
- private double terrainGrid = 25;
+ private uint viewDistance = 2000;
+ private double terrainGrid = 25;
private ushort maxMsgSend = 128;
private ushort maxSizeGuaranteed = 256;
@@ -158,10 +158,10 @@ public string PerfPreset
MaxMsgSend = 256;
MaxSizeGuaranteed = 512;
MaxSizeNonGuaranteed = 256;
- MinErrorToSend = 0.001;
- MinErrorToSendNear = 0.01;
- MaxPacketSize = 1400;
- MaxCustomFileSize = 160;
+ MinErrorToSend = 0.001;
+ MinErrorToSendNear = 0.01;
+ MaxPacketSize = 1400;
+ MaxCustomFileSize = 160;
switch ((short)Array.IndexOf(BasicCfgArrays.PerfPresets, value))
@@ -177,8 +177,8 @@ public string PerfPreset
MinBandwidth = 250000000;
break;
case 4:
- MaxMsgSend = 512;
- MinBandwidth = 1000000000;
+ MaxMsgSend = 512;
+ MinBandwidth = 1000000000;
break;
}
RaisePropertyChanged("PerfPreset");
diff --git a/FASTER/Models/ServerCfg.cs b/FASTER/Models/ServerCfg.cs
index d5f14f3..3659802 100644
--- a/FASTER/Models/ServerCfg.cs
+++ b/FASTER/Models/ServerCfg.cs
@@ -21,12 +21,12 @@ public class ServerCfg : INotifyPropertyChanged
private string passwordAdmin;
private string password;
private string hostname;
- private int maxPlayers = 32;
- private List motd = new();
+ private int maxPlayers = 32;
+ private List motd = new();
private int motdInterval;
- private List admins = new();
- private List headlessClients = new();
- private List localClient = new();
+ private List admins = new();
+ private List headlessClients = new();
+ private List localClient = new();
private bool headlessClientEnabled;
private bool votingEnabled;
private bool netlogEnabled;
@@ -47,18 +47,13 @@ public class ServerCfg : INotifyPropertyChanged
private bool autoSelectMission = true;
private bool randomMissionOrder = true;
private int briefingTimeOut = 60; // <-
- private int roleTimeOut = 90; // <- These are BI base figues
- private int votingTimeOut = 60; // <-
+ private int roleTimeOut = 90; // <- These are BI base figues
+ private int votingTimeOut = 60; // <-
private int debriefingTimeOut = 45; // <-
- private bool LogObjectNotFound = true; // logging enabled
- private bool SkipDescriptionParsing = false; // parse description.ext
- private bool ignoreMissionLoadErrors = false; // do not ingore errors
private int armaUnitsTimeout = 30; // Defines how long the player will be stuck connecting and wait for armaUnits data. Player will be notified if timeout elapsed and no units data was received
- private int queueSizeLogG = 1000000; // if a specific players message queue is larger than 1MB and '#monitor' is running, dump his messages to a logfile for analysis
private string forcedDifficulty = "Custom"; // By default forcedDifficulty is only applying Custom
-
- //Arma server only
+ //Arma Server Only
private short verifySignatures = 0; // 0 = Disabled (FASTER Default); 1 = Deprecated Activated ; 2 = Activated (Arma Default)
private bool drawingInMap = true;
private short disableVoN; // 0 = VoN activated ; 1 = VoN Disabled
@@ -78,17 +73,19 @@ public class ServerCfg : INotifyPropertyChanged
private string doubleIdDetected;
private string onUserConnected;
private string onUserDisconnected;
- private string onHackedData = "kick (_this select 0)";
+ private string onHackedData = "kick (_this select 0)";
private string onDifferentData;
- private string onUnsignedData = "kick (_this select 0)";
+ private string onUnsignedData = "kick (_this select 0)";
private string onUserKicked;
+ //Mision Settings
private bool missionSelectorChecked;
private string missionContentOverride;
- private List _missions = new();
+ private List _missions = new();
private bool autoInit;
private string difficulty = "Custom";
+ //Performance
private bool maxMemOverride;
private uint maxMem = 1024;
private bool cpuCountOverride;
@@ -97,8 +94,6 @@ public class ServerCfg : INotifyPropertyChanged
private string serverCfgContent;
-
-
#region Server Options
public string PasswordAdmin
{
@@ -290,7 +285,7 @@ public string AllowedFilePatching
set
{
allowedFilePatching = (short)Array.IndexOf(ServerCfgArrays.AllowFilePatchingStrings, value);
- RaisePropertyChanged("Password");
+ RaisePropertyChanged("AllowedFilePatching");
}
}
@@ -394,36 +389,6 @@ public int DebriefingTimeOut
}
}
- public bool logObjectNotFound
- {
- get => LogObjectNotFound;
- set
- {
- LogObjectNotFound = value;
- RaisePropertyChanged("logObjectNotFound");
- }
- }
-
- public bool skipDescriptionParsing
- {
- get => SkipDescriptionParsing;
- set
- {
- SkipDescriptionParsing = value;
- RaisePropertyChanged("skipDescriptionParsing");
- }
- }
-
- public bool IgnoreMissionLoadErrors
- {
- get => ignoreMissionLoadErrors;
- set
- {
- ignoreMissionLoadErrors = value;
- RaisePropertyChanged("IgnoreMissionLoadErrors");
- }
- }
-
public int ArmaUnitsTimeout
{
get => armaUnitsTimeout;
@@ -434,16 +399,6 @@ public int ArmaUnitsTimeout
}
}
- public int QueueSizeLogG
- {
- get => queueSizeLogG;
- set
- {
- queueSizeLogG = value;
- RaisePropertyChanged("QueueSizeLogG");
- }
- }
-
public string ForcedDifficulty
{
get => forcedDifficulty;
@@ -888,14 +843,10 @@ public string ProcessFile()
+ $"disableVoN = {disableVoN};\t\t\t\t// If set to 1, Voice over Net will not be available\r\n"
+ $"vonCodec = {vonCodec};\t\t\t\t// If set to 1 then it uses IETF standard OPUS codec, if to 0 then it uses SPEEX codec (since Arma 3 update 1.58+) \r\n"
+ $"skipLobby = {(skipLobby ? "1" : "0")};\t\t\t\t// Overridden by mission parameters\r\n"
- + $"vonCodecQuality = {vonCodecQuality};\t\t\t// since 1.62.95417 supports range 1-20 //since 1.63.x will supports range 1-30 //8kHz is 0-10, 16kHz is 11-20, 32kHz(48kHz) is 21-30 \r\n"
+ + $"vonCodecQuality = {vonCodecQuality};\t\t\t// Since 1.62.95417 supports range 1-20 //since 1.63.x will supports range 1-30 //8kHz is 0-10, 16kHz is 11-20, 32kHz(48kHz) is 21-30 \r\n"
+ $"persistent = {persistent};\t\t\t\t// If 1, missions still run on even after the last player disconnected.\r\n"
+ $"timeStampFormat = \"{timeStampFormat}\";\t\t// Set the timestamp format used on each report line in server-side RPT file. Possible values are \"none\" (default),\"short\",\"full\".\r\n"
+ $"BattlEye = {battlEye};\t\t\t\t// Server to use BattlEye system\r\n"
- + $"queueSizeLogG = {queueSizeLogG};\t\t\t// If a specific players message queue is larger than 1MB and #monitor is running, dump his messages to a logfile for analysis \r\n"
- + $"LogObjectNotFound = {logObjectNotFound};\t\t// When false to skip logging 'Server: Object not found messages'.\r\n"
- + $"SkipDescriptionParsing = {skipDescriptionParsing};\t\t// When true to skip parsing of description.ext/mission.sqm. Will show pbo filename instead of configured missionName. OverviewText and such won't work, but loading the mission list is a lot faster when there are many missions \r\n"
- + $"ignoreMissionLoadErrors = {ignoreMissionLoadErrors};\t\t// When set to true, the mission will load no matter the amount of loading errors. If set to false, the server will abort mission's loading and return to mission selection.\r\n"
+ $"forcedDifficulty = {forcedDifficulty};\t\t\t// Forced difficulty (Recruit, Regular, Veteran, Custom)\r\n"
+ "\r\n"
+ "// TIMEOUTS\r\n"
@@ -917,9 +868,9 @@ public string ProcessFile()
+ $"doubleIdDetected = \"{doubleIdDetected}\";\t\t\t//\r\n"
+ "\r\n"
+ "// SIGNATURE VERIFICATION\r\n"
- + $"onUnsignedData = \"{onUnsignedData}\";\t// unsigned data detected\r\n"
- + $"onHackedData = \"{onHackedData}\";\t// tampering of the signature detected\r\n"
- + $"onDifferentData = \"{onDifferentData}\";\t\t\t// data with a valid signature, but different version than the one present on server detected\r\n"
+ + $"onUnsignedData = \"{onUnsignedData}\";\t// Unsigned data detected\r\n"
+ + $"onHackedData = \"{onHackedData}\";\t// Tampering of the signature detected\r\n"
+ + $"onDifferentData = \"{onDifferentData}\";\t\t\t// Data with a valid signature, but different version than the one present on server detected\r\n"
+ "\r\n"
+ "\r\n"
+ "// MISSIONS CYCLE (see below)\r\n"
@@ -934,6 +885,7 @@ public string ProcessFile()
+ "// HEADLESS CLIENT\r\n"
+ $"{(headlessClientEnabled && !headlessClients.Exists(string.IsNullOrWhiteSpace) ? $"headlessClients[] = { "{\n\t\"" + string.Join("\",\n\t \"", headlessClients) + "\"\n}" };\r\n" : "")}"
+ $"{(headlessClientEnabled && !localClient.Exists(string.IsNullOrWhiteSpace)? $"localClient[] = { "{\n\t\"" + string.Join("\",\n\t \"", localClient) + "\"\n}" };" : "")}";
+ + "\r\n"
return output;
}
@@ -950,7 +902,7 @@ private void RaisePropertyChanged(string property)
[Serializable]
public class ProfileMission : INotifyPropertyChanged
{
- private bool missionChecked;
+ private bool missionChecked;
private string name;
private string path;
@@ -991,4 +943,4 @@ private void RaisePropertyChanged(string property)
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(property));
}
}
-}
+}
\ No newline at end of file
diff --git a/FASTER/Models/ServerProfile.cs b/FASTER/Models/ServerProfile.cs
index c9015a4..fa36f9f 100644
--- a/FASTER/Models/ServerProfile.cs
+++ b/FASTER/Models/ServerProfile.cs
@@ -1,5 +1,4 @@
-
-using System;
+using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.IO;
@@ -22,13 +21,14 @@ public ServerProfileCollection()
internal static void AddServerProfile(string profileName)
{
- var currentProfiles = Properties.Settings.Default.Profiles;
+ var currentProfiles = Properties.Settings.Default.Profiles;
var p = new ServerProfile(profileName);
- p.ServerCfg.ServerCfgContent = p.ServerCfg.ProcessFile();
- p.BasicCfg.BasicContent = p.BasicCfg.ProcessFile();
- p.ArmaProfile.ArmaProfileContent = p.ArmaProfile.ProcessFile();
+ p.ServerCfg.ServerCfgContent = p.ServerCfg.ProcessFile();
+ p.AdvancedOptions.AdvancedOptionsContent = p.AdvancedOptions.ProcessFile();
+ p.BasicCfg.BasicContent = p.BasicCfg.ProcessFile();
+ p.ArmaProfile.ArmaProfileContent = p.ArmaProfile.ProcessFile();
currentProfiles.Add(p);
- Properties.Settings.Default.Profiles = currentProfiles;
+ Properties.Settings.Default.Profiles = currentProfiles;
Properties.Settings.Default.Save();
MainWindow.Instance.LoadServerProfiles();
}
@@ -72,6 +72,7 @@ public class ServerProfile : INotifyPropertyChanged
private bool _profileModsFilterIsRegex = false;
private bool _profileModsFilterIsInvalid = false;
private ServerCfg _serverCfg;
+ private AdvancedOptions _advancedOptions;
private Arma3Profile _armaProfile;
private BasicCfg _basicCfg;
@@ -378,8 +379,8 @@ public bool ProfileModsFilterIsInvalid
}
}
- public ServerCfg ServerCfg
- {
+ public ServerCfg ServerCfg
+ {
get => _serverCfg;
set
{
@@ -390,6 +391,19 @@ public ServerCfg ServerCfg
RaisePropertyChanged("ServerCfg");
}
}
+
+ public AdvancedOptions AdvancedOptions
+ {
+ get => _advancedOptions;
+ set
+ {
+ if(_advancedOptions != null)
+ _advancedOptions.PropertyChanged -= Class_PropertyChanged;
+ _advancedOptions = value;
+ _advancedOptions.PropertyChanged += Class_PropertyChanged;
+ RaisePropertyChanged("AdvancedOptions");
+ }
+ }
public Arma3Profile ArmaProfile
{
@@ -424,12 +438,13 @@ public ServerProfile(string name, bool createFolder = true)
Name = name;
Executable = Path.Combine(Properties.Settings.Default.serverPath, "arma3server_x64.exe");
ServerCfg = new ServerCfg(){ Hostname = name};
+ AdvancedOptions = new AdvancedOptions();
ArmaProfile = new Arma3Profile();
BasicCfg = new BasicCfg();
ServerCfg.ServerCfgContent = ServerCfg.ProcessFile();
+ AdvancedOptions.AdvancedOptionsContent = AdvancedOptions.ProcessFile();
ArmaProfile.ArmaProfileContent = ArmaProfile.ProcessFile();
BasicCfg.BasicContent = BasicCfg.ProcessFile();
-
if (createFolder)
{ Directory.CreateDirectory(Path.Combine(Properties.Settings.Default.serverPath, "Servers", Id)); }
}
@@ -438,10 +453,12 @@ public ServerProfile()
{
_id = $"_{Guid.NewGuid():N}";
Name = _id;
- ServerCfg = new ServerCfg(){ Hostname = Name};
- ArmaProfile = new Arma3Profile();
- BasicCfg = new BasicCfg();
+ ServerCfg = new ServerCfg(){ Hostname = Name};
+ ArmaProfile = new Arma3Profile();
+ BasicCfg = new BasicCfg();
+ AdvancedOptions = new AdvancedOptions();
ServerCfg.ServerCfgContent = ServerCfg.ProcessFile();
+ AdvancedOptions.AdvancedOptionsContent = AdvancedOptions.ProcessFile();
ArmaProfile.ArmaProfileContent = ArmaProfile.ProcessFile();
BasicCfg.BasicContent = BasicCfg.ProcessFile();
}
@@ -526,8 +543,9 @@ private string GetCommandLine()
{
- string config = Path.Combine(ArmaPath, "Servers", Id, "server_config.cfg");
- string basic = Path.Combine(ArmaPath, "Servers", Id, "server_basic.cfg");
+ string config = Path.Combine(ArmaPath, "Servers", Id, "server_config.cfg");
+ string advanced = Path.Combine(ArmaPath, "Servers", Id, "server_advanced.cfg");
+ string basic = Path.Combine(ArmaPath, "Servers", Id, "server_basic.cfg");
string playerMods = string.Join(";", ProfileMods.Where(m => m.ClientSideChecked).OrderBy(m => m.LoadPriority).Select(m => $"@{Functions.SafeName(m.Name)}"));
string serverMods = string.Join(";", ProfileMods.Where(m => m.ServerSideChecked).OrderBy(m => m.LoadPriority).Select(m => $"@{Functions.SafeName(m.Name)}"));
@@ -535,6 +553,7 @@ private string GetCommandLine()
{
$"-port={Port}",
$" \"-config={config}\"",
+ $" \"-advanced={advanced}\"",
$" \"-cfg={basic}\"",
$" \"-profiles={Path.Combine(ArmaPath, "Servers", Id)}\"",
$" -name={Id}",
@@ -678,4 +697,4 @@ public override string ToString()
private void RaisePropertyChanged(string property)
{ PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(property)); }
}
-}
+}
\ No newline at end of file
diff --git a/FASTER/ViewModel/ProfileViewModel.cs b/FASTER/ViewModel/ProfileViewModel.cs
index b9b6f71..06c0dad 100644
--- a/FASTER/ViewModel/ProfileViewModel.cs
+++ b/FASTER/ViewModel/ProfileViewModel.cs
@@ -189,7 +189,8 @@ private bool ProfileFilesExist(string profile)
if (!Directory.Exists(Path.Combine(path, "Servers", profile)))
{ return false; }
- return File.Exists(Path.Combine(path, "Servers", profile, "server_config.cfg"))
+ return File.Exists(Path.Combine(path, "Servers", profile, "server_config.cfg"))
+ && File.Exists(Path.Combine(path, "Servers", profile, "server_advanced.cfg"))
&& File.Exists(Path.Combine(path, "Servers", profile, "server_basic.cfg"));
}
@@ -210,6 +211,7 @@ internal void DeleteProfile()
internal void SaveProfile()
{
string config = Path.Combine(Profile.ArmaPath, "Servers", Profile.Id, "server_config.cfg");
+ string advanced = Path.Combine(Profile.ArmaPath, "Servers", Profile.Id, "server_advanced.cfg");
string basic = Path.Combine(Profile.ArmaPath, "Servers", Profile.Id, "server_basic.cfg");
string serverProfile = Path.Combine(Profile.ArmaPath, "Servers", Profile.Id, "users", Profile.Id, $"{Profile.Id}.Arma3Profile");
@@ -220,6 +222,7 @@ internal void SaveProfile()
try
{
File.WriteAllLines(config, Profile.ServerCfg.ServerCfgContent.Replace("\r", "").Split('\n'));
+ File.WriteAllLines(advanced, Profile.AdvancedOptions.AdvancedOptionsContent.Replace("\r", "").Split('\n'));
File.WriteAllLines(basic, Profile.BasicCfg.BasicContent.Replace("\r", "").Split('\n'));
File.WriteAllLines(serverProfile, Profile.ArmaProfile.ArmaProfileContent.Replace("\r", "").Split('\n'));
}
@@ -242,7 +245,6 @@ internal void SaveProfile()
MissingMods++;
}
-
var index = Properties.Settings.Default.Profiles.FindIndex(p => p.Id == Profile.Id);
if (index != -1)
{ Properties.Settings.Default.Profiles[index] = Profile; }
@@ -253,7 +255,6 @@ internal void SaveProfile()
if (MissingMods > 0)
DisplayMessage($"{MissingMods} mods were not found in the Arma directory.\nMake sure you have deployed the correct mods");
-
}
public ObservableCollection FadeOutStrings { get; } = new ObservableCollection(ProfileCfgArrays.FadeOutStrings);
@@ -352,32 +353,41 @@ internal void SelectServerFile()
internal async Task CopyModKeys()
{
var mods = new List();
+ var failedMods = new List();
if (!Directory.Exists(Properties.Settings.Default.modStagingDirectory))
{
MainWindow.Instance.IFlyout.IsOpen = true;
- MainWindow.Instance.IFlyoutMessage.Content = $"The SteamCMD path does not exist :\n{Properties.Settings.Default.modStagingDirectory}";
+ MainWindow.Instance.IFlyoutMessage.Content = $"The SteamCMD path does not exist:\n{Properties.Settings.Default.modStagingDirectory}";
return;
}
var clientMods = Profile.ProfileMods.Where(p => p.ClientSideChecked).ToList();
var optionalMods = Profile.ProfileMods.Where(p => p.OptChecked).ToList();
- var steamMods = clientMods.Union(optionalMods).ToList();
+ var steamMods = clientMods.Union(optionalMods).Distinct().ToList();
foreach (var line in steamMods)
{
try
- { mods.AddRange(Directory.GetDirectories(Path.Combine(Properties.Settings.Default.modStagingDirectory, line.Id.ToString()))
- .SelectMany(subDir => Directory.GetFiles(subDir, "*.bikey", SearchOption.TopDirectoryOnly))); }
+ {
+ if (!Directory.Exists(Properties.Settings.Default.modStagingDirectory))
+ {
+ failedMods.Add(line.Name);
+ continue;
+ }
+ mods.AddRange(Directory.GetDirectories(Path.Combine(Properties.Settings.Default.modStagingDirectory, line.Id.ToString()))
+ .SelectMany(subDir => Directory.GetFiles(subDir, "*.bikey", SearchOption.TopDirectoryOnly)));
+ }
catch (DirectoryNotFoundException)
- { /*there was no directory*/ }
+ {
+ failedMods.Add(line.Name);
+ }
}
await ClearModKeys();
Directory.CreateDirectory(Path.Combine(Profile.ArmaPath, "keys"));
-
- foreach (var link in mods)
+ foreach (var link in mods.Distinct())
{
try { File.Copy(link, Path.Combine(Profile.ArmaPath, "keys", Path.GetFileName(link)), true); }
catch (IOException)
@@ -386,17 +396,29 @@ internal async Task CopyModKeys()
MainWindow.Instance.IFlyoutMessage.Content = $"Some keys could not be copied : {Path.GetFileName(link)}";
}
}
+
+ if (failedMods.Count > 0)
+ {
+ DisplayMessage("Failed to read keys for these mods:\n" + string.Join("\n", failedMods.Distinct()));
+ }
+ else
+ {
+ DisplayMessage("All mod keys copied successfully.");
+ }
+
await Task.Delay(1000);
- }
+ }
internal async Task ClearModKeys()
{
var ignoredKeys = new[] {"a3.bikey", "a3c.bikey", "gm.bikey", "ws.bikey", "csla.bikey", "vn.bikey", "spe.bikey", "rf.bikey", "ef.bikey" };
+
if (Directory.Exists(Path.Combine(Profile.ArmaPath, "keys")))
{
- foreach (var keyFile in Directory.GetFiles(Path.Combine(Profile.ArmaPath, "keys")))
+ foreach (var keyFile in Directory.GetFiles((Path.Combine(Profile.ArmaPath, "keys"))))
{
- if (Array.Exists(ignoredKeys, x => keyFile.Contains(x)))
+ var fileName = Path.GetFileName(keyFile);
+ if (ignoredKeys.Contains(fileName))
continue;
try
{
diff --git a/FASTER/ViewModel/SteamUpdaterViewModel.cs b/FASTER/ViewModel/SteamUpdaterViewModel.cs
index ee740d8..5039088 100644
--- a/FASTER/ViewModel/SteamUpdaterViewModel.cs
+++ b/FASTER/ViewModel/SteamUpdaterViewModel.cs
@@ -458,7 +458,7 @@ public async Task RunModsUpdater(ObservableCollection mods)
return;
}
- if (!SteamClient.Credentials.IsAnonymous) //IS SYNC NEABLED
+ if (!SteamClient.Credentials.IsAnonymous) //IS SYNC ENABLED
{
Parameters.Output += $"\n Getting manifest for {mod.WorkshopId}";
manifestId = SteamContentClient.GetPublishedFileDetailsAsync(mod.WorkshopId).Result.hcontent_file;
diff --git a/FASTER/Views/Profile.xaml b/FASTER/Views/Profile.xaml
index 0d82afc..33457a9 100644
--- a/FASTER/Views/Profile.xaml
+++ b/FASTER/Views/Profile.xaml
@@ -649,9 +649,9 @@
-
-
-
+
+
+
@@ -660,7 +660,7 @@
-
+
diff --git a/FASTER_Version.xml b/FASTER_Version.xml
index 62703b8..e999469 100644
--- a/FASTER_Version.xml
+++ b/FASTER_Version.xml
@@ -1,6 +1,6 @@
-
- 1.9.7.2
+ 1.9.7.3
https://github.com/Foxlider/FASTER/releases/latest/download/Release_x64.zip
https://github.com/Foxlider/FASTER/releases
true