Restore support for inline map rules.

This commit is contained in:
Paul Chote
2016-03-09 19:16:42 +00:00
parent 2d98e41f11
commit 20e8bc249d
11 changed files with 119 additions and 94 deletions

View File

@@ -21,8 +21,6 @@ namespace OpenRA
{ {
public sealed class RulesetCache public sealed class RulesetCache
{ {
static readonly string[] NoMapRules = new string[0];
readonly ModData modData; readonly ModData modData;
readonly Dictionary<string, ActorInfo> actorCache = new Dictionary<string, ActorInfo>(); readonly Dictionary<string, ActorInfo> actorCache = new Dictionary<string, ActorInfo>();
@@ -44,11 +42,13 @@ namespace OpenRA
this.modData = modData; this.modData = modData;
} }
/// <summary> public Ruleset Load(IReadOnlyFileSystem fileSystem,
/// Cache and return the Ruleset for a given map. MiniYaml additionalRules,
/// If a map isn't specified then return the default mod Ruleset. MiniYaml additionalWeapons,
/// </summary> MiniYaml additionalVoices,
public Ruleset Load(IReadOnlyFileSystem fileSystem, Map map = null) MiniYaml additionalNotifications,
MiniYaml additionalMusic,
MiniYaml additionalSequences)
{ {
var m = modData.Manifest; var m = modData.Manifest;
@@ -60,46 +60,58 @@ namespace OpenRA
Dictionary<string, TileSet> tileSets; Dictionary<string, TileSet> tileSets;
using (new PerfTimer("Actors")) using (new PerfTimer("Actors"))
actors = LoadYamlRules(fileSystem, actorCache, m.Rules, actors = LoadYamlRules(fileSystem, actorCache, m.Rules, additionalRules,
map != null ? map.RuleDefinitions : NoMapRules,
k => new ActorInfo(Game.ModData.ObjectCreator, k.Key.ToLowerInvariant(), k.Value)); k => new ActorInfo(Game.ModData.ObjectCreator, k.Key.ToLowerInvariant(), k.Value));
using (new PerfTimer("Weapons")) using (new PerfTimer("Weapons"))
weapons = LoadYamlRules(fileSystem, weaponCache, m.Weapons, weapons = LoadYamlRules(fileSystem, weaponCache, m.Weapons, additionalWeapons,
map != null ? map.WeaponDefinitions : NoMapRules,
k => new WeaponInfo(k.Key.ToLowerInvariant(), k.Value)); k => new WeaponInfo(k.Key.ToLowerInvariant(), k.Value));
using (new PerfTimer("Voices")) using (new PerfTimer("Voices"))
voices = LoadYamlRules(fileSystem, voiceCache, m.Voices, voices = LoadYamlRules(fileSystem, voiceCache, m.Voices, additionalVoices,
map != null ? map.VoiceDefinitions : NoMapRules,
k => new SoundInfo(k.Value)); k => new SoundInfo(k.Value));
using (new PerfTimer("Notifications")) using (new PerfTimer("Notifications"))
notifications = LoadYamlRules(fileSystem, notificationCache, m.Notifications, notifications = LoadYamlRules(fileSystem, notificationCache, m.Notifications, additionalNotifications,
map != null ? map.NotificationDefinitions : NoMapRules,
k => new SoundInfo(k.Value)); k => new SoundInfo(k.Value));
using (new PerfTimer("Music")) using (new PerfTimer("Music"))
music = LoadYamlRules(fileSystem, musicCache, m.Music, music = LoadYamlRules(fileSystem, musicCache, m.Music, additionalMusic,
map != null ? map.MusicDefinitions : NoMapRules,
k => new MusicInfo(k.Key, k.Value)); k => new MusicInfo(k.Key, k.Value));
using (new PerfTimer("TileSets")) using (new PerfTimer("TileSets"))
tileSets = LoadTileSets(fileSystem, tileSetCache, m.TileSets); tileSets = LoadTileSets(fileSystem, tileSetCache, m.TileSets);
// TODO: only initialize, and then cache, the provider for the given map // TODO: only initialize, and then cache, the provider for the given map
var sequences = tileSets.ToDictionary(t => t.Key, t => new SequenceProvider(fileSystem, modData, t.Value, map)); var sequences = tileSets.ToDictionary(t => t.Key, t => new SequenceProvider(fileSystem, modData, t.Value, additionalSequences));
return new Ruleset(actors, weapons, voices, notifications, music, tileSets, sequences); return new Ruleset(actors, weapons, voices, notifications, music, tileSets, sequences);
} }
/// <summary>
/// Cache and return the Ruleset for a given map.
/// If a map isn't specified then return the default mod Ruleset.
/// </summary>
public Ruleset Load(IReadOnlyFileSystem fileSystem, Map map = null)
{
return map != null ? Load(fileSystem, map.RuleDefinitions, map.WeaponDefinitions,
map.VoiceDefinitions, map.NotificationDefinitions, map.MusicDefinitions,
map.SequenceDefinitions) : Load(fileSystem, null, null, null, null, null, null);
}
Dictionary<string, T> LoadYamlRules<T>(IReadOnlyFileSystem fileSystem, Dictionary<string, T> LoadYamlRules<T>(IReadOnlyFileSystem fileSystem,
Dictionary<string, T> itemCache, Dictionary<string, T> itemCache,
string[] files, string[] mapFiles, IEnumerable<string> files, MiniYaml mapRules,
Func<MiniYamlNode, T> f) Func<MiniYamlNode, T> f)
{ {
RaiseProgress(); RaiseProgress();
var inputKey = string.Concat(string.Join("|", files.Append(mapFiles)), "|"); if (mapRules != null && mapRules.Value != null)
files = files.Append(FieldLoader.GetValue<string[]>("value", mapRules.Value));
var inputKey = string.Join("|", files);
if (mapRules != null && mapRules.Nodes.Any())
inputKey += "|" + mapRules.Nodes.WriteToString();
Func<MiniYamlNode, T> wrap = wkv => Func<MiniYamlNode, T> wrap = wkv =>
{ {
var key = inputKey + wkv.Value.ToLines(wkv.Key).JoinWith("|"); var key = inputKey + wkv.Value.ToLines(wkv.Key).JoinWith("|");
@@ -114,8 +126,9 @@ namespace OpenRA
return t; return t;
}; };
var tree = MiniYaml.Merge(files.Append(mapFiles).Select(s => MiniYaml.FromStream(fileSystem.Open(s)))) var tree = MiniYaml.Load(fileSystem, files, mapRules).ToDictionaryWithConflictLog(
.ToDictionaryWithConflictLog(n => n.Key, n => n.Value, "LoadYamlRules", null, null); n => n.Key, n => n.Value, "LoadYamlRules", null, null);
RaiseProgress(); RaiseProgress();
var itemSet = tree.ToDictionary(kv => kv.Key.ToLowerInvariant(), kv => wrap(new MiniYamlNode(kv.Key, kv.Value))); var itemSet = tree.ToDictionary(kv => kv.Key.ToLowerInvariant(), kv => wrap(new MiniYamlNode(kv.Key, kv.Value)));

View File

@@ -53,11 +53,16 @@ namespace OpenRA.Graphics
readonly Dictionary<string, UnitSequences> sequenceCache = new Dictionary<string, UnitSequences>(); readonly Dictionary<string, UnitSequences> sequenceCache = new Dictionary<string, UnitSequences>();
public SequenceProvider(IReadOnlyFileSystem fileSystem, ModData modData, TileSet tileSet, Map map) public SequenceProvider(IReadOnlyFileSystem fileSystem, ModData modData, TileSet tileSet, MiniYaml additionalSequences)
{ {
this.modData = modData; this.modData = modData;
this.tileSet = tileSet; this.tileSet = tileSet;
sequences = Exts.Lazy(() => LoadSequences(fileSystem, map)); sequences = Exts.Lazy(() =>
{
using (new Support.PerfTimer("LoadSequences"))
return Load(fileSystem, additionalSequences);
});
spriteCache = Exts.Lazy(() => new SpriteCache(fileSystem, modData.SpriteLoaders, new SheetBuilder(SheetType.Indexed))); spriteCache = Exts.Lazy(() => new SpriteCache(fileSystem, modData.SpriteLoaders, new SheetBuilder(SheetType.Indexed)));
} }
@@ -97,17 +102,9 @@ namespace OpenRA.Graphics
return unitSeq.Value.Keys; return unitSeq.Value.Keys;
} }
public Sequences LoadSequences(IReadOnlyFileSystem fileSystem, Map map) Sequences Load(IReadOnlyFileSystem fileSystem, MiniYaml additionalSequences)
{ {
using (new Support.PerfTimer("LoadSequences")) var nodes = MiniYaml.Load(fileSystem, modData.Manifest.Sequences, additionalSequences);
return Load(fileSystem, map != null ? map.SequenceDefinitions : new string[0]);
}
Sequences Load(IReadOnlyFileSystem fileSystem, string[] mapSequences)
{
var nodes = MiniYaml.Merge(modData.Manifest.Sequences.Append(mapSequences)
.Select(s => MiniYaml.FromStream(fileSystem.Open(s))));
var items = new Dictionary<string, UnitSequences>(); var items = new Dictionary<string, UnitSequences>();
foreach (var n in nodes) foreach (var n in nodes)
{ {

View File

@@ -21,13 +21,9 @@ namespace OpenRA.Graphics
{ {
static Dictionary<string, Dictionary<string, Voxel>> units; static Dictionary<string, Dictionary<string, Voxel>> units;
public static void Initialize(VoxelLoader loader, IReadOnlyFileSystem fileSystem, IEnumerable<string> voxelFiles) public static void Initialize(VoxelLoader loader, IReadOnlyFileSystem fileSystem, List<MiniYamlNode> sequences)
{ {
units = new Dictionary<string, Dictionary<string, Voxel>>(); units = new Dictionary<string, Dictionary<string, Voxel>>();
var sequences = MiniYaml.Merge(voxelFiles.Select(
s => MiniYaml.FromStream(fileSystem.Open(s))));
foreach (var s in sequences) foreach (var s in sequences)
LoadVoxelsForUnit(loader, s.Key, s.Value); LoadVoxelsForUnit(loader, s.Key, s.Value);

View File

@@ -141,14 +141,14 @@ namespace OpenRA
public Lazy<CPos[]> SpawnPoints; public Lazy<CPos[]> SpawnPoints;
// Yaml map data // Yaml map data
[FieldLoader.Ignore] public readonly string[] RuleDefinitions = { }; [FieldLoader.Ignore] public readonly MiniYaml RuleDefinitions;
[FieldLoader.Ignore] public readonly string[] SequenceDefinitions = { }; [FieldLoader.Ignore] public readonly MiniYaml SequenceDefinitions;
[FieldLoader.Ignore] public readonly string[] VoxelSequenceDefinitions = { }; [FieldLoader.Ignore] public readonly MiniYaml VoxelSequenceDefinitions;
[FieldLoader.Ignore] public readonly string[] WeaponDefinitions = { }; [FieldLoader.Ignore] public readonly MiniYaml WeaponDefinitions;
[FieldLoader.Ignore] public readonly string[] VoiceDefinitions = { }; [FieldLoader.Ignore] public readonly MiniYaml VoiceDefinitions;
[FieldLoader.Ignore] public readonly string[] MusicDefinitions = { }; [FieldLoader.Ignore] public readonly MiniYaml MusicDefinitions;
[FieldLoader.Ignore] public readonly string[] NotificationDefinitions = { }; [FieldLoader.Ignore] public readonly MiniYaml NotificationDefinitions;
[FieldLoader.Ignore] public readonly string[] TranslationDefinitions = { }; [FieldLoader.Ignore] public readonly MiniYaml TranslationDefinitions;
[FieldLoader.Ignore] public List<MiniYamlNode> PlayerDefinitions = new List<MiniYamlNode>(); [FieldLoader.Ignore] public List<MiniYamlNode> PlayerDefinitions = new List<MiniYamlNode>();
[FieldLoader.Ignore] public List<MiniYamlNode> ActorDefinitions = new List<MiniYamlNode>(); [FieldLoader.Ignore] public List<MiniYamlNode> ActorDefinitions = new List<MiniYamlNode>();
@@ -184,13 +184,6 @@ namespace OpenRA
throw new InvalidOperationException("Required file {0} not present in this map".F(filename)); throw new InvalidOperationException("Required file {0} not present in this map".F(filename));
} }
void LoadFileList(MiniYaml yaml, string section, ref string[] files)
{
MiniYamlNode node;
if ((node = yaml.Nodes.FirstOrDefault(n => n.Key == section)) != null)
files = FieldLoader.GetValue<string[]>(section, node.Value.Value);
}
/// <summary> /// <summary>
/// Initializes a new map created by the editor or importer. /// Initializes a new map created by the editor or importer.
/// The map will not receive a valid UID until after it has been saved and reloaded. /// The map will not receive a valid UID until after it has been saved and reloaded.
@@ -260,14 +253,14 @@ namespace OpenRA
return spawns.ToArray(); return spawns.ToArray();
}); });
LoadFileList(yaml, "Rules", ref RuleDefinitions); RuleDefinitions = LoadRuleSection(yaml, "Rules");
LoadFileList(yaml, "Sequences", ref SequenceDefinitions); SequenceDefinitions = LoadRuleSection(yaml, "Sequences");
LoadFileList(yaml, "VoxelSequences", ref VoxelSequenceDefinitions); VoxelSequenceDefinitions = LoadRuleSection(yaml, "VoxelSequences");
LoadFileList(yaml, "Weapons", ref WeaponDefinitions); WeaponDefinitions = LoadRuleSection(yaml, "Weapons");
LoadFileList(yaml, "Voices", ref VoiceDefinitions); VoiceDefinitions = LoadRuleSection(yaml, "Voices");
LoadFileList(yaml, "Music", ref MusicDefinitions); MusicDefinitions = LoadRuleSection(yaml, "Music");
LoadFileList(yaml, "Notifications", ref NotificationDefinitions); NotificationDefinitions = LoadRuleSection(yaml, "Notifications");
LoadFileList(yaml, "Translations", ref TranslationDefinitions); TranslationDefinitions = LoadRuleSection(yaml, "Translations");
PlayerDefinitions = MiniYaml.NodesOrEmpty(yaml, "Players"); PlayerDefinitions = MiniYaml.NodesOrEmpty(yaml, "Players");
ActorDefinitions = MiniYaml.NodesOrEmpty(yaml, "Actors"); ActorDefinitions = MiniYaml.NodesOrEmpty(yaml, "Actors");
@@ -287,6 +280,12 @@ namespace OpenRA
Uid = ComputeUID(Package); Uid = ComputeUID(Package);
} }
MiniYaml LoadRuleSection(MiniYaml yaml, string section)
{
var node = yaml.Nodes.FirstOrDefault(n => n.Key == section);
return node != null ? node.Value : null;
}
void PostInit() void PostInit()
{ {
rules = Exts.Lazy(() => rules = Exts.Lazy(() =>
@@ -448,21 +447,21 @@ namespace OpenRA
root.Add(new MiniYamlNode("Players", null, PlayerDefinitions)); root.Add(new MiniYamlNode("Players", null, PlayerDefinitions));
root.Add(new MiniYamlNode("Actors", null, ActorDefinitions)); root.Add(new MiniYamlNode("Actors", null, ActorDefinitions));
var fileFields = new[] var ruleSections = new[]
{ {
Pair.New("Rules", RuleDefinitions), new MiniYamlNode("Rules", RuleDefinitions),
Pair.New("Sequences", SequenceDefinitions), new MiniYamlNode("Sequences", SequenceDefinitions),
Pair.New("VoxelSequences", VoxelSequenceDefinitions), new MiniYamlNode("VoxelSequences", VoxelSequenceDefinitions),
Pair.New("Weapons", WeaponDefinitions), new MiniYamlNode("Weapons", WeaponDefinitions),
Pair.New("Voices", VoiceDefinitions), new MiniYamlNode("Voices", VoiceDefinitions),
Pair.New("Music", MusicDefinitions), new MiniYamlNode("Music", MusicDefinitions),
Pair.New("Notifications", NotificationDefinitions), new MiniYamlNode("Notifications", NotificationDefinitions),
Pair.New("Translations", TranslationDefinitions) new MiniYamlNode("Translations", TranslationDefinitions)
}; };
foreach (var kv in fileFields) foreach (var section in ruleSections)
if (kv.Second.Any()) if (section.Value != null && (section.Value.Value != null || section.Value.Nodes.Any()))
root.Add(new MiniYamlNode(kv.First, FieldSaver.FormatValue(kv.Second))); root.Add(section);
// Saving to a new package: copy over all the content from the map // Saving to a new package: copy over all the content from the map
if (Package != null && toPackage != Package) if (Package != null && toPackage != Package)

View File

@@ -13,6 +13,7 @@ using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
using OpenRA.FileSystem;
namespace OpenRA namespace OpenRA
{ {
@@ -375,6 +376,21 @@ namespace OpenRA
foreach (var line in Nodes.ToLines(false)) foreach (var line in Nodes.ToLines(false))
yield return "\t" + line; yield return "\t" + line;
} }
public static List<MiniYamlNode> Load(IReadOnlyFileSystem fileSystem, IEnumerable<string> files, MiniYaml mapRules)
{
if (mapRules != null && mapRules.Value != null)
{
var mapFiles = FieldLoader.GetValue<string[]>("value", mapRules.Value);
files = files.Append(mapFiles);
}
var yaml = files.Select(s => MiniYaml.FromStream(fileSystem.Open(s)));
if (mapRules != null && mapRules.Nodes.Any())
yaml = yaml.Append(mapRules.Nodes);
return MiniYaml.Merge(yaml);
}
} }
[Serializable] [Serializable]

View File

@@ -130,8 +130,7 @@ namespace OpenRA
return; return;
} }
var yaml = MiniYaml.Merge(Manifest.Translations.Append(map.TranslationDefinitions) var yaml = MiniYaml.Load(map, Manifest.Translations, map.TranslationDefinitions);
.Select(t => MiniYaml.FromStream(map.Open(t))));
Languages = yaml.Select(t => t.Key).ToArray(); Languages = yaml.Select(t => t.Key).ToArray();
foreach (var y in yaml) foreach (var y in yaml)
@@ -182,7 +181,7 @@ namespace OpenRA
foreach (var entry in map.Rules.Music) foreach (var entry in map.Rules.Music)
entry.Value.Load(map); entry.Value.Load(map);
VoxelProvider.Initialize(VoxelLoader, map, Manifest.VoxelSequences.Append(map.VoxelSequenceDefinitions)); VoxelProvider.Initialize(VoxelLoader, map, MiniYaml.Load(map, Manifest.VoxelSequences, map.VoxelSequenceDefinitions));
VoxelLoader.Finish(); VoxelLoader.Finish();
return map; return map;

View File

@@ -392,7 +392,7 @@ namespace OpenRA.Server
SendOrderTo(newConn, "Message", motd); SendOrderTo(newConn, "Message", motd);
} }
if (Map.RuleDefinitions.Any() && !LobbyInfo.IsSinglePlayer) if (Map.RuleDefinitions != null && !LobbyInfo.IsSinglePlayer)
SendOrderTo(newConn, "Message", "This map contains custom rules. Game experience may change."); SendOrderTo(newConn, "Message", "This map contains custom rules. Game experience may change.");
if (Settings.DisableSinglePlayer) if (Settings.DisableSinglePlayer)

