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
{
static readonly string[] NoMapRules = new string[0];
readonly ModData modData;
readonly Dictionary<string, ActorInfo> actorCache = new Dictionary<string, ActorInfo>();
@@ -44,11 +42,13 @@ namespace OpenRA
this.modData = modData;
}
/// <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)
public Ruleset Load(IReadOnlyFileSystem fileSystem,
MiniYaml additionalRules,
MiniYaml additionalWeapons,
MiniYaml additionalVoices,
MiniYaml additionalNotifications,
MiniYaml additionalMusic,
MiniYaml additionalSequences)
{
var m = modData.Manifest;
@@ -60,46 +60,58 @@ namespace OpenRA
Dictionary<string, TileSet> tileSets;
using (new PerfTimer("Actors"))
actors = LoadYamlRules(fileSystem, actorCache, m.Rules,
map != null ? map.RuleDefinitions : NoMapRules,
actors = LoadYamlRules(fileSystem, actorCache, m.Rules, additionalRules,
k => new ActorInfo(Game.ModData.ObjectCreator, k.Key.ToLowerInvariant(), k.Value));
using (new PerfTimer("Weapons"))
weapons = LoadYamlRules(fileSystem, weaponCache, m.Weapons,
map != null ? map.WeaponDefinitions : NoMapRules,
weapons = LoadYamlRules(fileSystem, weaponCache, m.Weapons, additionalWeapons,
k => new WeaponInfo(k.Key.ToLowerInvariant(), k.Value));
using (new PerfTimer("Voices"))
voices = LoadYamlRules(fileSystem, voiceCache, m.Voices,
map != null ? map.VoiceDefinitions : NoMapRules,
voices = LoadYamlRules(fileSystem, voiceCache, m.Voices, additionalVoices,
k => new SoundInfo(k.Value));
using (new PerfTimer("Notifications"))
notifications = LoadYamlRules(fileSystem, notificationCache, m.Notifications,
map != null ? map.NotificationDefinitions : NoMapRules,
notifications = LoadYamlRules(fileSystem, notificationCache, m.Notifications, additionalNotifications,
k => new SoundInfo(k.Value));
using (new PerfTimer("Music"))
music = LoadYamlRules(fileSystem, musicCache, m.Music,
map != null ? map.MusicDefinitions : NoMapRules,
music = LoadYamlRules(fileSystem, musicCache, m.Music, additionalMusic,
k => new MusicInfo(k.Key, k.Value));
using (new PerfTimer("TileSets"))
tileSets = LoadTileSets(fileSystem, tileSetCache, m.TileSets);
// 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);
}
/// <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> itemCache,
string[] files, string[] mapFiles,
IEnumerable<string> files, MiniYaml mapRules,
Func<MiniYamlNode, T> f)
{
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 =>
{
var key = inputKey + wkv.Value.ToLines(wkv.Key).JoinWith("|");
@@ -114,8 +126,9 @@ namespace OpenRA
return t;
};
var tree = MiniYaml.Merge(files.Append(mapFiles).Select(s => MiniYaml.FromStream(fileSystem.Open(s))))
.ToDictionaryWithConflictLog(n => n.Key, n => n.Value, "LoadYamlRules", null, null);
var tree = MiniYaml.Load(fileSystem, files, mapRules).ToDictionaryWithConflictLog(
n => n.Key, n => n.Value, "LoadYamlRules", null, null);
RaiseProgress();
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>();
public SequenceProvider(IReadOnlyFileSystem fileSystem, ModData modData, TileSet tileSet, Map map)
public SequenceProvider(IReadOnlyFileSystem fileSystem, ModData modData, TileSet tileSet, MiniYaml additionalSequences)
{
this.modData = modData;
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)));
}
@@ -97,17 +102,9 @@ namespace OpenRA.Graphics
return unitSeq.Value.Keys;
}
public Sequences LoadSequences(IReadOnlyFileSystem fileSystem, Map map)
Sequences Load(IReadOnlyFileSystem fileSystem, MiniYaml additionalSequences)
{
using (new Support.PerfTimer("LoadSequences"))
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 nodes = MiniYaml.Load(fileSystem, modData.Manifest.Sequences, additionalSequences);
var items = new Dictionary<string, UnitSequences>();
foreach (var n in nodes)
{

View File

@@ -21,13 +21,9 @@ namespace OpenRA.Graphics
{
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>>();
var sequences = MiniYaml.Merge(voxelFiles.Select(
s => MiniYaml.FromStream(fileSystem.Open(s))));
foreach (var s in sequences)
LoadVoxelsForUnit(loader, s.Key, s.Value);

View File

@@ -141,14 +141,14 @@ namespace OpenRA
public Lazy<CPos[]> SpawnPoints;
// Yaml map data
[FieldLoader.Ignore] public readonly string[] RuleDefinitions = { };
[FieldLoader.Ignore] public readonly string[] SequenceDefinitions = { };
[FieldLoader.Ignore] public readonly string[] VoxelSequenceDefinitions = { };
[FieldLoader.Ignore] public readonly string[] WeaponDefinitions = { };
[FieldLoader.Ignore] public readonly string[] VoiceDefinitions = { };
[FieldLoader.Ignore] public readonly string[] MusicDefinitions = { };
[FieldLoader.Ignore] public readonly string[] NotificationDefinitions = { };
[FieldLoader.Ignore] public readonly string[] TranslationDefinitions = { };
[FieldLoader.Ignore] public readonly MiniYaml RuleDefinitions;
[FieldLoader.Ignore] public readonly MiniYaml SequenceDefinitions;
[FieldLoader.Ignore] public readonly MiniYaml VoxelSequenceDefinitions;
[FieldLoader.Ignore] public readonly MiniYaml WeaponDefinitions;
[FieldLoader.Ignore] public readonly MiniYaml VoiceDefinitions;
[FieldLoader.Ignore] public readonly MiniYaml MusicDefinitions;
[FieldLoader.Ignore] public readonly MiniYaml NotificationDefinitions;
[FieldLoader.Ignore] public readonly MiniYaml TranslationDefinitions;
[FieldLoader.Ignore] public List<MiniYamlNode> PlayerDefinitions = 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));
}
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>
/// 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.
@@ -260,14 +253,14 @@ namespace OpenRA
return spawns.ToArray();
});
LoadFileList(yaml, "Rules", ref RuleDefinitions);
LoadFileList(yaml, "Sequences", ref SequenceDefinitions);
LoadFileList(yaml, "VoxelSequences", ref VoxelSequenceDefinitions);
LoadFileList(yaml, "Weapons", ref WeaponDefinitions);
LoadFileList(yaml, "Voices", ref VoiceDefinitions);
LoadFileList(yaml, "Music", ref MusicDefinitions);
LoadFileList(yaml, "Notifications", ref NotificationDefinitions);
LoadFileList(yaml, "Translations", ref TranslationDefinitions);
RuleDefinitions = LoadRuleSection(yaml, "Rules");
SequenceDefinitions = LoadRuleSection(yaml, "Sequences");
VoxelSequenceDefinitions = LoadRuleSection(yaml, "VoxelSequences");
WeaponDefinitions = LoadRuleSection(yaml, "Weapons");
VoiceDefinitions = LoadRuleSection(yaml, "Voices");
MusicDefinitions = LoadRuleSection(yaml, "Music");
NotificationDefinitions = LoadRuleSection(yaml, "Notifications");
TranslationDefinitions = LoadRuleSection(yaml, "Translations");
PlayerDefinitions = MiniYaml.NodesOrEmpty(yaml, "Players");
ActorDefinitions = MiniYaml.NodesOrEmpty(yaml, "Actors");
@@ -287,6 +280,12 @@ namespace OpenRA
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()
{
rules = Exts.Lazy(() =>
@@ -448,21 +447,21 @@ namespace OpenRA
root.Add(new MiniYamlNode("Players", null, PlayerDefinitions));
root.Add(new MiniYamlNode("Actors", null, ActorDefinitions));
var fileFields = new[]
var ruleSections = new[]
{
Pair.New("Rules", RuleDefinitions),
Pair.New("Sequences", SequenceDefinitions),
Pair.New("VoxelSequences", VoxelSequenceDefinitions),
Pair.New("Weapons", WeaponDefinitions),
Pair.New("Voices", VoiceDefinitions),
Pair.New("Music", MusicDefinitions),
Pair.New("Notifications", NotificationDefinitions),
Pair.New("Translations", TranslationDefinitions)
new MiniYamlNode("Rules", RuleDefinitions),
new MiniYamlNode("Sequences", SequenceDefinitions),
new MiniYamlNode("VoxelSequences", VoxelSequenceDefinitions),
new MiniYamlNode("Weapons", WeaponDefinitions),
new MiniYamlNode("Voices", VoiceDefinitions),
new MiniYamlNode("Music", MusicDefinitions),
new MiniYamlNode("Notifications", NotificationDefinitions),
new MiniYamlNode("Translations", TranslationDefinitions)
};
foreach (var kv in fileFields)
if (kv.Second.Any())
root.Add(new MiniYamlNode(kv.First, FieldSaver.FormatValue(kv.Second)));
foreach (var section in ruleSections)
if (section.Value != null && (section.Value.Value != null || section.Value.Nodes.Any()))
root.Add(section);
// Saving to a new package: copy over all the content from the map
if (Package != null && toPackage != Package)

View File

@@ -13,6 +13,7 @@ using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using OpenRA.FileSystem;
namespace OpenRA
{
@@ -375,6 +376,21 @@ namespace OpenRA
foreach (var line in Nodes.ToLines(false))
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]

View File

@@ -130,8 +130,7 @@ namespace OpenRA
return;
}
var yaml = MiniYaml.Merge(Manifest.Translations.Append(map.TranslationDefinitions)
.Select(t => MiniYaml.FromStream(map.Open(t))));
var yaml = MiniYaml.Load(map, Manifest.Translations, map.TranslationDefinitions);
Languages = yaml.Select(t => t.Key).ToArray();
foreach (var y in yaml)
@@ -182,7 +181,7 @@ namespace OpenRA
foreach (var entry in map.Rules.Music)
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();
return map;

View File

@@ -392,7 +392,7 @@ namespace OpenRA.Server
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.");
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)
{
if (map != null && !map.SequenceDefinitions.Any())
if (map.SequenceDefinitions == null)
return;
var modData = Game.ModData;
this.emitError = emitError;
var mapSequences = map != null ? map.SequenceDefinitions : new string[0];
sequenceDefinitions = MiniYaml.Merge(modData.Manifest.Sequences.Append(mapSequences).Select(s => MiniYaml.FromStream(map.Open(s))));
sequenceDefinitions = MiniYaml.Load(map, modData.Manifest.Sequences, map.SequenceDefinitions);
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 sequenceProviders = map == null ? rules.Sequences.Values : new[] { rules.Sequences[map.Tileset] };
var sequenceProviders = new[] { rules.Sequences[map.Tileset] };
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));
if (server.Map.RuleDefinitions.Any())
if (server.Map.RuleDefinitions != null)
server.SendMessage("This map contains custom rules. Game experience may change.");
if (server.Settings.DisableSinglePlayer)

View File

@@ -90,7 +90,7 @@ namespace OpenRA.Mods.Common.UtilityCommands
testMap.PreloadRules();
// 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);
// 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);
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 (!map.Package.Contains(filename))
continue;
if (yaml == null)
return;
var yaml = MiniYaml.FromStream(map.Package.GetStream(filename));
processFile(engineDate, ref yaml, null, 0);
((IReadWritePackage)map.Package).Update(filename, Encoding.ASCII.GetBytes(yaml.WriteToString()));
}
if (yaml.Value != null)
{
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)