Merge pull request #7606 from pchote/manifest

Allow mods to define custom manifest data.
This commit is contained in:
Taryn Hill
2015-03-07 09:51:00 -06:00
11 changed files with 64 additions and 23 deletions

View File

@@ -18,6 +18,7 @@ using OpenRA.Primitives;
namespace OpenRA namespace OpenRA
{ {
public enum TileShape { Rectangle, Diamond } public enum TileShape { Rectangle, Diamond }
public interface IGlobalModData { }
// Describes what is to be loaded in order to run a mod // Describes what is to be loaded in order to run a mod
public class Manifest public class Manifest
@@ -33,7 +34,6 @@ namespace OpenRA
public readonly IReadOnlyDictionary<string, string> MapFolders; public readonly IReadOnlyDictionary<string, string> MapFolders;
public readonly MiniYaml LoadScreen; public readonly MiniYaml LoadScreen;
public readonly MiniYaml LobbyDefaults; public readonly MiniYaml LobbyDefaults;
public readonly InstallData ContentInstaller;
public readonly Dictionary<string, Pair<string, int>> Fonts; public readonly Dictionary<string, Pair<string, int>> Fonts;
public readonly Size TileSize = new Size(24, 24); public readonly Size TileSize = new Size(24, 24);
public readonly TileShape TileShape = TileShape.Rectangle; public readonly TileShape TileShape = TileShape.Rectangle;
@@ -54,10 +54,19 @@ namespace OpenRA
[Desc("Default subcell index used if SubCellInit is absent", "0 - full cell, 1 - first sub-cell")] [Desc("Default subcell index used if SubCellInit is absent", "0 - full cell, 1 - first sub-cell")]
public readonly int SubCellDefaultIndex = 3; public readonly int SubCellDefaultIndex = 3;
readonly string[] reservedModuleNames = { "Metadata", "Folders", "MapFolders", "Packages", "Rules",
"Sequences", "VoxelSequences", "Cursors", "Chrome", "Assemblies", "ChromeLayout", "Weapons",
"Voices", "Notifications", "Music", "Translations", "TileSets", "ChromeMetrics", "Missions",
"ServerTraits", "LoadScreen", "LobbyDefaults", "Fonts", "TileSize",
"TileShape", "SubCells", "SupportsMapsFrom", "SpriteFormats" };
readonly TypeDictionary modules = new TypeDictionary();
readonly Dictionary<string, MiniYaml> yaml;
public Manifest(string mod) public Manifest(string mod)
{ {
var path = Platform.ResolvePath(".", "mods", mod, "mod.yaml"); var path = Platform.ResolvePath(".", "mods", mod, "mod.yaml");
var yaml = new MiniYaml(null, MiniYaml.FromFile(path)).ToDictionary(); yaml = new MiniYaml(null, MiniYaml.FromFile(path)).ToDictionary();
Mod = FieldLoader.Load<ModMetadata>(yaml["Metadata"]); Mod = FieldLoader.Load<ModMetadata>(yaml["Metadata"]);
Mod.Id = mod; Mod.Id = mod;
@@ -86,9 +95,6 @@ namespace OpenRA
LoadScreen = yaml["LoadScreen"]; LoadScreen = yaml["LoadScreen"];
LobbyDefaults = yaml["LobbyDefaults"]; LobbyDefaults = yaml["LobbyDefaults"];
if (yaml.ContainsKey("ContentInstaller"))
ContentInstaller = FieldLoader.Load<InstallData>(yaml["ContentInstaller"]);
Fonts = yaml["Fonts"].ToDictionary(my => Fonts = yaml["Fonts"].ToDictionary(my =>
{ {
var nd = my.ToDictionary(); var nd = my.ToDictionary();
@@ -133,6 +139,23 @@ namespace OpenRA
SpriteFormats = FieldLoader.GetValue<string[]>("SpriteFormats", yaml["SpriteFormats"].Value); SpriteFormats = FieldLoader.GetValue<string[]>("SpriteFormats", yaml["SpriteFormats"].Value);
} }
public void LoadCustomData(ObjectCreator oc)
{
foreach (var kv in yaml)
{
if (reservedModuleNames.Contains(kv.Key))
continue;
var t = oc.FindType(kv.Key);
if (t == null || !typeof(IGlobalModData).IsAssignableFrom(t))
throw new InvalidDataException("`{0}` is not a valid mod manifest entry.".F(kv.Key));
var module = oc.CreateObject<IGlobalModData>(kv.Key);
FieldLoader.Load(module, kv.Value);
modules.Add(module);
}
}
static string[] YamlList(Dictionary<string, MiniYaml> yaml, string key, bool parsePaths = false) static string[] YamlList(Dictionary<string, MiniYaml> yaml, string key, bool parsePaths = false)
{ {
if (!yaml.ContainsKey(key)) if (!yaml.ContainsKey(key))
@@ -152,5 +175,19 @@ namespace OpenRA
return new ReadOnlyDictionary<string, string>(inner); return new ReadOnlyDictionary<string, string>(inner);
} }
public T Get<T>() where T : IGlobalModData
{
var module = modules.GetOrDefault<T>();
// Lazily create the default values if not explicitly defined.
if (module == null)
{
module = (T)Game.ModData.ObjectCreator.CreateBasic(typeof(T));
modules.Add(module);
}
return module;
}
} }
} }

View File

@@ -38,6 +38,8 @@ namespace OpenRA
Languages = new string[0]; Languages = new string[0];
Manifest = new Manifest(mod); Manifest = new Manifest(mod);
ObjectCreator = new ObjectCreator(Manifest); ObjectCreator = new ObjectCreator(Manifest);
Manifest.LoadCustomData(ObjectCreator);
if (useLoadScreen) if (useLoadScreen)
{ {
LoadScreen = ObjectCreator.CreateObject<ILoadScreen>(Manifest.LoadScreen.Value); LoadScreen = ObjectCreator.CreateObject<ILoadScreen>(Manifest.LoadScreen.Value);

View File

@@ -275,7 +275,6 @@
<Compile Include="Map\TileSet.cs" /> <Compile Include="Map\TileSet.cs" />
<Compile Include="FieldLoader.cs" /> <Compile Include="FieldLoader.cs" />
<Compile Include="FieldSaver.cs" /> <Compile Include="FieldSaver.cs" />
<Compile Include="InstallUtils.cs" />
<Compile Include="Manifest.cs" /> <Compile Include="Manifest.cs" />
<Compile Include="Graphics\Vertex.cs" /> <Compile Include="Graphics\Vertex.cs" />
<Compile Include="FileFormats\ImaAdpcmLoader.cs" /> <Compile Include="FileFormats\ImaAdpcmLoader.cs" />

View File

@@ -16,9 +16,9 @@ using ICSharpCode.SharpZipLib;
using ICSharpCode.SharpZipLib.Zip; using ICSharpCode.SharpZipLib.Zip;
using OpenRA.FileSystem; using OpenRA.FileSystem;
namespace OpenRA namespace OpenRA.Mods.Common
{ {
public class InstallData public class ContentInstaller : IGlobalModData
{ {
public readonly string MenuWidget = null; public readonly string MenuWidget = null;
public readonly string MusicMenuWidget = null; public readonly string MusicMenuWidget = null;

View File

@@ -40,7 +40,7 @@ namespace OpenRA.Mods.Common.LoadScreens
// Check whether the mod content is installed // Check whether the mod content is installed
// TODO: The installation code has finally been beaten into shape, so we can // TODO: The installation code has finally been beaten into shape, so we can
// finally move it all into the planned "Manage Content" panel in the modchooser mod. // finally move it all into the planned "Manage Content" panel in the modchooser mod.
var installData = Game.ModData.Manifest.ContentInstaller; var installData = Game.ModData.Manifest.Get<ContentInstaller>();
var installModContent = !installData.TestFiles.All(f => GlobalFileSystem.Exists(f)); var installModContent = !installData.TestFiles.All(f => GlobalFileSystem.Exists(f));
var installModMusic = args != null && args.Contains("Install.Music"); var installModMusic = args != null && args.Contains("Install.Music");

View File

@@ -63,6 +63,10 @@
<HintPath>..\thirdparty\Mono.Nat.dll</HintPath> <HintPath>..\thirdparty\Mono.Nat.dll</HintPath>
<Private>False</Private> <Private>False</Private>
</Reference> </Reference>
<Reference Include="ICSharpCode.SharpZipLib">
<HintPath>..\thirdparty\ICSharpCode.SharpZipLib.dll</HintPath>
<Private>False</Private>
</Reference>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<ProjectReference Include="..\OpenRA.Game\OpenRA.Game.csproj"> <ProjectReference Include="..\OpenRA.Game\OpenRA.Game.csproj">
@@ -572,6 +576,7 @@
<Compile Include="UtilityCommands\CheckCodeStyle.cs" /> <Compile Include="UtilityCommands\CheckCodeStyle.cs" />
<Compile Include="UtilityCommands\ReplayMetadataCommand.cs" /> <Compile Include="UtilityCommands\ReplayMetadataCommand.cs" />
<Compile Include="Widgets\Logic\ReplayUtils.cs" /> <Compile Include="Widgets\Logic\ReplayUtils.cs" />
<Compile Include="InstallUtils.cs" />
</ItemGroup> </ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" /> <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<PropertyGroup> <PropertyGroup>

View File

@@ -24,10 +24,12 @@ namespace OpenRA.Mods.Common.Widgets.Logic
readonly Action continueLoading; readonly Action continueLoading;
readonly ButtonWidget retryButton, backButton; readonly ButtonWidget retryButton, backButton;
readonly Widget installingContainer, insertDiskContainer; readonly Widget installingContainer, insertDiskContainer;
readonly ContentInstaller installData;
[ObjectCreator.UseCtor] [ObjectCreator.UseCtor]
public InstallFromCDLogic(Widget widget, Action continueLoading) public InstallFromCDLogic(Widget widget, Action continueLoading)
{ {
installData = Game.ModData.Manifest.Get<ContentInstaller>();
this.continueLoading = continueLoading; this.continueLoading = continueLoading;
panel = widget.Get("INSTALL_FROMCD_PANEL"); panel = widget.Get("INSTALL_FROMCD_PANEL");
progressBar = panel.Get<ProgressBarWidget>("PROGRESS_BAR"); progressBar = panel.Get<ProgressBarWidget>("PROGRESS_BAR");
@@ -46,7 +48,7 @@ namespace OpenRA.Mods.Common.Widgets.Logic
bool IsValidDisk(string diskRoot) bool IsValidDisk(string diskRoot)
{ {
return Game.ModData.Manifest.ContentInstaller.DiskTestFiles.All(f => File.Exists(Path.Combine(diskRoot, f))); return installData.DiskTestFiles.All(f => File.Exists(Path.Combine(diskRoot, f)));
} }
void CheckForDisk() void CheckForDisk()
@@ -70,13 +72,13 @@ namespace OpenRA.Mods.Common.Widgets.Logic
installingContainer.IsVisible = () => true; installingContainer.IsVisible = () => true;
var dest = Platform.ResolvePath("^", "Content", Game.ModData.Manifest.Mod.Id); var dest = Platform.ResolvePath("^", "Content", Game.ModData.Manifest.Mod.Id);
var copyFiles = Game.ModData.Manifest.ContentInstaller.CopyFilesFromCD; var copyFiles = installData.CopyFilesFromCD;
var packageToExtract = Game.ModData.Manifest.ContentInstaller.PackageToExtractFromCD.Split(':'); var packageToExtract = installData.PackageToExtractFromCD.Split(':');
var extractPackage = packageToExtract.First(); var extractPackage = packageToExtract.First();
var annotation = packageToExtract.Length > 1 ? packageToExtract.Last() : null; var annotation = packageToExtract.Length > 1 ? packageToExtract.Last() : null;
var extractFiles = Game.ModData.Manifest.ContentInstaller.ExtractFilesFromCD; var extractFiles = installData.ExtractFilesFromCD;
var installCounter = 0; var installCounter = 0;
var installTotal = copyFiles.Length + extractFiles.Length; var installTotal = copyFiles.Length + extractFiles.Length;

View File

@@ -19,13 +19,13 @@ namespace OpenRA.Mods.Common.Widgets.Logic
[ObjectCreator.UseCtor] [ObjectCreator.UseCtor]
public InstallLogic(Widget widget, Action continueLoading) public InstallLogic(Widget widget, Action continueLoading)
{ {
var mirrorListUrl = Game.ModData.Manifest.ContentInstaller.PackageMirrorList; var installData = Game.ModData.Manifest.Get<ContentInstaller>();
var panel = widget.Get("INSTALL_PANEL"); var panel = widget.Get("INSTALL_PANEL");
var widgetArgs = new WidgetArgs() var widgetArgs = new WidgetArgs()
{ {
{ "afterInstall", () => { Ui.CloseWindow(); continueLoading(); } }, { "afterInstall", () => { Ui.CloseWindow(); continueLoading(); } },
{ "continueLoading", continueLoading }, { "continueLoading", continueLoading },
{ "mirrorListUrl", mirrorListUrl }, { "mirrorListUrl", installData.PackageMirrorList },
}; };
panel.Get<ButtonWidget>("DOWNLOAD_BUTTON").OnClick = () => panel.Get<ButtonWidget>("DOWNLOAD_BUTTON").OnClick = () =>

View File

@@ -42,13 +42,13 @@ namespace OpenRA.Mods.Common.Widgets.Logic
var downloadButton = installMusicContainer.GetOrNull<ButtonWidget>("DOWNLOAD_BUTTON"); var downloadButton = installMusicContainer.GetOrNull<ButtonWidget>("DOWNLOAD_BUTTON");
if (downloadButton != null) if (downloadButton != null)
{ {
var mirrorListUrl = Game.ModData.Manifest.ContentInstaller.MusicPackageMirrorList; var installData = Game.ModData.Manifest.Get<ContentInstaller>();
downloadButton.IsVisible = () => !string.IsNullOrEmpty(mirrorListUrl); downloadButton.IsVisible = () => !string.IsNullOrEmpty(installData.MusicPackageMirrorList);
downloadButton.OnClick = () => downloadButton.OnClick = () =>
{ {
Ui.OpenWindow("INSTALL_DOWNLOAD_PANEL", new WidgetArgs() { Ui.OpenWindow("INSTALL_DOWNLOAD_PANEL", new WidgetArgs() {
{ "afterInstall", () => Game.InitializeMod(Game.Settings.Game.Mod, null) }, { "afterInstall", () => Game.InitializeMod(Game.Settings.Game.Mod, null) },
{ "mirrorListUrl", mirrorListUrl }, { "mirrorListUrl", installData.MusicPackageMirrorList },
}); });
}; };
} }

View File

@@ -92,7 +92,7 @@ namespace OpenRA.Mods.Common.Widgets.Logic
Game.InitializeMod(Game.Settings.Game.Mod, new Arguments(args)); Game.InitializeMod(Game.Settings.Game.Mod, new Arguments(args));
}; };
var installData = Game.ModData.Manifest.ContentInstaller; var installData = Game.ModData.Manifest.Get<ContentInstaller>();
installButton.IsVisible = () => modRules.InstalledMusic.ToArray().Length <= installData.ShippedSoundtracks; installButton.IsVisible = () => modRules.InstalledMusic.ToArray().Length <= installData.ShippedSoundtracks;
} }

View File

@@ -121,10 +121,6 @@ ChromeLayout:
./mods/cnc/chrome/assetbrowser.yaml ./mods/cnc/chrome/assetbrowser.yaml
./mods/cnc/chrome/missionbrowser.yaml ./mods/cnc/chrome/missionbrowser.yaml
Movies:
./mods/cnc/movies-gdi.yaml
./mods/cnc/movies-nod.yaml
Voices: Voices:
./mods/cnc/audio/voices.yaml ./mods/cnc/audio/voices.yaml