Rework mod enumeration / caching.

- Replaced ModMetadata.AllMods with Game.Mods.
- Store / reference mod Manifest instead of ModMetadata.
- Removes engine dependency on ModContent class.
This commit is contained in:
Paul Chote
2016-08-05 17:07:04 +01:00
parent 45a596953e
commit 3df9efb95d
33 changed files with 301 additions and 219 deletions

View File

@@ -13,7 +13,6 @@ using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
using System.Reflection;
using OpenRA.Primitives; using OpenRA.Primitives;
namespace OpenRA.FileSystem namespace OpenRA.FileSystem
@@ -34,9 +33,15 @@ namespace OpenRA.FileSystem
// Mod packages that should not be disposed // Mod packages that should not be disposed
readonly List<IReadOnlyPackage> modPackages = new List<IReadOnlyPackage>(); readonly List<IReadOnlyPackage> modPackages = new List<IReadOnlyPackage>();
readonly IReadOnlyDictionary<string, Manifest> installedMods;
Cache<string, List<IReadOnlyPackage>> fileIndex = new Cache<string, List<IReadOnlyPackage>>(_ => new List<IReadOnlyPackage>()); Cache<string, List<IReadOnlyPackage>> fileIndex = new Cache<string, List<IReadOnlyPackage>>(_ => new List<IReadOnlyPackage>());
public FileSystem(IReadOnlyDictionary<string, Manifest> installedMods)
{
this.installedMods = installedMods;
}
public IReadOnlyPackage OpenPackage(string filename) public IReadOnlyPackage OpenPackage(string filename)
{ {
if (filename.EndsWith(".mix", StringComparison.InvariantCultureIgnoreCase)) if (filename.EndsWith(".mix", StringComparison.InvariantCultureIgnoreCase))
@@ -108,9 +113,9 @@ namespace OpenRA.FileSystem
{ {
name = name.Substring(1); name = name.Substring(1);
ModMetadata mod; Manifest mod;
if (!ModMetadata.AllMods.TryGetValue(name, out mod)) if (!installedMods.TryGetValue(name, out mod))
throw new InvalidOperationException("Could not load mod '{0}'. Available mods: {1}".F(name, ModMetadata.AllMods.Keys.JoinWith(", "))); throw new InvalidOperationException("Could not load mod '{0}'. Available mods: {1}".F(name, installedMods.Keys.JoinWith(", ")));
package = mod.Package; package = mod.Package;
modPackages.Add(package); modPackages.Add(package);

View File

@@ -21,6 +21,7 @@ using System.Reflection;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using OpenRA.Chat; using OpenRA.Chat;
using OpenRA.FileSystem;
using OpenRA.Graphics; using OpenRA.Graphics;
using OpenRA.Network; using OpenRA.Network;
using OpenRA.Primitives; using OpenRA.Primitives;
@@ -35,6 +36,8 @@ namespace OpenRA
public const int Timestep = 40; public const int Timestep = 40;
public const int TimestepJankThreshold = 250; // Don't catch up for delays larger than 250ms public const int TimestepJankThreshold = 250; // Don't catch up for delays larger than 250ms
public static InstalledMods Mods { get; private set; }
public static ModData ModData; public static ModData ModData;
public static Settings Settings; public static Settings Settings;
public static ICursor Cursor; public static ICursor Cursor;
@@ -300,22 +303,23 @@ namespace OpenRA
GlobalChat = new GlobalChat(); GlobalChat = new GlobalChat();
Mods = new InstalledMods();
Console.WriteLine("Available mods:"); Console.WriteLine("Available mods:");
foreach (var mod in ModMetadata.AllMods) foreach (var mod in Mods)
Console.WriteLine("\t{0}: {1} ({2})", mod.Key, mod.Value.Title, mod.Value.Version); Console.WriteLine("\t{0}: {1} ({2})", mod.Key, mod.Value.Mod.Title, mod.Value.Mod.Version);
InitializeMod(Settings.Game.Mod, args); InitializeMod(Settings.Game.Mod, args);
} }
public static bool IsModInstalled(string modId) public static bool IsModInstalled(string modId)
{ {
return ModMetadata.AllMods[modId].RequiresMods.All(IsModInstalled); return Mods.ContainsKey(modId) && Mods[modId].RequiresMods.All(IsModInstalled);
} }
public static bool IsModInstalled(KeyValuePair<string, string> mod) public static bool IsModInstalled(KeyValuePair<string, string> mod)
{ {
return ModMetadata.AllMods.ContainsKey(mod.Key) return Mods.ContainsKey(mod.Key)
&& ModMetadata.AllMods[mod.Key].Version == mod.Value && Mods[mod.Key].Mod.Version == mod.Value
&& IsModInstalled(mod.Key); && IsModInstalled(mod.Key);
} }
@@ -347,7 +351,7 @@ namespace OpenRA
ModData = null; ModData = null;
// Fall back to default if the mod doesn't exist or has missing prerequisites. // Fall back to default if the mod doesn't exist or has missing prerequisites.
if (!ModMetadata.AllMods.ContainsKey(mod) || !IsModInstalled(mod)) if (!IsModInstalled(mod))
mod = new GameSettings().Mod; mod = new GameSettings().Mod;
Console.WriteLine("Loading mod: {0}", mod); Console.WriteLine("Loading mod: {0}", mod);
@@ -355,7 +359,7 @@ namespace OpenRA
Sound.StopVideo(); Sound.StopVideo();
ModData = new ModData(mod, true); ModData = new ModData(Mods[mod], Mods, true);
using (new PerfTimer("LoadMaps")) using (new PerfTimer("LoadMaps"))
ModData.MapCache.LoadMaps(); ModData.MapCache.LoadMaps();
@@ -468,7 +472,7 @@ namespace OpenRA
ThreadPool.QueueUserWorkItem(_ => ThreadPool.QueueUserWorkItem(_ =>
{ {
var mod = ModData.Manifest.Mod; var mod = ModData.Manifest.Mod;
var directory = Platform.ResolvePath("^", "Screenshots", mod.Id, mod.Version); var directory = Platform.ResolvePath("^", "Screenshots", ModData.Manifest.Id, mod.Version);
Directory.CreateDirectory(directory); Directory.CreateDirectory(directory);
var filename = TimestampedFilename(); var filename = TimestampedFilename();

View File

@@ -16,10 +16,12 @@ namespace OpenRA
public class Utility public class Utility
{ {
public readonly ModData ModData; public readonly ModData ModData;
public readonly InstalledMods Mods;
public Utility(ModData modData) public Utility(ModData modData, InstalledMods mods)
{ {
ModData = modData; ModData = modData;
Mods = mods;
} }
} }

View File

@@ -0,0 +1,117 @@
#region Copyright & License Information
/*
* Copyright 2007-2016 The OpenRA Developers (see AUTHORS)
* This file is part of OpenRA, which is free software. It is made
* available to you under the terms of the GNU General Public License
* as published by the Free Software Foundation, either version 3 of
* the License, or (at your option) any later version. For more
* information, see COPYING.
*/
#endregion
using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using OpenRA.FileSystem;
using OpenRA.Primitives;
namespace OpenRA
{
public class InstalledMods : IReadOnlyDictionary<string, Manifest>
{
readonly Dictionary<string, Manifest> mods;
public InstalledMods()
{
mods = GetInstalledMods();
}
static IEnumerable<Pair<string, string>> GetCandidateMods()
{
// Get mods that are in the game folder.
var basePath = Platform.ResolvePath(Path.Combine(".", "mods"));
var mods = Directory.GetDirectories(basePath)
.Select(x => Pair.New(x.Substring(basePath.Length + 1), x))
.ToList();
foreach (var m in Directory.GetFiles(basePath, "*.oramod"))
mods.Add(Pair.New(Path.GetFileNameWithoutExtension(m), m));
// Get mods that are in the support folder.
var supportPath = Platform.ResolvePath(Path.Combine("^", "mods"));
if (!Directory.Exists(supportPath))
return mods;
foreach (var pair in Directory.GetDirectories(supportPath).ToDictionary(x => x.Substring(supportPath.Length + 1)))
mods.Add(Pair.New(pair.Key, pair.Value));
foreach (var m in Directory.GetFiles(supportPath, "*.oramod"))
mods.Add(Pair.New(Path.GetFileNameWithoutExtension(m), m));
return mods;
}
static Manifest LoadMod(string id, string path)
{
IReadOnlyPackage package = null;
try
{
if (Directory.Exists(path))
package = new Folder(path);
else
{
try
{
using (var fileStream = File.OpenRead(path))
package = new ZipFile(fileStream, path);
}
catch
{
throw new InvalidDataException(path + " is not a valid mod package");
}
}
if (!package.Contains("mod.yaml"))
throw new InvalidDataException(path + " is not a valid mod package");
// Mods in the support directory and oramod packages (which are listed later
// in the CandidateMods list) override mods in the main install.
return new Manifest(id, package);
}
catch (Exception)
{
if (package != null)
package.Dispose();
return null;
}
}
static Dictionary<string, Manifest> GetInstalledMods()
{
var ret = new Dictionary<string, Manifest>();
foreach (var pair in GetCandidateMods())
{
var mod = LoadMod(pair.First, pair.Second);
// Mods in the support directory and oramod packages (which are listed later
// in the CandidateMods list) override mods in the main install.
if (mod != null)
ret[pair.First] = mod;
}
return ret;
}
public Manifest this[string key] { get { return mods[key]; } }
public int Count { get { return mods.Count; } }
public ICollection<string> Keys { get { return mods.Keys; } }
public ICollection<Manifest> Values { get { return mods.Values; } }
public bool ContainsKey(string key) { return mods.ContainsKey(key); }
public IEnumerator<KeyValuePair<string, Manifest>> GetEnumerator() { return mods.GetEnumerator(); }
public bool TryGetValue(string key, out Manifest value) { return mods.TryGetValue(key, out value); }
IEnumerator IEnumerable.GetEnumerator() { return mods.GetEnumerator(); }
}
}

View File

@@ -34,6 +34,8 @@ namespace OpenRA
/// <summary> Describes what is to be loaded in order to run a mod. </summary> /// <summary> Describes what is to be loaded in order to run a mod. </summary>
public class Manifest public class Manifest
{ {
public readonly string Id;
public readonly IReadOnlyPackage Package;
public readonly ModMetadata Mod; public readonly ModMetadata Mod;
public readonly string[] public readonly string[]
Rules, ServerTraits, Rules, ServerTraits,
@@ -60,14 +62,15 @@ namespace OpenRA
readonly TypeDictionary modules = new TypeDictionary(); readonly TypeDictionary modules = new TypeDictionary();
readonly Dictionary<string, MiniYaml> yaml; readonly Dictionary<string, MiniYaml> yaml;
public Manifest(string modId) bool customDataLoaded;
{
var package = ModMetadata.AllMods[modId].Package;
public Manifest(string modId, IReadOnlyPackage package)
{
Id = modId;
Package = package;
yaml = new MiniYaml(null, MiniYaml.FromStream(package.GetStream("mod.yaml"), "mod.yaml")).ToDictionary(); yaml = new MiniYaml(null, MiniYaml.FromStream(package.GetStream("mod.yaml"), "mod.yaml")).ToDictionary();
Mod = FieldLoader.Load<ModMetadata>(yaml["Metadata"]); Mod = FieldLoader.Load<ModMetadata>(yaml["Metadata"]);
Mod.Id = modId;
// TODO: Use fieldloader // TODO: Use fieldloader
MapFolders = YamlDictionary(yaml, "MapFolders"); MapFolders = YamlDictionary(yaml, "MapFolders");
@@ -106,7 +109,7 @@ namespace OpenRA
RequiresMods = yaml["RequiresMods"].ToDictionary(my => my.Value); RequiresMods = yaml["RequiresMods"].ToDictionary(my => my.Value);
// Allow inherited mods to import parent maps. // Allow inherited mods to import parent maps.
var compat = new List<string> { Mod.Id }; var compat = new List<string> { Id };
if (yaml.ContainsKey("SupportsMapsFrom")) if (yaml.ContainsKey("SupportsMapsFrom"))
compat.AddRange(yaml["SupportsMapsFrom"].Value.Split(',').Select(c => c.Trim())); compat.AddRange(yaml["SupportsMapsFrom"].Value.Split(',').Select(c => c.Trim()));
@@ -147,6 +150,8 @@ namespace OpenRA
modules.Add(module); modules.Add(module);
} }
customDataLoaded = true;
} }
static string[] YamlList(Dictionary<string, MiniYaml> yaml, string key, bool parsePaths = false) static string[] YamlList(Dictionary<string, MiniYaml> yaml, string key, bool parsePaths = false)
@@ -171,8 +176,12 @@ namespace OpenRA
return modules.Contains<T>(); return modules.Contains<T>();
} }
/// <summary>Load a cached IGlobalModData instance.</summary>
public T Get<T>() where T : IGlobalModData public T Get<T>() where T : IGlobalModData
{ {
if (!customDataLoaded)
throw new InvalidOperationException("Attempted to call Manifest.Get() before loading custom data!");
var module = modules.GetOrDefault<T>(); var module = modules.GetOrDefault<T>();
// Lazily create the default values if not explicitly defined. // Lazily create the default values if not explicitly defined.
@@ -184,5 +193,36 @@ namespace OpenRA
return module; return module;
} }
/// <summary>
/// Load an uncached IGlobalModData instance directly from the manifest yaml.
/// This should only be used by external mods that want to query data from this mod.
/// </summary>
public T Get<T>(ObjectCreator oc) where T : IGlobalModData
{
MiniYaml data;
var t = typeof(T);
if (!yaml.TryGetValue(t.Name, out data))
{
// Lazily create the default values if not explicitly defined.
return (T)oc.CreateBasic(t);
}
IGlobalModData module;
var ctor = t.GetConstructor(new[] { typeof(MiniYaml) });
if (ctor != null)
{
// Class has opted-in to DIY initialization
module = (IGlobalModData)ctor.Invoke(new object[] { data.Value });
}
else
{
// Automatically load the child nodes using FieldLoader
module = oc.CreateObject<IGlobalModData>(t.Name);
FieldLoader.Load(module, data);
}
return (T)module;
}
} }
} }

