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