Merge pull request #7606 from pchote/manifest
Allow mods to define custom manifest data.
This commit is contained in:
@@ -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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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);
|
||||||
|
|||||||
@@ -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" />
|
||||||
|
|||||||
@@ -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;
|
||||||
@@ -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");
|
||||||
|
|
||||||
|
|||||||
@@ -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>
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
@@ -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 = () =>
|
||||||
|
|||||||
@@ -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 },
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user