Restore support for inline map rules.
This commit is contained in:
@@ -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)));
|
||||
|
||||
@@ -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)
|
||||
{
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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]
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
{
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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)
|
||||
|
||||
Reference in New Issue
Block a user