View File

@@ -33,7 +33,7 @@ namespace OpenRA
public ILoadScreen LoadScreen { get; private set; } public ILoadScreen LoadScreen { get; private set; }
public VoxelLoader VoxelLoader { get; private set; } public VoxelLoader VoxelLoader { get; private set; }
public CursorProvider CursorProvider { get; private set; } public CursorProvider CursorProvider { get; private set; }
public FS ModFiles = new FS(); public FS ModFiles;
public IReadOnlyFileSystem DefaultFileSystem { get { return ModFiles; } } public IReadOnlyFileSystem DefaultFileSystem { get { return ModFiles; } }
readonly Lazy<Ruleset> defaultRules; readonly Lazy<Ruleset> defaultRules;
@@ -45,10 +45,14 @@ namespace OpenRA
readonly Lazy<IReadOnlyDictionary<string, SequenceProvider>> defaultSequences; readonly Lazy<IReadOnlyDictionary<string, SequenceProvider>> defaultSequences;
public IReadOnlyDictionary<string, SequenceProvider> DefaultSequences { get { return defaultSequences.Value; } } public IReadOnlyDictionary<string, SequenceProvider> DefaultSequences { get { return defaultSequences.Value; } }
public ModData(string mod, bool useLoadScreen = false) public ModData(Manifest mod, InstalledMods mods, bool useLoadScreen = false)
{ {
Languages = new string[0]; Languages = new string[0];
Manifest = new Manifest(mod);
ModFiles = new FS(mods);
// Take a local copy of the manifest
Manifest = new Manifest(mod.Id, mod.Package);
ModFiles.LoadFromManifest(Manifest); ModFiles.LoadFromManifest(Manifest);
ObjectCreator = new ObjectCreator(Manifest, ModFiles); ObjectCreator = new ObjectCreator(Manifest, ModFiles);

View File

@@ -9,118 +9,14 @@
*/ */
#endregion #endregion
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using OpenRA.FileSystem;
using OpenRA.Primitives;
namespace OpenRA namespace OpenRA
{ {
public class ModMetadata public class ModMetadata
{ {
public static readonly Dictionary<string, ModMetadata> AllMods = ValidateMods();
public string Id;
public string Title; public string Title;
public string Description; public string Description;
public string Version; public string Version;
public string Author; public string Author;
public bool Hidden; public bool Hidden;
public Dictionary<string, string> RequiresMods;
public ModContent ModContent;
public IReadOnlyPackage Package;
static Dictionary<string, ModMetadata> ValidateMods()
{
var ret = new Dictionary<string, ModMetadata>();
foreach (var pair in GetCandidateMods())
{
IReadOnlyPackage package = null;
try
{
if (Directory.Exists(pair.Second))
package = new Folder(pair.Second);
else
{
try
{
using (var fileStream = File.OpenRead(pair.Second))
package = new ZipFile(fileStream, pair.Second);
}
catch
{
throw new InvalidDataException(pair.Second + " is not a valid mod package");
}
}
if (!package.Contains("mod.yaml"))
{
package.Dispose();
continue;
}
var yaml = new MiniYaml(null, MiniYaml.FromStream(package.GetStream("mod.yaml"), "mod.yaml"));
var nd = yaml.ToDictionary();
if (!nd.ContainsKey("Metadata"))
{
package.Dispose();
continue;
}
var metadata = FieldLoader.Load<ModMetadata>(nd["Metadata"]);
metadata.Id = pair.First;
metadata.Package = package;
if (nd.ContainsKey("RequiresMods"))
metadata.RequiresMods = nd["RequiresMods"].ToDictionary(my => my.Value);
else
metadata.RequiresMods = new Dictionary<string, string>();
if (nd.ContainsKey("ModContent"))
metadata.ModContent = FieldLoader.Load<ModContent>(nd["ModContent"]);
// Mods in the support directory and oramod packages (which are listed later
// in the CandidateMods list) override mods in the main install.
ret[pair.First] = metadata;
}
catch (Exception ex)
{
if (package != null)
package.Dispose();
Console.WriteLine("An exception occurred when trying to load ModMetadata for `{0}`:".F(pair.First));
Console.WriteLine(ex.Message);
}
}
return ret;
}
static IEnumerable<Pair<string, string>> GetCandidateMods()
{
// Get mods that are in the game folder.
var basePath = Platform.ResolvePath(Path.Combine(".", "mods"));
var mods = Directory.GetDirectories(basePath)
.Select(x => Pair.New(x.Substring(basePath.Length + 1), x))
.ToList();
foreach (var m in Directory.GetFiles(basePath, "*.oramod"))
mods.Add(Pair.New(Path.GetFileNameWithoutExtension(m), m));
// Get mods that are in the support folder.
var supportPath = Platform.ResolvePath(Path.Combine("^", "mods"));
if (!Directory.Exists(supportPath))
return mods;
foreach (var pair in Directory.GetDirectories(supportPath).ToDictionary(x => x.Substring(supportPath.Length + 1)))
mods.Add(Pair.New(pair.Key, pair.Value));
foreach (var m in Directory.GetFiles(supportPath, "*.oramod"))
mods.Add(Pair.New(Path.GetFileNameWithoutExtension(m), m));
return mods;
}
} }
} }

View File

@@ -38,14 +38,14 @@ namespace OpenRA.Network
{ {
FieldLoader.Load(this, yaml); FieldLoader.Load(this, yaml);
ModMetadata mod; Manifest mod;
var modVersion = Mods.Split('@'); var modVersion = Mods.Split('@');
if (modVersion.Length == 2 && ModMetadata.AllMods.TryGetValue(modVersion[0], out mod)) if (modVersion.Length == 2 && Game.Mods.TryGetValue(modVersion[0], out mod))
{ {
ModId = modVersion[0]; ModId = modVersion[0];
ModVersion = modVersion[1]; ModVersion = modVersion[1];
ModLabel = "{0} ({1})".F(mod.Title, modVersion[1]); ModLabel = "{0} ({1})".F(mod.Mod.Title, modVersion[1]);
IsCompatible = Game.Settings.Debug.IgnoreVersionMismatch || ModVersion == mod.Version; IsCompatible = Game.Settings.Debug.IgnoreVersionMismatch || ModVersion == mod.Mod.Version;
} }
else else
ModLabel = "Unknown mod: {0}".F(Mods); ModLabel = "Unknown mod: {0}".F(Mods);

View File

@@ -44,8 +44,8 @@ namespace OpenRA.Network
void StartSavingReplay(byte[] initialContent) void StartSavingReplay(byte[] initialContent)
{ {
var filename = chooseFilename(); var filename = chooseFilename();
var mod = Game.ModData.Manifest.Mod; var mod = Game.ModData.Manifest;
var dir = Platform.ResolvePath("^", "Replays", mod.Id, mod.Version); var dir = Platform.ResolvePath("^", "Replays", mod.Id, mod.Mod.Version);
if (!Directory.Exists(dir)) if (!Directory.Exists(dir))
Directory.CreateDirectory(dir); Directory.CreateDirectory(dir);

View File

@@ -132,13 +132,13 @@ namespace OpenRA.Network
case "HandshakeRequest": case "HandshakeRequest":
{ {
// Switch to the server's mod if we need and are able to // Switch to the server's mod if we need and are able to
var mod = Game.ModData.Manifest.Mod; var mod = Game.ModData.Manifest;
var request = HandshakeRequest.Deserialize(order.TargetString); var request = HandshakeRequest.Deserialize(order.TargetString);
ModMetadata serverMod; Manifest serverMod;
if (request.Mod != mod.Id && if (request.Mod != mod.Id &&
ModMetadata.AllMods.TryGetValue(request.Mod, out serverMod) && Game.Mods.TryGetValue(request.Mod, out serverMod) &&
serverMod.Version == request.Version) serverMod.Mod.Version == request.Version)
{ {
var replay = orderManager.Connection as ReplayConnection; var replay = orderManager.Connection as ReplayConnection;
var launchCommand = replay != null ? var launchCommand = replay != null ?
@@ -170,7 +170,7 @@ namespace OpenRA.Network
{ {
Client = info, Client = info,
Mod = mod.Id, Mod = mod.Id,
Version = mod.Version, Version = mod.Mod.Version,
Password = orderManager.Password Password = orderManager.Password
}; };

View File

@@ -245,6 +245,7 @@
<Compile Include="FileSystem\ZipFolder.cs" /> <Compile Include="FileSystem\ZipFolder.cs" />
<Compile Include="Primitives\float3.cs" /> <Compile Include="Primitives\float3.cs" />
<Compile Include="ModContent.cs" /> <Compile Include="ModContent.cs" />
<Compile Include="InstalledMods.cs" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<Compile Include="FileSystem\D2kSoundResources.cs" /> <Compile Include="FileSystem\D2kSoundResources.cs" />

View File

@@ -158,7 +158,7 @@ namespace OpenRA.Server
foreach (var t in serverTraits.WithInterface<INotifyServerStart>()) foreach (var t in serverTraits.WithInterface<INotifyServerStart>())
t.ServerStarted(this); t.ServerStarted(this);
Log.Write("server", "Initial mod: {0}", ModData.Manifest.Mod.Id); Log.Write("server", "Initial mod: {0}", ModData.Manifest.Id);
Log.Write("server", "Initial map: {0}", LobbyInfo.GlobalSettings.Map); Log.Write("server", "Initial map: {0}", LobbyInfo.GlobalSettings.Map);
var timeout = serverTraits.WithInterface<ITick>().Min(t => t.TickTimeout); var timeout = serverTraits.WithInterface<ITick>().Min(t => t.TickTimeout);
@@ -262,7 +262,7 @@ namespace OpenRA.Server
// Dispatch a handshake order // Dispatch a handshake order
var request = new HandshakeRequest var request = new HandshakeRequest
{ {
Mod = ModData.Manifest.Mod.Id, Mod = ModData.Manifest.Id,
Version = ModData.Manifest.Mod.Version, Version = ModData.Manifest.Mod.Version,
Map = LobbyInfo.GlobalSettings.Map Map = LobbyInfo.GlobalSettings.Map
}; };
@@ -327,7 +327,7 @@ namespace OpenRA.Server
else else
client.Color = HSLColor.FromRGB(255, 255, 255); client.Color = HSLColor.FromRGB(255, 255, 255);
if (ModData.Manifest.Mod.Id != handshake.Mod) if (ModData.Manifest.Id != handshake.Mod)
{ {
Log.Write("server", "Rejected connection from {0}; mods do not match.", Log.Write("server", "Rejected connection from {0}; mods do not match.",
newConn.Socket.RemoteEndPoint); newConn.Socket.RemoteEndPoint);

View File

@@ -176,7 +176,7 @@ namespace OpenRA
gameInfo = new GameInformation gameInfo = new GameInformation
{ {
Mod = Game.ModData.Manifest.Mod.Id, Mod = Game.ModData.Manifest.Id,
Version = Game.ModData.Manifest.Mod.Version, Version = Game.ModData.Manifest.Mod.Version,
MapUid = Map.Uid, MapUid = Map.Uid,

View File

@@ -79,7 +79,7 @@ namespace OpenRA.Mods.Common.LoadScreens
if (replayMeta != null) if (replayMeta != null)
{ {
var mod = replayMeta.GameInfo.Mod; var mod = replayMeta.GameInfo.Mod;
if (mod != null && mod != Game.ModData.Manifest.Mod.Id && ModMetadata.AllMods.ContainsKey(mod)) if (mod != null && mod != Game.ModData.Manifest.Id && Game.Mods.ContainsKey(mod))
Game.InitializeMod(mod, args); Game.InitializeMod(mod, args);
} }

View File

@@ -52,7 +52,7 @@ namespace OpenRA.Mods.Common.Server
lastPing = Game.RunTime; lastPing = Game.RunTime;
isBusy = true; isBusy = true;
var mod = server.ModData.Manifest.Mod; var mod = server.ModData.Manifest;
// important to grab these on the main server thread, not in the worker we're about to spawn -- they may be modified // important to grab these on the main server thread, not in the worker we're about to spawn -- they may be modified
// by the main thread as clients join and leave. // by the main thread as clients join and leave.
@@ -79,7 +79,7 @@ namespace OpenRA.Mods.Common.Server
(int)server.State, (int)server.State,
numPlayers, numPlayers,
numBots, numBots,
"{0}@{1}".F(mod.Id, mod.Version), "{0}@{1}".F(mod.Id, mod.Mod.Version),
server.LobbyInfo.GlobalSettings.Map, server.LobbyInfo.GlobalSettings.Map,
numSlots, numSlots,
numSpectators, numSpectators,

View File

@@ -74,7 +74,7 @@ namespace OpenRA.Mods.Common.UtilityCommands
Author = "Westwood Studios", Author = "Westwood Studios",
}; };
Map.RequiresMod = ModData.Manifest.Mod.Id; Map.RequiresMod = ModData.Manifest.Id;
SetBounds(Map, mapSection); SetBounds(Map, mapSection);

View File

@@ -11,7 +11,6 @@
using System; using System;
using System.IO; using System.IO;
using System.Linq;
using OpenRA.FileSystem; using OpenRA.FileSystem;
namespace OpenRA.Mods.Common.UtilityCommands namespace OpenRA.Mods.Common.UtilityCommands
@@ -31,7 +30,7 @@ namespace OpenRA.Mods.Common.UtilityCommands
var filename = Path.GetFileName(args[1]); var filename = Path.GetFileName(args[1]);
var path = Path.GetDirectoryName(args[1]); var path = Path.GetDirectoryName(args[1]);
var fs = new OpenRA.FileSystem.FileSystem(); var fs = new FileSystem.FileSystem(utility.Mods);
fs.Mount(path, "parent"); fs.Mount(path, "parent");
var package = new InstallShieldPackage(fs, "parent|" + filename); var package = new InstallShieldPackage(fs, "parent|" + filename);

View File

@@ -31,7 +31,7 @@ namespace OpenRA.Mods.Common.UtilityCommands
var filename = Path.GetFileName(args[1]); var filename = Path.GetFileName(args[1]);
var path = Path.GetDirectoryName(args[1]); var path = Path.GetDirectoryName(args[1]);
var fs = new OpenRA.FileSystem.FileSystem(); var fs = new FileSystem.FileSystem(utility.Mods);
// Needed to access the global mix database // Needed to access the global mix database
fs.LoadFromManifest(utility.ModData.Manifest); fs.LoadFromManifest(utility.ModData.Manifest);

View File

@@ -39,14 +39,14 @@ namespace OpenRA.Mods.Common.UtilityCommands
remap[i] = i; remap[i] = i;
var srcMod = args[1].Split(':')[0]; var srcMod = args[1].Split(':')[0];
var srcModData = new ModData(srcMod); var srcModData = new ModData(utility.Mods[srcMod], utility.Mods);
Game.ModData = srcModData; Game.ModData = srcModData;
var srcPaletteInfo = srcModData.DefaultRules.Actors["player"].TraitInfo<PlayerColorPaletteInfo>(); var srcPaletteInfo = srcModData.DefaultRules.Actors["player"].TraitInfo<PlayerColorPaletteInfo>();
var srcRemapIndex = srcPaletteInfo.RemapIndex; var srcRemapIndex = srcPaletteInfo.RemapIndex;
var destMod = args[2].Split(':')[0]; var destMod = args[2].Split(':')[0];
var destModData = new ModData(destMod); var destModData = new ModData(utility.Mods[destMod], utility.Mods);
Game.ModData = destModData; Game.ModData = destModData;
var destPaletteInfo = destModData.DefaultRules.Actors["player"].TraitInfo<PlayerColorPaletteInfo>(); var destPaletteInfo = destModData.DefaultRules.Actors["player"].TraitInfo<PlayerColorPaletteInfo>();
var destRemapIndex = destPaletteInfo.RemapIndex; var destRemapIndex = destPaletteInfo.RemapIndex;

View File

@@ -346,7 +346,7 @@ namespace OpenRA.Mods.Common.UtilityCommands
{ {
foreach (var node in nodes) foreach (var node in nodes)
{ {
if (engineVersion < 20160730 && modData.Manifest.Mod.Id == "d2k" && depth == 2) if (engineVersion < 20160730 && modData.Manifest.Id == "d2k" && depth == 2)
{ {
if (node.Key == "Start") if (node.Key == "Start")
node.Value.Value = RemapD2k106Sequence(FieldLoader.GetValue<int>("", node.Value.Value)).ToString(); node.Value.Value = RemapD2k106Sequence(FieldLoader.GetValue<int>("", node.Value.Value)).ToString();
@@ -416,7 +416,7 @@ namespace OpenRA.Mods.Common.UtilityCommands
foreach (var node in nodes) foreach (var node in nodes)
{ {
// Fix RA building footprints to not use _ when it's not necessary // Fix RA building footprints to not use _ when it's not necessary
if (engineVersion < 20160619 && modData.Manifest.Mod.Id == "ra" && depth == 1) if (engineVersion < 20160619 && modData.Manifest.Id == "ra" && depth == 1)
{ {
var buildings = new List<string>() { "tsla", "gap", "agun", "apwr", "fapw" }; var buildings = new List<string>() { "tsla", "gap", "agun", "apwr", "fapw" };
if (buildings.Contains(parent.Value.Value) && node.Key == "Location") if (buildings.Contains(parent.Value.Value) && node.Key == "Location")
@@ -424,7 +424,7 @@ namespace OpenRA.Mods.Common.UtilityCommands
} }
// Fix TD building footprints to not use _ when it's not necessary // Fix TD building footprints to not use _ when it's not necessary
if (engineVersion < 20160619 && modData.Manifest.Mod.Id == "cnc" && depth == 1) if (engineVersion < 20160619 && modData.Manifest.Id == "cnc" && depth == 1)
{ {
var buildings = new List<string>() { "atwr", "obli", "tmpl", "weap", "hand" }; var buildings = new List<string>() { "atwr", "obli", "tmpl", "weap", "hand" };
if (buildings.Contains(parent.Value.Value) && node.Key == "Location") if (buildings.Contains(parent.Value.Value) && node.Key == "Location")

View File

@@ -165,7 +165,7 @@ namespace OpenRA.Mods.Common.Widgets.Logic
if (playerDefinitions != null) if (playerDefinitions != null)
map.PlayerDefinitions = playerDefinitions; map.PlayerDefinitions = playerDefinitions;
map.RequiresMod = modData.Manifest.Mod.Id; map.RequiresMod = modData.Manifest.Id;
var combinedPath = Platform.ResolvePath(Path.Combine(selectedDirectory.Folder.Name, filename.Text + fileTypes[fileType].Extension)); var combinedPath = Platform.ResolvePath(Path.Combine(selectedDirectory.Folder.Name, filename.Text + fileTypes[fileType].Extension));

View File

@@ -21,7 +21,7 @@ namespace OpenRA.Mods.Common.Widgets.Logic
{ {
var panel = widget.Get("INSTALL_MOD_PANEL"); var panel = widget.Get("INSTALL_MOD_PANEL");
var mods = ModMetadata.AllMods[modId].RequiresMods.Where(m => !Game.IsModInstalled(m)).Select(m => "{0} ({1})".F(m.Key, m.Value)); var mods = Game.Mods[modId].RequiresMods.Where(m => !Game.IsModInstalled(m)).Select(m => "{0} ({1})".F(m.Key, m.Value));
var text = string.Join(", ", mods); var text = string.Join(", ", mods);
panel.Get<LabelWidget>("MOD_LIST").Text = text; panel.Get<LabelWidget>("MOD_LIST").Text = text;

View File

@@ -10,7 +10,6 @@
#endregion #endregion
using System; using System;
using System.IO;
using System.Linq; using System.Linq;
using OpenRA.Widgets; using OpenRA.Widgets;
@@ -24,11 +23,12 @@ namespace OpenRA.Mods.Common.Widgets.Logic
bool discAvailable; bool discAvailable;
[ObjectCreator.UseCtor] [ObjectCreator.UseCtor]
public ModContentLogic(Widget widget, string modId, Action onCancel) public ModContentLogic(Widget widget, Manifest mod, ModContent content, Action onCancel)
{ {
this.content = content;
var panel = widget.Get("CONTENT_PANEL"); var panel = widget.Get("CONTENT_PANEL");
content = ModMetadata.AllMods[modId].ModContent;
scrollPanel = panel.Get<ScrollPanelWidget>("PACKAGES"); scrollPanel = panel.Get<ScrollPanelWidget>("PACKAGES");
template = scrollPanel.Get<ContainerWidget>("PACKAGE_TEMPLATE"); template = scrollPanel.Get<ContainerWidget>("PACKAGE_TEMPLATE");

View File

@@ -17,13 +17,10 @@ namespace OpenRA.Mods.Common.Widgets.Logic
public class ModContentPromptLogic : ChromeLogic public class ModContentPromptLogic : ChromeLogic
{ {
[ObjectCreator.UseCtor] [ObjectCreator.UseCtor]
public ModContentPromptLogic(Widget widget, string modId, Action continueLoading) public ModContentPromptLogic(Widget widget, Manifest mod, ModContent content, Action continueLoading)
{ {
var panel = widget.Get("CONTENT_PROMPT_PANEL"); var panel = widget.Get("CONTENT_PROMPT_PANEL");
var mod = ModMetadata.AllMods[modId];
var content = ModMetadata.AllMods[modId].ModContent;
var headerTemplate = panel.Get<LabelWidget>("HEADER_TEMPLATE"); var headerTemplate = panel.Get<LabelWidget>("HEADER_TEMPLATE");
var headerLines = !string.IsNullOrEmpty(content.InstallPromptMessage) ? content.InstallPromptMessage.Replace("\\n", "\n").Split('\n') : new string[0]; var headerLines = !string.IsNullOrEmpty(content.InstallPromptMessage) ? content.InstallPromptMessage.Replace("\\n", "\n").Split('\n') : new string[0];
var headerHeight = 0; var headerHeight = 0;
@@ -46,7 +43,8 @@ namespace OpenRA.Mods.Common.Widgets.Logic
{ {
Ui.OpenWindow("CONTENT_PANEL", new WidgetArgs Ui.OpenWindow("CONTENT_PANEL", new WidgetArgs
{ {
{ "modId", modId }, { "mod", mod },
{ "content", content },
{ "onCancel", Ui.CloseWindow } { "onCancel", Ui.CloseWindow }
}); });
}; };

View File

@@ -90,7 +90,7 @@ namespace OpenRA.Mods.Common.Widgets.Logic
// so we can't do this inside the input handler. // so we can't do this inside the input handler.
Game.RunAfterTick(() => Game.RunAfterTick(() =>
{ {
Game.Settings.Game.PreviousMod = modData.Manifest.Mod.Id; Game.Settings.Game.PreviousMod = modData.Manifest.Id;
Game.InitializeMod("modchooser", null); Game.InitializeMod("modchooser", null);
}); });
}; };
@@ -283,8 +283,8 @@ namespace OpenRA.Mods.Common.Widgets.Logic
{ {
// Send the mod and engine version to support version-filtered news (update prompts) // Send the mod and engine version to support version-filtered news (update prompts)
var newsURL = Game.Settings.Game.NewsUrl + "?version={0}&mod={1}&modversion={2}".F( var newsURL = Game.Settings.Game.NewsUrl + "?version={0}&mod={1}&modversion={2}".F(
Uri.EscapeUriString(ModMetadata.AllMods["modchooser"].Version), Uri.EscapeUriString(Game.Mods["modchooser"].Mod.Version),
Uri.EscapeUriString(Game.ModData.Manifest.Mod.Id), Uri.EscapeUriString(Game.ModData.Manifest.Id),
Uri.EscapeUriString(Game.ModData.Manifest.Mod.Version)); Uri.EscapeUriString(Game.ModData.Manifest.Mod.Version));
// Append system profile data if the player has opted in // Append system profile data if the player has opted in

View File

@@ -24,32 +24,39 @@ namespace OpenRA.Mods.Common.Widgets.Logic
{ {
readonly Widget modList; readonly Widget modList;
readonly ButtonWidget modTemplate; readonly ButtonWidget modTemplate;
readonly ModMetadata[] allMods; readonly Manifest[] allMods;
readonly Dictionary<string, Sprite> previews = new Dictionary<string, Sprite>(); readonly Dictionary<string, Sprite> previews = new Dictionary<string, Sprite>();
readonly Dictionary<string, Sprite> logos = new Dictionary<string, Sprite>(); readonly Dictionary<string, Sprite> logos = new Dictionary<string, Sprite>();
readonly Cache<Manifest, ModContent> content = new Cache<Manifest, ModContent>(LoadModContent);
readonly Widget modChooserPanel; readonly Widget modChooserPanel;
readonly ButtonWidget loadButton; readonly ButtonWidget loadButton;
readonly SheetBuilder sheetBuilder; readonly SheetBuilder sheetBuilder;
ModMetadata selectedMod; Manifest selectedMod;
string selectedAuthor; string selectedAuthor;
string selectedDescription; string selectedDescription;
int modOffset = 0; int modOffset = 0;
static ModContent LoadModContent(Manifest mod)
{
return mod.Get<ModContent>(Game.ModData.ObjectCreator);
}
[ObjectCreator.UseCtor] [ObjectCreator.UseCtor]
public ModBrowserLogic(Widget widget, ModData modData) public ModBrowserLogic(Widget widget, ModData modData)
{ {
modChooserPanel = widget; modChooserPanel = widget;
loadButton = modChooserPanel.Get<ButtonWidget>("LOAD_BUTTON"); loadButton = modChooserPanel.Get<ButtonWidget>("LOAD_BUTTON");
loadButton.OnClick = () => LoadMod(selectedMod); loadButton.OnClick = () => LoadMod(selectedMod);
loadButton.IsDisabled = () => selectedMod.Id == modData.Manifest.Mod.Id; loadButton.IsDisabled = () => selectedMod.Id == modData.Manifest.Id;
var contentButton = modChooserPanel.Get<ButtonWidget>("CONFIGURE_BUTTON"); var contentButton = modChooserPanel.Get<ButtonWidget>("CONFIGURE_BUTTON");
contentButton.IsDisabled = () => selectedMod.ModContent == null;
contentButton.OnClick = () => contentButton.OnClick = () =>
{ {
var widgetArgs = new WidgetArgs var widgetArgs = new WidgetArgs
{ {
{ "modId", selectedMod.Id }, { "mod", selectedMod },
{ "content", content[selectedMod] },
{ "onCancel", () => { } } { "onCancel", () => { } }
}; };
@@ -62,9 +69,9 @@ namespace OpenRA.Mods.Common.Widgets.Logic
modTemplate = modList.Get<ButtonWidget>("MOD_TEMPLATE"); modTemplate = modList.Get<ButtonWidget>("MOD_TEMPLATE");
modChooserPanel.Get<LabelWidget>("MOD_DESC").GetText = () => selectedDescription; modChooserPanel.Get<LabelWidget>("MOD_DESC").GetText = () => selectedDescription;
modChooserPanel.Get<LabelWidget>("MOD_TITLE").GetText = () => selectedMod.Title; modChooserPanel.Get<LabelWidget>("MOD_TITLE").GetText = () => selectedMod.Mod.Title;
modChooserPanel.Get<LabelWidget>("MOD_AUTHOR").GetText = () => selectedAuthor; modChooserPanel.Get<LabelWidget>("MOD_AUTHOR").GetText = () => selectedAuthor;
modChooserPanel.Get<LabelWidget>("MOD_VERSION").GetText = () => selectedMod.Version; modChooserPanel.Get<LabelWidget>("MOD_VERSION").GetText = () => selectedMod.Mod.Version;
var prevMod = modChooserPanel.Get<ButtonWidget>("PREV_MOD"); var prevMod = modChooserPanel.Get<ButtonWidget>("PREV_MOD");
prevMod.OnClick = () => { modOffset -= 1; RebuildModList(); }; prevMod.OnClick = () => { modOffset -= 1; RebuildModList(); };
@@ -82,8 +89,8 @@ namespace OpenRA.Mods.Common.Widgets.Logic
}; };
sheetBuilder = new SheetBuilder(SheetType.BGRA); sheetBuilder = new SheetBuilder(SheetType.BGRA);
allMods = ModMetadata.AllMods.Values.Where(m => !m.Hidden) allMods = Game.Mods.Values.Where(m => !m.Mod.Hidden)
.OrderBy(m => m.Title) .OrderBy(m => m.Mod.Title)
.ToArray(); .ToArray();
// Load preview images, and eat any errors // Load preview images, and eat any errors
@@ -91,7 +98,7 @@ namespace OpenRA.Mods.Common.Widgets.Logic
{ {
try try
{ {
using (var stream = ModMetadata.AllMods[mod.Id].Package.GetStream("preview.png")) using (var stream = mod.Package.GetStream("preview.png"))
using (var preview = new Bitmap(stream)) using (var preview = new Bitmap(stream))
if (preview.Width == 296 && preview.Height == 196) if (preview.Width == 296 && preview.Height == 196)
previews.Add(mod.Id, sheetBuilder.Add(preview)); previews.Add(mod.Id, sheetBuilder.Add(preview));
@@ -100,7 +107,7 @@ namespace OpenRA.Mods.Common.Widgets.Logic
try try
{ {
using (var stream = ModMetadata.AllMods[mod.Id].Package.GetStream("logo.png")) using (var stream = mod.Package.GetStream("logo.png"))
using (var logo = new Bitmap(stream)) using (var logo = new Bitmap(stream))
if (logo.Width == 96 && logo.Height == 96) if (logo.Width == 96 && logo.Height == 96)
logos.Add(mod.Id, sheetBuilder.Add(logo)); logos.Add(mod.Id, sheetBuilder.Add(logo));
@@ -108,9 +115,9 @@ namespace OpenRA.Mods.Common.Widgets.Logic
catch (Exception) { } catch (Exception) { }
} }
ModMetadata initialMod; Manifest initialMod;
ModMetadata.AllMods.TryGetValue(Game.Settings.Game.PreviousMod, out initialMod); Game.Mods.TryGetValue(Game.Settings.Game.PreviousMod, out initialMod);
SelectMod(initialMod != null && initialMod.Id != "modchooser" ? initialMod : ModMetadata.AllMods["ra"]); SelectMod(initialMod != null && initialMod.Id != "modchooser" ? initialMod : Game.Mods["ra"]);
RebuildModList(); RebuildModList();
} }
@@ -146,7 +153,7 @@ namespace OpenRA.Mods.Common.Widgets.Logic
SelectMod(mod); SelectMod(mod);
}; };
item.TooltipText = mod.Title; item.TooltipText = mod.Mod.Title;
if (j < 9) if (j < 9)
item.Key = new Hotkey((Keycode)((int)Keycode.NUMBER_1 + j), Modifiers.None); item.Key = new Hotkey((Keycode)((int)Keycode.NUMBER_1 + j), Modifiers.None);
@@ -160,23 +167,25 @@ namespace OpenRA.Mods.Common.Widgets.Logic
} }
} }
void SelectMod(ModMetadata mod) void SelectMod(Manifest mod)
{ {
selectedMod = mod; selectedMod = mod;
selectedAuthor = "By " + (mod.Author ?? "unknown author"); selectedAuthor = "By " + (mod.Mod.Author ?? "unknown author");
selectedDescription = (mod.Description ?? "").Replace("\\n", "\n"); selectedDescription = (mod.Mod.Description ?? "").Replace("\\n", "\n");
var selectedIndex = Array.IndexOf(allMods, mod); var selectedIndex = Array.IndexOf(allMods, mod);
if (selectedIndex - modOffset > 4) if (selectedIndex - modOffset > 4)
modOffset = selectedIndex - 4; modOffset = selectedIndex - 4;
} }
void LoadMod(ModMetadata mod) void LoadMod(Manifest mod)
{ {
if (!Game.IsModInstalled(mod.Id)) var modId = mod.Id;
if (!Game.IsModInstalled(modId))
{ {
var widgetArgs = new WidgetArgs var widgetArgs = new WidgetArgs
{ {
{ "modId", mod.Id } { "mod", selectedMod },
{ "content", content[selectedMod] },
}; };
Ui.OpenWindow("INSTALL_MOD_PANEL", widgetArgs); Ui.OpenWindow("INSTALL_MOD_PANEL", widgetArgs);
@@ -188,8 +197,9 @@ namespace OpenRA.Mods.Common.Widgets.Logic
var widgetArgs = new WidgetArgs var widgetArgs = new WidgetArgs
{ {
{ "continueLoading", () => { "continueLoading", () =>
Game.RunAfterTick(() => Game.InitializeMod(mod.Id, new Arguments())) }, Game.RunAfterTick(() => Game.InitializeMod(modId, new Arguments())) },
{ "modId", mod.Id } { "mod", selectedMod },
{ "content", content[selectedMod] },
}; };
Ui.OpenWindow("CONTENT_PROMPT_PANEL", widgetArgs); Ui.OpenWindow("CONTENT_PROMPT_PANEL", widgetArgs);
@@ -201,13 +211,13 @@ namespace OpenRA.Mods.Common.Widgets.Logic
{ {
Ui.CloseWindow(); Ui.CloseWindow();
sheetBuilder.Dispose(); sheetBuilder.Dispose();
Game.InitializeMod(mod.Id, null); Game.InitializeMod(modId, null);
}); });
} }
static bool IsModInstalled(ModMetadata mod) bool IsModInstalled(Manifest mod)
{ {
return mod.ModContent.Packages return content[mod].Packages
.Where(p => p.Value.Required) .Where(p => p.Value.Required)
.All(p => p.Value.TestFiles.All(f => File.Exists(Platform.ResolvePath(f)))); .All(p => p.Value.TestFiles.All(f => File.Exists(Platform.ResolvePath(f))));
} }

View File

@@ -316,8 +316,8 @@ namespace OpenRA.Mods.Common.Widgets.Logic
}; };
var queryURL = Game.Settings.Server.MasterServer + "games?version={0}&mod={1}&modversion={2}".F( var queryURL = Game.Settings.Server.MasterServer + "games?version={0}&mod={1}&modversion={2}".F(
Uri.EscapeUriString(ModMetadata.AllMods["modchooser"].Version), Uri.EscapeUriString(Game.Mods["modchooser"].Mod.Version),
Uri.EscapeUriString(Game.ModData.Manifest.Mod.Id), Uri.EscapeUriString(Game.ModData.Manifest.Id),
Uri.EscapeUriString(Game.ModData.Manifest.Mod.Version)); Uri.EscapeUriString(Game.ModData.Manifest.Mod.Version));
currentQuery = new Download(queryURL, _ => { }, onComplete); currentQuery = new Download(queryURL, _ => { }, onComplete);
@@ -330,7 +330,7 @@ namespace OpenRA.Mods.Common.Widgets.Logic
return 0; return 0;
// Games for the current mod+version are sorted first // Games for the current mod+version are sorted first
if (testEntry.ModId == modData.Manifest.Mod.Id) if (testEntry.ModId == modData.Manifest.Id)
return 2; return 2;
// Followed by games for different mods that are joinable // Followed by games for different mods that are joinable

View File

@@ -59,8 +59,8 @@ namespace OpenRA.Mods.Common.Widgets.Logic
replayList = panel.Get<ScrollPanelWidget>("REPLAY_LIST"); replayList = panel.Get<ScrollPanelWidget>("REPLAY_LIST");
var template = panel.Get<ScrollItemWidget>("REPLAY_TEMPLATE"); var template = panel.Get<ScrollItemWidget>("REPLAY_TEMPLATE");
var mod = modData.Manifest.Mod; var mod = modData.Manifest;
var dir = Platform.ResolvePath("^", "Replays", mod.Id, mod.Version); var dir = Platform.ResolvePath("^", "Replays", mod.Id, mod.Mod.Version);
if (Directory.Exists(dir)) if (Directory.Exists(dir))
ThreadPool.QueueUserWorkItem(_ => LoadReplays(dir, template)); ThreadPool.QueueUserWorkItem(_ => LoadReplays(dir, template));

View File

@@ -34,10 +34,10 @@ namespace OpenRA.Mods.Common.Widgets.Logic
if (mod == null) if (mod == null)
return IncompatibleReplayDialog("unknown mod", mod, onCancel); return IncompatibleReplayDialog("unknown mod", mod, onCancel);
var allMods = ModMetadata.AllMods; if (!Game.Mods.ContainsKey(mod))
if (!allMods.ContainsKey(mod))
return IncompatibleReplayDialog("unavailable mod", mod, onCancel); return IncompatibleReplayDialog("unavailable mod", mod, onCancel);
else if (allMods[mod].Version != version)
if (Game.Mods[mod].Mod.Version != version)
return IncompatibleReplayDialog("incompatible version", version, onCancel); return IncompatibleReplayDialog("incompatible version", version, onCancel);
if (replayMeta.GameInfo.MapPreview.Status != MapStatus.Available) if (replayMeta.GameInfo.MapPreview.Status != MapStatus.Available)

View File

@@ -31,7 +31,7 @@ namespace OpenRA.Mods.D2k.UtilityCommands
Game.ModData = utility.ModData; Game.ModData = utility.ModData;
var rules = Ruleset.LoadDefaultsForTileSet(utility.ModData, "ARRAKIS"); var rules = Ruleset.LoadDefaultsForTileSet(utility.ModData, "ARRAKIS");
var map = D2kMapImporter.Import(args[1], utility.ModData.Manifest.Mod.Id, args[2], rules); var map = D2kMapImporter.Import(args[1], utility.ModData.Manifest.Id, args[2], rules);
if (map == null) if (map == null)
return; return;

View File

@@ -168,7 +168,7 @@ namespace OpenRA.Mods.TS.UtilityCommands
Title = basic.GetValue("Name", Path.GetFileNameWithoutExtension(filename)), Title = basic.GetValue("Name", Path.GetFileNameWithoutExtension(filename)),
Author = "Westwood Studios", Author = "Westwood Studios",
Bounds = new Rectangle(iniBounds[0], iniBounds[1], iniBounds[2], 2 * iniBounds[3] + 2 * iniBounds[1]), Bounds = new Rectangle(iniBounds[0], iniBounds[1], iniBounds[2], 2 * iniBounds[3] + 2 * iniBounds[1]),
RequiresMod = utility.ModData.Manifest.Mod.Id RequiresMod = utility.ModData.Manifest.Id
}; };
var fullSize = new int2(iniSize[2], iniSize[3]); var fullSize = new int2(iniSize[2], iniSize[3]);

View File

@@ -30,9 +30,11 @@ namespace OpenRA.Server
Game.InitializeSettings(new Arguments(args)); Game.InitializeSettings(new Arguments(args));
var settings = Game.Settings.Server; var settings = Game.Settings.Server;
// HACK: The engine code *still* assumes that Game.ModData is set
var mod = Game.Settings.Game.Mod; var mod = Game.Settings.Game.Mod;
var modData = Game.ModData = new ModData(mod, false); var mods = new InstalledMods();
// HACK: The engine code *still* assumes that Game.ModData is set
var modData = Game.ModData = new ModData(mods[mod], mods);
modData.MapCache.LoadMaps(); modData.MapCache.LoadMaps();
settings.Map = modData.MapCache.ChooseInitialMap(settings.Map, new MersenneTwister()); settings.Map = modData.MapCache.ChooseInitialMap(settings.Map, new MersenneTwister());

View File

@@ -16,6 +16,8 @@ using System.Runtime.Serialization;
namespace OpenRA namespace OpenRA
{ {
using UtilityActions = Dictionary<string, KeyValuePair<Action<Utility, string[]>, Func<string[], bool>>>;
[Serializable] [Serializable]
public class NoSuchCommandException : Exception public class NoSuchCommandException : Exception
{ {
@@ -37,27 +39,29 @@ namespace OpenRA
{ {
static void Main(string[] args) static void Main(string[] args)
{ {
if (args.Length == 0)
{
PrintUsage(null);
return;
}
Log.AddChannel("perf", null); Log.AddChannel("perf", null);
Log.AddChannel("debug", null); Log.AddChannel("debug", null);
var modName = args[0]; Game.InitializeSettings(Arguments.Empty);
if (!ModMetadata.AllMods.Keys.Contains(modName)) var mods = new InstalledMods();
if (args.Length == 0)
{ {
PrintUsage(null); PrintUsage(mods, null);
return; return;
} }
Game.InitializeSettings(Arguments.Empty); var modName = args[0];
var modData = new ModData(modName); if (!mods.Keys.Contains(modName))
var utility = new Utility(modData); {
PrintUsage(mods, null);
return;
}
var modData = new ModData(mods[modName], mods);
var utility = new Utility(modData, mods);
args = args.Skip(1).ToArray(); args = args.Skip(1).ToArray();
var actions = new Dictionary<string, KeyValuePair<Action<Utility, string[]>, Func<string[], bool>>>(); var actions = new UtilityActions();
foreach (var commandType in modData.ObjectCreator.GetTypesImplementing<IUtilityCommand>()) foreach (var commandType in modData.ObjectCreator.GetTypesImplementing<IUtilityCommand>())
{ {
var command = (IUtilityCommand)Activator.CreateInstance(commandType); var command = (IUtilityCommand)Activator.CreateInstance(commandType);
@@ -67,7 +71,7 @@ namespace OpenRA
if (args.Length == 0) if (args.Length == 0)
{ {
PrintUsage(actions); PrintUsage(mods, actions);
return; return;
} }
@@ -106,10 +110,10 @@ namespace OpenRA
} }
} }
static void PrintUsage(IDictionary<string, KeyValuePair<Action<Utility, string[]>, Func<string[], bool>>> actions) static void PrintUsage(InstalledMods mods, UtilityActions actions)
{ {
Console.WriteLine("Run `OpenRA.Utility.exe [MOD]` to see a list of available commands."); Console.WriteLine("Run `OpenRA.Utility.exe [MOD]` to see a list of available commands.");
Console.WriteLine("The available mods are: " + string.Join(", ", ModMetadata.AllMods.Keys)); Console.WriteLine("The available mods are: " + string.Join(", ", mods.Keys));
Console.WriteLine(); Console.WriteLine();
if (actions == null) if (actions == null)