View File

@@ -26,18 +26,17 @@ namespace OpenRA.Mods.Common.Lint
public void Run(Action<string> emitError, Action<string> emitWarning, Map map) public void Run(Action<string> emitError, Action<string> emitWarning, Map map)
{ {
if (map != null && !map.SequenceDefinitions.Any()) if (map.SequenceDefinitions == null)
return; return;
var modData = Game.ModData; var modData = Game.ModData;
this.emitError = emitError; this.emitError = emitError;
var mapSequences = map != null ? map.SequenceDefinitions : new string[0]; sequenceDefinitions = MiniYaml.Load(map, modData.Manifest.Sequences, map.SequenceDefinitions);
sequenceDefinitions = MiniYaml.Merge(modData.Manifest.Sequences.Append(mapSequences).Select(s => MiniYaml.FromStream(map.Open(s))));
var rules = map == null ? modData.DefaultRules : map.Rules; var rules = map.Rules;
var factions = rules.Actors["world"].TraitInfos<FactionInfo>().Select(f => f.InternalName).ToArray(); var factions = rules.Actors["world"].TraitInfos<FactionInfo>().Select(f => f.InternalName).ToArray();
var sequenceProviders = map == null ? rules.Sequences.Values : new[] { rules.Sequences[map.Tileset] }; var sequenceProviders = new[] { rules.Sequences[map.Tileset] };
foreach (var actorInfo in rules.Actors) foreach (var actorInfo in rules.Actors)
{ {

View File

@@ -387,7 +387,7 @@ namespace OpenRA.Mods.Common.Server
server.SendMessage("{0} changed the map to {1}.".F(client.Name, server.Map.Title)); server.SendMessage("{0} changed the map to {1}.".F(client.Name, server.Map.Title));
if (server.Map.RuleDefinitions.Any()) if (server.Map.RuleDefinitions != null)
server.SendMessage("This map contains custom rules. Game experience may change."); server.SendMessage("This map contains custom rules. Game experience may change.");
if (server.Settings.DisableSinglePlayer) if (server.Settings.DisableSinglePlayer)

View File

@@ -90,7 +90,7 @@ namespace OpenRA.Mods.Common.UtilityCommands
testMap.PreloadRules(); testMap.PreloadRules();
// Run all rule checks on the map if it defines custom rules. // Run all rule checks on the map if it defines custom rules.
if (testMap.RuleDefinitions.Any() || testMap.VoiceDefinitions.Any() || testMap.WeaponDefinitions.Any()) if (testMap.RuleDefinitions != null || testMap.VoiceDefinitions != null || testMap.WeaponDefinitions != null)
CheckRules(modData, testMap.Rules, testMap); CheckRules(modData, testMap.Rules, testMap);
// Run all map-level checks here. // Run all map-level checks here.

View File

@@ -27,17 +27,23 @@ namespace OpenRA.Mods.Common.UtilityCommands
delegate void UpgradeAction(int engineVersion, ref List<MiniYamlNode> nodes, MiniYamlNode parent, int depth); delegate void UpgradeAction(int engineVersion, ref List<MiniYamlNode> nodes, MiniYamlNode parent, int depth);
static void ProcessYaml(Map map, IEnumerable<string> files, int engineDate, UpgradeAction processFile) static void ProcessYaml(Map map, MiniYaml yaml, int engineDate, UpgradeAction processYaml)
{ {
foreach (var filename in files) if (yaml == null)
{ return;
if (!map.Package.Contains(filename))
continue;
var yaml = MiniYaml.FromStream(map.Package.GetStream(filename)); if (yaml.Value != null)
processFile(engineDate, ref yaml, null, 0); {
((IReadWritePackage)map.Package).Update(filename, Encoding.ASCII.GetBytes(yaml.WriteToString())); var files = FieldLoader.GetValue<string[]>("value", yaml.Value);
} foreach (var filename in files)
{
var fileNodes = MiniYaml.FromStream(map.Package.GetStream(filename));
processYaml(engineDate, ref fileNodes, null, 0);
((IReadWritePackage)map.Package).Update(filename, Encoding.ASCII.GetBytes(fileNodes.WriteToString()));
}
}
processYaml(engineDate, ref yaml.Nodes, null, 1);
} }
public static void UpgradeMap(ModData modData, IReadWritePackage package, int engineDate) public static void UpgradeMap(ModData modData, IReadWritePackage package, int engineDate)