#region Copyright & License Information /* * Copyright 2007-2016 The OpenRA Developers (see AUTHORS) * This file is part of OpenRA, which is free software. It is made * available to you under the terms of the GNU General Public License * as published by the Free Software Foundation. For more information, * see COPYING. */ #endregion using System; using System.Collections.Generic; using System.Linq; using OpenRA.FileSystem; using OpenRA.GameRules; using OpenRA.Graphics; using OpenRA.Support; namespace OpenRA { public sealed class RulesetCache { static readonly List NoMapRules = new List(); readonly ModData modData; readonly Dictionary actorCache = new Dictionary(); readonly Dictionary weaponCache = new Dictionary(); readonly Dictionary voiceCache = new Dictionary(); readonly Dictionary notificationCache = new Dictionary(); readonly Dictionary musicCache = new Dictionary(); readonly Dictionary tileSetCache = new Dictionary(); public event EventHandler LoadingProgress; void RaiseProgress() { if (LoadingProgress != null) LoadingProgress(this, new EventArgs()); } public RulesetCache(ModData modData) { this.modData = modData; } /// /// Cache and return the Ruleset for a given map. /// If a map isn't specified then return the default mod Ruleset. /// public Ruleset Load(IReadOnlyFileSystem fileSystem, Map map = null) { var m = modData.Manifest; Dictionary actors; Dictionary weapons; Dictionary voices; Dictionary notifications; Dictionary music; Dictionary tileSets; using (new PerfTimer("Actors")) actors = LoadYamlRules(fileSystem, actorCache, m.Rules, map != null ? map.RuleDefinitions : NoMapRules, 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, k => new WeaponInfo(k.Key.ToLowerInvariant(), k.Value)); using (new PerfTimer("Voices")) voices = LoadYamlRules(fileSystem, voiceCache, m.Voices, map != null ? map.VoiceDefinitions : NoMapRules, k => new SoundInfo(k.Value)); using (new PerfTimer("Notifications")) notifications = LoadYamlRules(fileSystem, notificationCache, m.Notifications, map != null ? map.NotificationDefinitions : NoMapRules, k => new SoundInfo(k.Value)); using (new PerfTimer("Music")) music = LoadYamlRules(fileSystem, musicCache, m.Music, map != null ? map.MusicDefinitions : NoMapRules, 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)); return new Ruleset(actors, weapons, voices, notifications, music, tileSets, sequences); } Dictionary LoadYamlRules(IReadOnlyFileSystem fileSystem, Dictionary itemCache, string[] files, List nodes, Func f) { RaiseProgress(); var inputKey = string.Concat(string.Join("|", files), "|", nodes.WriteToString()); Func wrap = wkv => { var key = inputKey + wkv.Value.ToLines(wkv.Key).JoinWith("|"); T t; if (itemCache.TryGetValue(key, out t)) return t; t = f(wkv); itemCache.Add(key, t); RaiseProgress(); return t; }; var tree = MiniYaml.Merge(files.Select(s => MiniYaml.FromStream(fileSystem.Open(s))).Append(nodes)) .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))); RaiseProgress(); return itemSet; } Dictionary LoadTileSets(IReadOnlyFileSystem fileSystem, Dictionary itemCache, string[] files) { var items = new Dictionary(); foreach (var file in files) { TileSet t; if (itemCache.TryGetValue(file, out t)) items.Add(t.Id, t); else { t = new TileSet(fileSystem, file); itemCache.Add(file, t); items.Add(t.Id, t); } } return items; } } }