From 225bcbbd22e63dbdee9b1173b3d18e753919969b Mon Sep 17 00:00:00 2001 From: RoosterDragon Date: Sat, 16 Oct 2021 16:52:06 +0100 Subject: [PATCH] Normalize all support dir paths to end with a directory separator. Previously, some paths used a separator and some did not. This broke some de-duplication logic in ExternalMods which tried to enumerate distinct paths but would end up running logic on the same directory more than one as it was provided both with and without a trailing directory separator. By normalizing the path this logic now works. --- OpenRA.Game/ExternalMods.cs | 59 +++++++++++++++---------------------- OpenRA.Game/Platform.cs | 22 +++++++------- 2 files changed, 34 insertions(+), 47 deletions(-) diff --git a/OpenRA.Game/ExternalMods.cs b/OpenRA.Game/ExternalMods.cs index d45fe1c189..a2b80e2469 100644 --- a/OpenRA.Game/ExternalMods.cs +++ b/OpenRA.Game/ExternalMods.cs @@ -66,12 +66,7 @@ namespace OpenRA // Several types of support directory types are available, depending on // how the player has installed and launched the game. // Read registration metadata from all of them - var sources = Enum.GetValues(typeof(SupportDirType)) - .Cast() - .Select(t => Platform.GetSupportDir(t)) - .Distinct(); - - foreach (var source in sources) + foreach (var source in GetSupportDirs(ModRegistration.User | ModRegistration.System)) { var metadataPath = Path.Combine(source, "ModMetadata"); if (!Directory.Exists(metadataPath)) @@ -148,7 +143,7 @@ namespace OpenRA if (stream != null) yaml.Value.Nodes.Add(new MiniYamlNode("Icon3x", Convert.ToBase64String(stream.ReadAllBytes()))); - var sources = new List(); + var sources = new HashSet(); if (registration.HasFlag(ModRegistration.System)) sources.Add(Platform.GetSupportDir(SupportDirType.System)); @@ -167,7 +162,7 @@ namespace OpenRA LoadMod(yaml.Value, forceRegistration: true); var lines = new List { yaml }.ToLines().ToArray(); - foreach (var source in sources.Distinct()) + foreach (var source in sources) { var metadataPath = Path.Combine(source, "ModMetadata"); @@ -193,20 +188,7 @@ namespace OpenRA /// internal void ClearInvalidRegistrations(ModRegistration registration) { - var sources = new List(); - if (registration.HasFlag(ModRegistration.System)) - sources.Add(Platform.GetSupportDir(SupportDirType.System)); - - if (registration.HasFlag(ModRegistration.User)) - { - // User support dir may be using the modern or legacy value, or overridden by the user - // Add all the possibilities and let the .Distinct() below ignore the duplicates - sources.Add(Platform.GetSupportDir(SupportDirType.User)); - sources.Add(Platform.GetSupportDir(SupportDirType.ModernUser)); - sources.Add(Platform.GetSupportDir(SupportDirType.LegacyUser)); - } - - foreach (var source in sources.Distinct()) + foreach (var source in GetSupportDirs(registration)) { var metadataPath = Path.Combine(source, "ModMetadata"); if (!Directory.Exists(metadataPath)) @@ -254,23 +236,10 @@ namespace OpenRA internal void Unregister(Manifest mod, ModRegistration registration) { - var sources = new List(); - if (registration.HasFlag(ModRegistration.System)) - sources.Add(Platform.GetSupportDir(SupportDirType.System)); - - if (registration.HasFlag(ModRegistration.User)) - { - // User support dir may be using the modern or legacy value, or overridden by the user - // Add all the possibilities and let the .Distinct() below ignore the duplicates - sources.Add(Platform.GetSupportDir(SupportDirType.User)); - sources.Add(Platform.GetSupportDir(SupportDirType.ModernUser)); - sources.Add(Platform.GetSupportDir(SupportDirType.LegacyUser)); - } - var key = ExternalMod.MakeKey(mod); mods.Remove(key); - foreach (var source in sources.Distinct()) + foreach (var source in GetSupportDirs(registration)) { var path = Path.Combine(source, "ModMetadata", key + ".yaml"); try @@ -286,6 +255,24 @@ namespace OpenRA } } + IEnumerable GetSupportDirs(ModRegistration registration) + { + var sources = new HashSet(4); + if (registration.HasFlag(ModRegistration.System)) + sources.Add(Platform.GetSupportDir(SupportDirType.System)); + + if (registration.HasFlag(ModRegistration.User)) + { + // User support dir may be using the modern or legacy value, or overridden by the user + // Add all the possibilities and let the HashSet ignore the duplicates + sources.Add(Platform.GetSupportDir(SupportDirType.User)); + sources.Add(Platform.GetSupportDir(SupportDirType.ModernUser)); + sources.Add(Platform.GetSupportDir(SupportDirType.LegacyUser)); + } + + return sources; + } + public ExternalMod this[string key] => mods[key]; public int Count => mods.Count; public ICollection Keys => mods.Keys; diff --git a/OpenRA.Game/Platform.cs b/OpenRA.Game/Platform.cs index e21326d0ea..3e201d00bd 100644 --- a/OpenRA.Game/Platform.cs +++ b/OpenRA.Game/Platform.cs @@ -100,8 +100,8 @@ namespace OpenRA { case PlatformType.Windows: { - modernUserSupportPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), "OpenRA"); - legacyUserSupportPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.Personal), "OpenRA"); + modernUserSupportPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), "OpenRA") + Path.DirectorySeparatorChar; + legacyUserSupportPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.Personal), "OpenRA") + Path.DirectorySeparatorChar; systemSupportPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.CommonApplicationData), "OpenRA") + Path.DirectorySeparatorChar; break; } @@ -110,7 +110,7 @@ namespace OpenRA { modernUserSupportPath = legacyUserSupportPath = Path.Combine( Environment.GetFolderPath(Environment.SpecialFolder.Personal), - "Library", "Application Support", "OpenRA"); + "Library", "Application Support", "OpenRA") + Path.DirectorySeparatorChar; systemSupportPath = "/Library/Application Support/OpenRA/"; break; @@ -118,13 +118,13 @@ namespace OpenRA case PlatformType.Linux: { - legacyUserSupportPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.Personal), ".openra"); + legacyUserSupportPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.Personal), ".openra") + Path.DirectorySeparatorChar; var xdgConfigHome = Environment.GetEnvironmentVariable("XDG_CONFIG_HOME"); if (string.IsNullOrEmpty(xdgConfigHome)) - xdgConfigHome = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.Personal), ".config"); + xdgConfigHome = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.Personal), ".config") + Path.DirectorySeparatorChar; - modernUserSupportPath = Path.Combine(xdgConfigHome, "openra"); + modernUserSupportPath = Path.Combine(xdgConfigHome, "openra") + Path.DirectorySeparatorChar; systemSupportPath = "/var/games/openra/"; break; @@ -132,22 +132,22 @@ namespace OpenRA default: { - modernUserSupportPath = legacyUserSupportPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.Personal), ".openra"); + modernUserSupportPath = legacyUserSupportPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.Personal), ".openra") + Path.DirectorySeparatorChar; systemSupportPath = "/var/games/openra/"; break; } } // Use a local directory in the game root if it exists (shared with the system support dir) - var localSupportDir = Path.Combine(EngineDir, "Support"); + var localSupportDir = Path.Combine(EngineDir, "Support") + Path.DirectorySeparatorChar; if (Directory.Exists(localSupportDir)) - userSupportPath = systemSupportPath = localSupportDir + Path.DirectorySeparatorChar; + userSupportPath = systemSupportPath = localSupportDir; // Use the fallback directory if it exists and the preferred one does not else if (!Directory.Exists(modernUserSupportPath) && Directory.Exists(legacyUserSupportPath)) - userSupportPath = legacyUserSupportPath + Path.DirectorySeparatorChar; + userSupportPath = legacyUserSupportPath; else - userSupportPath = modernUserSupportPath + Path.DirectorySeparatorChar; + userSupportPath = modernUserSupportPath; supportDirInitialized = true; }