#region Copyright & License Information /* * Copyright 2007-2015 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.GameRules; using OpenRA.Graphics; using OpenRA.Support; namespace OpenRA { public sealed class RulesetCache : IDisposable { 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(); readonly Dictionary sequenceCaches = new Dictionary(); public event EventHandler LoadingProgress; void RaiseProgress() { if (LoadingProgress != null) LoadingProgress(this, new EventArgs()); } public RulesetCache(ModData modData) { this.modData = modData; } public Ruleset LoadDefaultRules() { return LoadMapRules(new Map()); } public Ruleset LoadMapRules(Map map) { var m = modData.Manifest; Dictionary actors; Dictionary weapons; Dictionary voices; Dictionary notifications; Dictionary music; Dictionary tileSets; using (new PerfTimer("Actors")) actors = LoadYamlRules(actorCache, m.Rules, map.RuleDefinitions, (k, y) => new ActorInfo(k.Key.ToLowerInvariant(), k.Value, y)); using (new PerfTimer("Weapons")) weapons = LoadYamlRules(weaponCache, m.Weapons, map.WeaponDefinitions, (k, _) => new WeaponInfo(k.Key.ToLowerInvariant(), k.Value)); using (new PerfTimer("Voices")) voices = LoadYamlRules(voiceCache, m.Voices, map.VoiceDefinitions, (k, _) => new SoundInfo(k.Value)); using (new PerfTimer("Notifications")) notifications = LoadYamlRules(notificationCache, m.Notifications, map.NotificationDefinitions, (k, _) => new SoundInfo(k.Value)); using (new PerfTimer("Music")) music = LoadYamlRules(musicCache, m.Music, new List(), (k, _) => new MusicInfo(k.Key, k.Value)); using (new PerfTimer("TileSets")) tileSets = LoadTileSets(tileSetCache, sequenceCaches, m.TileSets); var sequences = sequenceCaches.ToDictionary(kvp => kvp.Key, kvp => new SequenceProvider(kvp.Value, map)); return new Ruleset(actors, weapons, voices, notifications, music, tileSets, sequences); } Dictionary LoadYamlRules( Dictionary itemCache, string[] files, List nodes, Func, T> f) { RaiseProgress(); var inputKey = string.Concat(string.Join("|", files), "|", nodes.WriteToString()); var mergedNodes = files .Select(s => MiniYaml.FromFile(s)) .Aggregate(nodes, MiniYaml.MergeLiberal); Func, T> wrap = (wkv, wyy) => { var key = inputKey + wkv.Value.ToLines(wkv.Key).JoinWith("|"); T t; if (itemCache.TryGetValue(key, out t)) return t; t = f(wkv, wyy); itemCache.Add(key, t); RaiseProgress(); return t; }; var yy = mergedNodes.ToDictionary(x => x.Key, x => x.Value); var itemSet = mergedNodes.ToDictionaryWithConflictLog(kv => kv.Key.ToLowerInvariant(), kv => wrap(kv, yy), "LoadYamlRules", null, null); RaiseProgress(); return itemSet; } Dictionary LoadTileSets(Dictionary itemCache, Dictionary sequenceCaches, 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(modData, file); itemCache.Add(file, t); // every time we load a tile set, we create a sequence cache for it var sc = new SequenceCache(modData, t); sequenceCaches.Add(t.Id, sc); items.Add(t.Id, t); } } return items; } public void Dispose() { foreach (var cache in sequenceCaches.Values) cache.Dispose(); sequenceCaches.Clear(); } } }