Example #1
0
        public ExternalMods()
        {
            // Don't try to load mod icons if we don't have a texture to put them in
            if (Game.Renderer != null)
            {
                sheetBuilder = new SheetBuilder(SheetType.BGRA, CreateSheet);
            }

            // Several types of support directory types are available, depending on
            // how the player has installed and launched the game.
            // Read registration metadata from all of them
            foreach (var source in GetSupportDirs(ModRegistration.User | ModRegistration.System))
            {
                var metadataPath = Path.Combine(source, "ModMetadata");
                if (!Directory.Exists(metadataPath))
                {
                    continue;
                }

                foreach (var path in Directory.GetFiles(metadataPath, "*.yaml"))
                {
                    try
                    {
                        var yaml = MiniYaml.FromStream(File.OpenRead(path), path).First().Value;
                        LoadMod(yaml, path);
                    }
                    catch (Exception e)
                    {
                        Log.Write("debug", "Failed to parse mod metadata file '{0}'", path);
                        Log.Write("debug", e.ToString());
                    }
                }
            }
        }
Example #2
0
        public ExternalMods()
        {
            sheetBuilder = new SheetBuilder(SheetType.BGRA, 256);

            // Several types of support directory types are available, depending on
            // how the player has installed and launched the game.
            // Read registration metadata from all of them
            var sources = Enum.GetValues(typeof(SupportDirType))
                          .Cast <SupportDirType>()
                          .Select(t => Platform.GetSupportDir(t))
                          .Distinct();

            foreach (var source in sources)
            {
                var metadataPath = Path.Combine(source, "ModMetadata");
                if (!Directory.Exists(metadataPath))
                {
                    continue;
                }

                foreach (var path in Directory.GetFiles(metadataPath, "*.yaml"))
                {
                    try
                    {
                        var yaml = MiniYaml.FromStream(File.OpenRead(path), path).First().Value;
                        LoadMod(yaml, path);
                    }
                    catch (Exception e)
                    {
                        Log.Write("debug", "Failed to parse mod metadata file '{0}'", path);
                        Log.Write("debug", e.ToString());
                    }
                }
            }
        }
Example #3
0
        public ExternalMods()
        {
            sheetBuilder = new SheetBuilder(SheetType.BGRA, 256);

            // If the player has defined a local support directory (in the game directory)
            // then this will override both the regular and system support dirs
            var sources = new[] { Platform.SystemSupportDir, Platform.SupportDir };

            foreach (var source in sources.Distinct())
            {
                var metadataPath = Path.Combine(source, "ModMetadata");
                if (!Directory.Exists(metadataPath))
                {
                    continue;
                }

                foreach (var path in Directory.GetFiles(metadataPath, "*.yaml"))
                {
                    try
                    {
                        var yaml = MiniYaml.FromStream(File.OpenRead(path), path).First().Value;
                        LoadMod(yaml, path);
                    }
                    catch (Exception e)
                    {
                        Log.Write("debug", "Failed to parse mod metadata file '{0}'", path);
                        Log.Write("debug", e.ToString());
                    }
                }
            }
        }
Example #4
0
        public static bool DefinesUnsafeCustomRules(ModData modData, IReadOnlyFileSystem fileSystem,
                                                    MiniYaml mapRules, MiniYaml mapWeapons, MiniYaml mapVoices, MiniYaml mapNotifications, MiniYaml mapSequences)
        {
            // Maps that define any weapon, voice, notification, or sequence overrides are always flagged
            if (AnyCustomYaml(mapWeapons) || AnyCustomYaml(mapVoices) || AnyCustomYaml(mapNotifications) || AnyCustomYaml(mapSequences))
            {
                return(true);
            }

            // Any trait overrides that aren't explicitly whitelisted are flagged
            if (mapRules != null)
            {
                if (AnyFlaggedTraits(modData, mapRules.Nodes))
                {
                    return(true);
                }

                if (mapRules.Value != null)
                {
                    var mapFiles = FieldLoader.GetValue <string[]>("value", mapRules.Value);
                    foreach (var f in mapFiles)
                    {
                        if (AnyFlaggedTraits(modData, MiniYaml.FromStream(fileSystem.Open(f), f)))
                        {
                            return(true);
                        }
                    }
                }
            }

            return(false);
        }
Example #5
0
        public ExternalMods(string launchPath)
        {
            // Process.Start requires paths to not be quoted, even if they contain spaces
            if (launchPath.First() == '"' && launchPath.Last() == '"')
            {
                launchPath = launchPath.Substring(1, launchPath.Length - 2);
            }

            this.launchPath = launchPath;
            sheetBuilder    = new SheetBuilder(SheetType.BGRA, 256);

            // Load registered mods
            var supportPath = Platform.ResolvePath(Path.Combine("^", "ModMetadata"));

            if (!Directory.Exists(supportPath))
            {
                return;
            }

            foreach (var path in Directory.GetFiles(supportPath, "*.yaml"))
            {
                try
                {
                    var yaml = MiniYaml.FromStream(File.OpenRead(path), path).First().Value;
                    LoadMod(yaml);
                }
                catch (Exception e)
                {
                    Log.Write("debug", "Failed to parse mod metadata file '{0}'", path);
                    Log.Write("debug", e.ToString());
                }
            }
        }
Example #6
0
        public void QueryRemoteMapDetails(string repositoryUrl, IEnumerable <string> uids, Action <MapPreview> mapDetailsReceived = null, Action <MapPreview> mapQueryFailed = null)
        {
            var queryUids = uids.Distinct()
                            .Where(uid => uid != null)
                            .Select(uid => previews[uid])
                            .Where(p => p.Status == MapStatus.Unavailable)
                            .Select(p => p.Uid)
                            .ToList();

            foreach (var uid in queryUids)
            {
                previews[uid].UpdateRemoteSearch(MapStatus.Searching, null);
            }

            Task.Run(async() =>
            {
                var client = HttpClientFactory.Create();

                // Limit each query to 50 maps at a time to avoid request size limits
                for (var i = 0; i < queryUids.Count; i += 50)
                {
                    var batchUids = queryUids.Skip(i).Take(50).ToList();
                    var url       = repositoryUrl + "hash/" + string.Join(",", batchUids) + "/yaml";
                    try
                    {
                        var httpResponseMessage = await client.GetAsync(url);
                        var result = await httpResponseMessage.Content.ReadAsStreamAsync();

                        var yaml = MiniYaml.FromStream(result);
                        foreach (var kv in yaml)
                        {
                            previews[kv.Key].UpdateRemoteSearch(MapStatus.DownloadAvailable, kv.Value, mapDetailsReceived);
                        }

                        foreach (var uid in batchUids)
                        {
                            var p = previews[uid];
                            if (p.Status != MapStatus.DownloadAvailable)
                            {
                                p.UpdateRemoteSearch(MapStatus.Unavailable, null);
                            }
                        }
                    }
                    catch (Exception e)
                    {
                        Log.Write("debug", "Remote map query failed with error: {0}", e);
                        Log.Write("debug", "URL was: {0}", url);

                        foreach (var uid in batchUids)
                        {
                            var p = previews[uid];
                            p.UpdateRemoteSearch(MapStatus.Unavailable, null);
                            mapQueryFailed?.Invoke(p);
                        }
                    }
                }
            });
        }
Example #7
0
        /// <summary>
        /// Removes invalid mod registrations:
        /// * LaunchPath no longer exists
        /// * LaunchPath and mod id matches the active mod, but the version is different
        /// * Filename doesn't match internal key
        /// * Fails to parse as a mod registration
        /// </summary>
        internal void ClearInvalidRegistrations(ModRegistration registration)
        {
            foreach (var source in GetSupportDirs(registration))
            {
                var metadataPath = Path.Combine(source, "ModMetadata");
                if (!Directory.Exists(metadataPath))
                {
                    continue;
                }

                foreach (var path in Directory.GetFiles(metadataPath, "*.yaml"))
                {
                    string modKey = null;
                    try
                    {
                        var yaml = MiniYaml.FromStream(File.OpenRead(path), path).First().Value;
                        var m    = FieldLoader.Load <ExternalMod>(yaml);
                        modKey = ExternalMod.MakeKey(m);

                        // Continue to the next entry if this one is valid
                        // HACK: Explicitly invalidate paths to OpenRA.dll to clean up bogus metadata files
                        // that were created after the initial migration from .NET Framework to Core/5.
                        if (File.Exists(m.LaunchPath) && Path.GetFileNameWithoutExtension(path) == modKey && Path.GetExtension(m.LaunchPath) != ".dll")
                        {
                            continue;
                        }
                    }
                    catch (Exception e)
                    {
                        Log.Write("debug", "Failed to parse mod metadata file '{0}'", path);
                        Log.Write("debug", e.ToString());
                    }

                    // Remove from the ingame mod switcher
                    if (Path.GetFileNameWithoutExtension(path) == modKey)
                    {
                        mods.Remove(modKey);
                    }

                    // Remove stale or corrupted metadata
                    try
                    {
                        File.Delete(path);
                        Log.Write("debug", "Removed invalid mod metadata file '{0}'", path);
                    }
                    catch (Exception e)
                    {
                        Log.Write("debug", "Failed to remove mod metadata file '{0}'", path);
                        Log.Write("debug", e.ToString());
                    }
                }
            }
        }
        public void RefreshPlayerData(Action onComplete = null)
        {
            if (State != LinkState.Unlinked && State != LinkState.Linked && State != LinkState.ConnectionFailed)
            {
                return;
            }

            Task.Run(async() =>
            {
                try
                {
                    var client = HttpClientFactory.Create();

                    var httpResponseMessage = await client.GetAsync(playerDatabase.Profile + Fingerprint);
                    var result = await httpResponseMessage.Content.ReadAsStreamAsync();

                    var yaml = MiniYaml.FromStream(result).First();
                    if (yaml.Key == "Player")
                    {
                        innerData = FieldLoader.Load <PlayerProfile>(yaml.Value);
                        if (innerData.KeyRevoked)
                        {
                            Log.Write("debug", "Revoking key with fingerprint {0}", Fingerprint);
                            DeleteKeypair();
                        }
                        else
                        {
                            innerState = LinkState.Linked;
                        }
                    }
                    else
                    {
                        innerState = LinkState.Unlinked;
                    }
                }
                catch (Exception e)
                {
                    Log.Write("debug", "Failed to parse player data result with exception: {0}", e);
                    innerState = LinkState.ConnectionFailed;
                }
                finally
                {
                    onComplete?.Invoke();
                }
            });

            innerState = LinkState.CheckingLink;
        }
Example #9
0
        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), s));

            if (mapRules != null && mapRules.Nodes.Any())
            {
                yaml = yaml.Append(mapRules.Nodes);
            }

            return(MiniYaml.Merge(yaml));
        }
Example #10
0
        public WidgetLoader(ModData modData)
        {
            this.modData = modData;

            foreach (var file in modData.Manifest.ChromeLayout.Select(a => MiniYaml.FromStream(modData.DefaultFileSystem.Open(a), a)))
            {
                foreach (var w in file)
                {
                    var key = w.Key.Substring(w.Key.IndexOf('@') + 1);
                    if (widgets.ContainsKey(key))
                    {
                        throw new InvalidDataException("Widget has duplicate Key `{0}` at {1}".F(w.Key, w.Location));
                    }
                    widgets.Add(key, w);
                }
            }
        }
Example #11
0
            public void SetCustomRules(ModData modData, IReadOnlyFileSystem fileSystem, Dictionary <string, MiniYaml> yaml)
            {
                RuleDefinitions          = LoadRuleSection(yaml, "Rules");
                WeaponDefinitions        = LoadRuleSection(yaml, "Weapons");
                VoiceDefinitions         = LoadRuleSection(yaml, "Voices");
                MusicDefinitions         = LoadRuleSection(yaml, "Music");
                NotificationDefinitions  = LoadRuleSection(yaml, "Notifications");
                SequenceDefinitions      = LoadRuleSection(yaml, "Sequences");
                ModelSequenceDefinitions = LoadRuleSection(yaml, "ModelSequences");

                try
                {
                    // PERF: Implement a minimal custom loader for custom world and player actors to minimize loading time
                    // This assumes/enforces that these actor types can only inherit abstract definitions (starting with ^)
                    if (RuleDefinitions != null)
                    {
                        var files = modData.Manifest.Rules.AsEnumerable();
                        if (RuleDefinitions.Value != null)
                        {
                            var mapFiles = FieldLoader.GetValue <string[]>("value", RuleDefinitions.Value);
                            files = files.Append(mapFiles);
                        }

                        var sources = files.Select(s => MiniYaml.FromStream(fileSystem.Open(s), s).Where(IsLoadableRuleDefinition).ToList());
                        if (RuleDefinitions.Nodes.Any())
                        {
                            sources = sources.Append(RuleDefinitions.Nodes.Where(IsLoadableRuleDefinition).ToList());
                        }

                        var yamlNodes = MiniYaml.Merge(sources);
                        WorldActorInfo  = new ActorInfo(modData.ObjectCreator, "world", yamlNodes.First(n => n.Key.ToLowerInvariant() == "world").Value);
                        PlayerActorInfo = new ActorInfo(modData.ObjectCreator, "player", yamlNodes.First(n => n.Key.ToLowerInvariant() == "player").Value);
                        return;
                    }
                }
                catch (Exception e)
                {
                    Log.Write("debug", "Failed to load rules for `{0}` with error: {1}", Title, e.Message);
                }

                WorldActorInfo  = modData.DefaultRules.Actors[SystemActors.World];
                PlayerActorInfo = modData.DefaultRules.Actors[SystemActors.Player];
            }
Example #12
0
        public TileSet(IReadOnlyFileSystem fileSystem, string filepath)
        {
            var yaml = MiniYaml.FromStream(fileSystem.Open(filepath), filepath)
                       .ToDictionary(x => x.Key, x => x.Value);

            // General info
            FieldLoader.Load(this, yaml["General"]);

            // TerrainTypes
            TerrainInfo = yaml["Terrain"].ToDictionary().Values
                          .Select(y => new TerrainTypeInfo(y))
                          .OrderBy(tt => tt.Type)
                          .ToArray();

            if (TerrainInfo.Length >= byte.MaxValue)
            {
                throw new InvalidDataException("Too many terrain types.");
            }

            for (byte i = 0; i < TerrainInfo.Length; i++)
            {
                var tt = TerrainInfo[i].Type;

                if (terrainIndexByType.ContainsKey(tt))
                {
                    throw new InvalidDataException("Duplicate terrain type '{0}' in '{1}'.".F(tt, filepath));
                }

                terrainIndexByType.Add(tt, i);
            }

            defaultWalkableTerrainIndex = GetTerrainIndex("Clear");

            // Templates
            Templates = yaml["Templates"].ToDictionary().Values
                        .Select(y => new TerrainTemplateInfo(this, y)).ToDictionary(t => t.Id).AsReadOnly();
        }
Example #13
0
        /// <summary>
        /// Removes invalid mod registrations:
        /// * LaunchPath no longer exists
        /// * LaunchPath and mod id matches the active mod, but the version is different
        /// * Filename doesn't match internal key
        /// * Fails to parse as a mod registration
        /// </summary>
        internal void ClearInvalidRegistrations(ExternalMod activeMod, ModRegistration registration)
        {
            var sources = new List <string>();

            if (registration.HasFlag(ModRegistration.System))
            {
                sources.Add(Platform.GetSupportDir(SupportDirType.System));
            }

            if (registration.HasFlag(ModRegistration.User))
            {
                // User support dir may be using the modern or legacy value, or overridden by the user
                // Add all the possibilities and let the .Distinct() below ignore the duplicates
                sources.Add(Platform.GetSupportDir(SupportDirType.User));
                sources.Add(Platform.GetSupportDir(SupportDirType.ModernUser));
                sources.Add(Platform.GetSupportDir(SupportDirType.LegacyUser));
            }

            var activeModKey = ExternalMod.MakeKey(activeMod);

            foreach (var source in sources.Distinct())
            {
                var metadataPath = Path.Combine(source, "ModMetadata");
                if (!Directory.Exists(metadataPath))
                {
                    continue;
                }

                foreach (var path in Directory.GetFiles(metadataPath, "*.yaml"))
                {
                    string modKey = null;
                    try
                    {
                        var yaml = MiniYaml.FromStream(File.OpenRead(path), path).First().Value;
                        var m    = FieldLoader.Load <ExternalMod>(yaml);
                        modKey = ExternalMod.MakeKey(m);

                        // Continue to the next entry if it is the active mod (even if the LaunchPath is bogus)
                        if (modKey == activeModKey)
                        {
                            continue;
                        }

                        // Continue to the next entry if this one is valid
                        if (File.Exists(m.LaunchPath) && Path.GetFileNameWithoutExtension(path) == modKey &&
                            !(activeMod != null && m.LaunchPath == activeMod.LaunchPath && m.Id == activeMod.Id && m.Version != activeMod.Version))
                        {
                            continue;
                        }
                    }
                    catch (Exception e)
                    {
                        Log.Write("debug", "Failed to parse mod metadata file '{0}'", path);
                        Log.Write("debug", e.ToString());
                    }

                    // Remove from the ingame mod switcher
                    if (Path.GetFileNameWithoutExtension(path) == modKey)
                    {
                        mods.Remove(modKey);
                    }

                    // Remove stale or corrupted metadata
                    try
                    {
                        File.Delete(path);
                        Log.Write("debug", "Removed invalid mod metadata file '{0}'", path);
                    }
                    catch (Exception e)
                    {
                        Log.Write("debug", "Failed to remove mod metadata file '{0}'", path);
                        Log.Write("debug", e.ToString());
                    }
                }
            }
        }
Example #14
0
        // The standard constructor for most purposes
        public Map(string path)
        {
            Path      = path;
            Container = GlobalFileSystem.OpenPackage(path, null, int.MaxValue);

            AssertExists("map.yaml");
            AssertExists("map.bin");

            var yaml = new MiniYaml(null, MiniYaml.FromStream(Container.GetContent("map.yaml"), path));

            FieldLoader.Load(this, yaml);

            // Support for formats 1-3 dropped 2011-02-11.
            // Use release-20110207 to convert older maps to format 4
            // Use release-20110511 to convert older maps to format 5
            // Use release-20141029 to convert older maps to format 6
            if (MapFormat < 6)
            {
                throw new InvalidDataException("Map format {0} is not supported.\n File: {1}".F(MapFormat, path));
            }

            var nd = yaml.ToDictionary();

            // Format 6 -> 7 combined the Selectable and UseAsShellmap flags into the Class enum
            if (MapFormat < 7)
            {
                MiniYaml useAsShellmap;
                if (nd.TryGetValue("UseAsShellmap", out useAsShellmap) && bool.Parse(useAsShellmap.Value))
                {
                    Visibility = MapVisibility.Shellmap;
                }
                else if (Type == "Mission" || Type == "Campaign")
                {
                    Visibility = MapVisibility.MissionSelector;
                }
            }

            // Load players
            foreach (var my in nd["Players"].ToDictionary().Values)
            {
                var player = new PlayerReference(my);
                Players.Add(player.Name, player);
            }

            Actors = Exts.Lazy(() =>
            {
                var ret = new Dictionary <string, ActorReference>();
                foreach (var kv in nd["Actors"].ToDictionary())
                {
                    ret.Add(kv.Key, new ActorReference(kv.Value.Value, kv.Value.ToDictionary()));
                }
                return(ret);
            });

            // Smudges
            Smudges = Exts.Lazy(() =>
            {
                var ret = new List <SmudgeReference>();
                foreach (var name in nd["Smudges"].ToDictionary().Keys)
                {
                    var vals = name.Split(' ');
                    var loc  = vals[1].Split(',');
                    ret.Add(new SmudgeReference(vals[0], new int2(
                                                    Exts.ParseIntegerInvariant(loc[0]),
                                                    Exts.ParseIntegerInvariant(loc[1])),
                                                Exts.ParseIntegerInvariant(vals[2])));
                }

                return(ret);
            });

            RuleDefinitions          = MiniYaml.NodesOrEmpty(yaml, "Rules");
            SequenceDefinitions      = MiniYaml.NodesOrEmpty(yaml, "Sequences");
            VoxelSequenceDefinitions = MiniYaml.NodesOrEmpty(yaml, "VoxelSequences");
            WeaponDefinitions        = MiniYaml.NodesOrEmpty(yaml, "Weapons");
            VoiceDefinitions         = MiniYaml.NodesOrEmpty(yaml, "Voices");
            NotificationDefinitions  = MiniYaml.NodesOrEmpty(yaml, "Notifications");
            TranslationDefinitions   = MiniYaml.NodesOrEmpty(yaml, "Translations");

            MapTiles     = Exts.Lazy(() => LoadMapTiles());
            MapResources = Exts.Lazy(() => LoadResourceTiles());
            MapHeight    = Exts.Lazy(() => LoadMapHeight());

            TileShape      = Game.ModData.Manifest.TileShape;
            SubCellOffsets = Game.ModData.Manifest.SubCellOffsets;
            LastSubCell    = (SubCell)(SubCellOffsets.Length - 1);
            DefaultSubCell = (SubCell)Game.ModData.Manifest.SubCellDefaultIndex;

            if (Container.Exists("map.png"))
            {
                using (var dataStream = Container.GetContent("map.png"))
                    CustomPreview = new Bitmap(dataStream);
            }

            PostInit();

            // The Uid is calculated from the data on-disk, so
            // format changes must be flushed to disk.
            // TODO: this isn't very nice
            if (MapFormat < 7)
            {
                Save(path);
            }

            Uid = ComputeHash();
        }
Example #15
0
        public Manifest(string modId, IReadOnlyPackage package)
        {
            Id      = modId;
            Package = package;
            yaml    = new MiniYaml(null, MiniYaml.FromStream(package.GetStream("mod.yaml"), "mod.yaml")).ToDictionary();

            Metadata = FieldLoader.Load <ModMetadata>(yaml["Metadata"]);

            // TODO: Use fieldloader
            MapFolders = YamlDictionary(yaml, "MapFolders");

            MiniYaml packages;

            if (yaml.TryGetValue("Packages", out packages))
            {
                Packages = packages.ToDictionary(x => x.Value).AsReadOnly();
            }

            Rules          = YamlList(yaml, "Rules");
            Sequences      = YamlList(yaml, "Sequences");
            ModelSequences = YamlList(yaml, "ModelSequences");
            Cursors        = YamlList(yaml, "Cursors");
            Chrome         = YamlList(yaml, "Chrome");
            Assemblies     = YamlList(yaml, "Assemblies");
            ChromeLayout   = YamlList(yaml, "ChromeLayout");
            Weapons        = YamlList(yaml, "Weapons");
            Voices         = YamlList(yaml, "Voices");
            Notifications  = YamlList(yaml, "Notifications");
            Music          = YamlList(yaml, "Music");
            Translations   = YamlList(yaml, "Translations");
            TileSets       = YamlList(yaml, "TileSets");
            ChromeMetrics  = YamlList(yaml, "ChromeMetrics");
            Missions       = YamlList(yaml, "Missions");
            Hotkeys        = YamlList(yaml, "Hotkeys");

            ServerTraits = YamlList(yaml, "ServerTraits");

            if (!yaml.TryGetValue("LoadScreen", out LoadScreen))
            {
                throw new InvalidDataException("`LoadScreen` section is not defined.");
            }

            // Allow inherited mods to import parent maps.
            var compat = new List <string> {
                Id
            };

            if (yaml.ContainsKey("SupportsMapsFrom"))
            {
                compat.AddRange(yaml["SupportsMapsFrom"].Value.Split(',').Select(c => c.Trim()));
            }

            MapCompatibility = compat.ToArray();

            if (yaml.ContainsKey("PackageFormats"))
            {
                PackageFormats = FieldLoader.GetValue <string[]>("PackageFormats", yaml["PackageFormats"].Value);
            }

            if (yaml.ContainsKey("SoundFormats"))
            {
                SoundFormats = FieldLoader.GetValue <string[]>("SoundFormats", yaml["SoundFormats"].Value);
            }

            if (yaml.ContainsKey("SpriteFormats"))
            {
                SpriteFormats = FieldLoader.GetValue <string[]>("SpriteFormats", yaml["SpriteFormats"].Value);
            }
        }
Example #16
0
        public Manifest(string modId)
        {
            var package = ModMetadata.AllMods[modId].Package;

            yaml = new MiniYaml(null, MiniYaml.FromStream(package.GetStream("mod.yaml"))).ToDictionary();

            Mod    = FieldLoader.Load <ModMetadata>(yaml["Metadata"]);
            Mod.Id = modId;

            // TODO: Use fieldloader
            MapFolders = YamlDictionary(yaml, "MapFolders");

            MiniYaml packages;

            if (yaml.TryGetValue("Packages", out packages))
            {
                Packages = packages.ToDictionary(x => x.Value).AsReadOnly();
            }

            Rules          = YamlList(yaml, "Rules");
            Sequences      = YamlList(yaml, "Sequences");
            VoxelSequences = YamlList(yaml, "VoxelSequences");
            Cursors        = YamlList(yaml, "Cursors");
            Chrome         = YamlList(yaml, "Chrome");
            Assemblies     = YamlList(yaml, "Assemblies");
            ChromeLayout   = YamlList(yaml, "ChromeLayout");
            Weapons        = YamlList(yaml, "Weapons");
            Voices         = YamlList(yaml, "Voices");
            Notifications  = YamlList(yaml, "Notifications");
            Music          = YamlList(yaml, "Music");
            Translations   = YamlList(yaml, "Translations");
            TileSets       = YamlList(yaml, "TileSets");
            ChromeMetrics  = YamlList(yaml, "ChromeMetrics");
            Missions       = YamlList(yaml, "Missions");

            ServerTraits = YamlList(yaml, "ServerTraits");

            if (!yaml.TryGetValue("LoadScreen", out LoadScreen))
            {
                throw new InvalidDataException("`LoadScreen` section is not defined.");
            }

            Fonts = yaml["Fonts"].ToDictionary(my =>
            {
                var nd = my.ToDictionary();
                return(Pair.New(nd["Font"].Value, Exts.ParseIntegerInvariant(nd["Size"].Value)));
            });

            RequiresMods = yaml["RequiresMods"].ToDictionary(my => my.Value);

            // Allow inherited mods to import parent maps.
            var compat = new List <string> {
                Mod.Id
            };

            if (yaml.ContainsKey("SupportsMapsFrom"))
            {
                compat.AddRange(yaml["SupportsMapsFrom"].Value.Split(',').Select(c => c.Trim()));
            }

            MapCompatibility = compat.ToArray();

            if (yaml.ContainsKey("SoundFormats"))
            {
                SoundFormats = FieldLoader.GetValue <string[]>("SoundFormats", yaml["SoundFormats"].Value);
            }

            if (yaml.ContainsKey("SpriteFormats"))
            {
                SpriteFormats = FieldLoader.GetValue <string[]>("SpriteFormats", yaml["SpriteFormats"].Value);
            }
        }
Example #17
0
        static Dictionary <string, ModMetadata> ValidateMods()
        {
            var ret = new Dictionary <string, ModMetadata>();

            foreach (var pair in GetCandidateMods())
            {
                IReadOnlyPackage package = null;
                try
                {
                    if (Directory.Exists(pair.Second))
                    {
                        package = new Folder(pair.Second);
                    }
                    else
                    {
                        try
                        {
                            package = new ZipFile(null, pair.Second);
                        }
                        catch
                        {
                            throw new InvalidDataException(pair.Second + " is not a valid mod package");
                        }
                    }

                    if (!package.Contains("mod.yaml"))
                    {
                        package.Dispose();
                        continue;
                    }

                    var yaml = new MiniYaml(null, MiniYaml.FromStream(package.GetStream("mod.yaml")));
                    var nd   = yaml.ToDictionary();
                    if (!nd.ContainsKey("Metadata"))
                    {
                        package.Dispose();
                        continue;
                    }

                    var metadata = FieldLoader.Load <ModMetadata>(nd["Metadata"]);
                    metadata.Id      = pair.First;
                    metadata.Package = package;

                    if (nd.ContainsKey("RequiresMods"))
                    {
                        metadata.RequiresMods = nd["RequiresMods"].ToDictionary(my => my.Value);
                    }
                    else
                    {
                        metadata.RequiresMods = new Dictionary <string, string>();
                    }

                    if (nd.ContainsKey("ContentInstaller"))
                    {
                        metadata.Content = FieldLoader.Load <ContentInstaller>(nd["ContentInstaller"]);
                    }

                    // Mods in the support directory and oramod packages (which are listed later
                    // in the CandidateMods list) override mods in the main install.
                    ret[pair.First] = metadata;
                }
                catch (Exception ex)
                {
                    if (package != null)
                    {
                        package.Dispose();
                    }
                    Console.WriteLine("An exception occurred when trying to load ModMetadata for `{0}`:".F(pair.First));
                    Console.WriteLine(ex.Message);
                }
            }

            return(ret);
        }
Example #18
0
        public Map(ModData modData, IReadOnlyPackage package)
        {
            this.modData = modData;
            Package      = package;

            if (!Package.Contains("map.yaml") || !Package.Contains("map.bin"))
            {
                throw new InvalidDataException("Not a valid map\n File: {0}".F(package.Name));
            }

            var yaml = new MiniYaml(null, MiniYaml.FromStream(Package.GetStream("map.yaml"), package.Name));

            foreach (var field in YamlFields)
            {
                field.Deserialize(this, yaml.Nodes);
            }

            if (MapFormat != SupportedMapFormat)
            {
                throw new InvalidDataException("Map format {0} is not supported.\n File: {1}".F(MapFormat, package.Name));
            }

            PlayerDefinitions = MiniYaml.NodesOrEmpty(yaml, "Players");
            ActorDefinitions  = MiniYaml.NodesOrEmpty(yaml, "Actors");

            Grid = modData.Manifest.Get <MapGrid>();

            var size = new Size(MapSize.X, MapSize.Y);

            Tiles     = new CellLayer <TerrainTile>(Grid.Type, size);
            Resources = new CellLayer <ResourceTile>(Grid.Type, size);
            Height    = new CellLayer <byte>(Grid.Type, size);

            using (var s = Package.GetStream("map.bin"))
            {
                var header = new BinaryDataHeader(s, MapSize);
                if (header.TilesOffset > 0)
                {
                    s.Position = header.TilesOffset;
                    for (var i = 0; i < MapSize.X; i++)
                    {
                        for (var j = 0; j < MapSize.Y; j++)
                        {
                            var tile  = s.ReadUInt16();
                            var index = s.ReadUInt8();

                            // TODO: Remember to remove this when rewriting tile variants / PickAny
                            if (index == byte.MaxValue)
                            {
                                index = (byte)(i % 4 + (j % 4) * 4);
                            }

                            Tiles[new MPos(i, j)] = new TerrainTile(tile, index);
                        }
                    }
                }

                if (header.ResourcesOffset > 0)
                {
                    s.Position = header.ResourcesOffset;
                    for (var i = 0; i < MapSize.X; i++)
                    {
                        for (var j = 0; j < MapSize.Y; j++)
                        {
                            var type    = s.ReadUInt8();
                            var density = s.ReadUInt8();
                            Resources[new MPos(i, j)] = new ResourceTile(type, density);
                        }
                    }
                }

                if (header.HeightsOffset > 0)
                {
                    s.Position = header.HeightsOffset;
                    for (var i = 0; i < MapSize.X; i++)
                    {
                        for (var j = 0; j < MapSize.Y; j++)
                        {
                            Height[new MPos(i, j)] = s.ReadUInt8().Clamp((byte)0, Grid.MaximumTerrainHeight);
                        }
                    }
                }
            }

            if (Grid.MaximumTerrainHeight > 0)
            {
                Tiles.CellEntryChanged  += UpdateProjection;
                Height.CellEntryChanged += UpdateProjection;
            }

            PostInit();

            Uid = ComputeUID(Package);
        }
Example #19
0
        public Map(string path)
        {
            Path      = path;
            Container = FileSystem.OpenPackage(path, int.MaxValue);

            AssertExists("map.yaml");
            AssertExists("map.bin");

            var yaml = new MiniYaml(null, MiniYaml.FromStream(Container.GetContent("map.yaml")));

            FieldLoader.Load(this, yaml);
            Uid = ComputeHash();

            // Support for formats 1-3 dropped 2011-02-11.
            // Use release-20110207 to convert older maps to format 4
            // Use release-20110511 to convert older maps to format 5
            if (MapFormat < 5)
            {
                throw new InvalidDataException("Map format {0} is not supported.\n File: {1}".F(MapFormat, path));
            }

            // Load players
            foreach (var kv in yaml.NodesDict["Players"].NodesDict)
            {
                var player = new PlayerReference(kv.Value);
                Players.Add(player.Name, player);
            }

            Actors = Lazy.New(() =>
            {
                var ret = new Dictionary <string, ActorReference>();
                foreach (var kv in yaml.NodesDict["Actors"].NodesDict)
                {
                    ret.Add(kv.Key, new ActorReference(kv.Value.Value, kv.Value.NodesDict));
                }
                return(ret);
            });

            // Smudges
            Smudges = Lazy.New(() =>
            {
                var ret = new List <SmudgeReference>();
                foreach (var kv in yaml.NodesDict["Smudges"].NodesDict)
                {
                    var vals = kv.Key.Split(' ');
                    var loc  = vals[1].Split(',');
                    ret.Add(new SmudgeReference(vals[0], new int2(int.Parse(loc[0]), int.Parse(loc[1])), int.Parse(vals[2])));
                }

                return(ret);
            });

            Rules     = NodesOrEmpty(yaml, "Rules");
            Sequences = NodesOrEmpty(yaml, "Sequences");
            Weapons   = NodesOrEmpty(yaml, "Weapons");
            Voices    = NodesOrEmpty(yaml, "Voices");

            CustomTerrain = new string[MapSize.X, MapSize.Y];

            MapTiles     = Lazy.New(() => LoadMapTiles());
            MapResources = Lazy.New(() => LoadResourceTiles());
        }
Example #20
0
        public Manifest(string modId, IReadOnlyPackage package)
        {
            Id      = modId;
            Package = package;

            var nodes = MiniYaml.FromStream(package.GetStream("mod.yaml"), "mod.yaml");

            for (var i = nodes.Count - 1; i >= 0; i--)
            {
                if (nodes[i].Key != "Include")
                {
                    continue;
                }

                // Replace `Includes: filename.yaml` with the contents of filename.yaml
                var filename = nodes[i].Value.Value;
                var contents = package.GetStream(filename);
                if (contents == null)
                {
                    throw new YamlException("{0}: File `{1}` not found.".F(nodes[i].Location, filename));
                }

                nodes.RemoveAt(i);
                nodes.InsertRange(i, MiniYaml.FromStream(contents, filename));
            }

            // Merge inherited overrides
            yaml = new MiniYaml(null, MiniYaml.Merge(new[] { nodes })).ToDictionary();

            Metadata = FieldLoader.Load <ModMetadata>(yaml["Metadata"]);

            // TODO: Use fieldloader
            MapFolders = YamlDictionary(yaml, "MapFolders");

            if (yaml.TryGetValue("Packages", out var packages))
            {
                Packages = packages.ToDictionary(x => x.Value);
            }

            Rules          = YamlList(yaml, "Rules");
            Sequences      = YamlList(yaml, "Sequences");
            ModelSequences = YamlList(yaml, "ModelSequences");
            Cursors        = YamlList(yaml, "Cursors");
            Chrome         = YamlList(yaml, "Chrome");
            Assemblies     = YamlList(yaml, "Assemblies");
            ChromeLayout   = YamlList(yaml, "ChromeLayout");
            Weapons        = YamlList(yaml, "Weapons");
            Voices         = YamlList(yaml, "Voices");
            Notifications  = YamlList(yaml, "Notifications");
            Music          = YamlList(yaml, "Music");
            TileSets       = YamlList(yaml, "TileSets");
            ChromeMetrics  = YamlList(yaml, "ChromeMetrics");
            Missions       = YamlList(yaml, "Missions");
            Hotkeys        = YamlList(yaml, "Hotkeys");

            ServerTraits = YamlList(yaml, "ServerTraits");

            if (!yaml.TryGetValue("LoadScreen", out LoadScreen))
            {
                throw new InvalidDataException("`LoadScreen` section is not defined.");
            }

            // Allow inherited mods to import parent maps.
            var compat = new List <string> {
                Id
            };

            if (yaml.ContainsKey("SupportsMapsFrom"))
            {
                compat.AddRange(yaml["SupportsMapsFrom"].Value.Split(',').Select(c => c.Trim()));
            }

            MapCompatibility = compat.ToArray();

            if (yaml.ContainsKey("PackageFormats"))
            {
                PackageFormats = FieldLoader.GetValue <string[]>("PackageFormats", yaml["PackageFormats"].Value);
            }

            if (yaml.ContainsKey("SoundFormats"))
            {
                SoundFormats = FieldLoader.GetValue <string[]>("SoundFormats", yaml["SoundFormats"].Value);
            }

            if (yaml.ContainsKey("SpriteFormats"))
            {
                SpriteFormats = FieldLoader.GetValue <string[]>("SpriteFormats", yaml["SpriteFormats"].Value);
            }

            if (yaml.ContainsKey("VideoFormats"))
            {
                VideoFormats = FieldLoader.GetValue <string[]>("VideoFormats", yaml["VideoFormats"].Value);
            }
        }
Example #21
0
        public void UpdateFromMap(IReadOnlyPackage p, IReadOnlyPackage parent, MapClassification classification, string[] mapCompatibility, MapGridType gridType)
        {
            Dictionary <string, MiniYaml> yaml;

            using (var yamlStream = p.GetStream("map.yaml"))
            {
                if (yamlStream == null)
                {
                    throw new FileNotFoundException("Required file map.yaml not present in this map");
                }

                yaml = new MiniYaml(null, MiniYaml.FromStream(yamlStream, "map.yaml", stringPool: cache.StringPool)).ToDictionary();
            }

            Package       = p;
            parentPackage = parent;

            var newData = innerData.Clone();

            newData.GridType = gridType;
            newData.Class    = classification;

            if (yaml.TryGetValue("MapFormat", out var temp))
            {
                var format = FieldLoader.GetValue <int>("MapFormat", temp.Value);
                if (format != Map.SupportedMapFormat)
                {
                    throw new InvalidDataException($"Map format {format} is not supported.");
                }
            }

            if (yaml.TryGetValue("Title", out temp))
            {
                newData.Title = temp.Value;
            }

            if (yaml.TryGetValue("Categories", out temp))
            {
                newData.Categories = FieldLoader.GetValue <string[]>("Categories", temp.Value);
            }

            if (yaml.TryGetValue("Tileset", out temp))
            {
                newData.TileSet = temp.Value;
            }

            if (yaml.TryGetValue("Author", out temp))
            {
                newData.Author = temp.Value;
            }

            if (yaml.TryGetValue("Bounds", out temp))
            {
                newData.Bounds = FieldLoader.GetValue <Rectangle>("Bounds", temp.Value);
            }

            if (yaml.TryGetValue("Visibility", out temp))
            {
                newData.Visibility = FieldLoader.GetValue <MapVisibility>("Visibility", temp.Value);
            }

            string requiresMod = string.Empty;

            if (yaml.TryGetValue("RequiresMod", out temp))
            {
                requiresMod = temp.Value;
            }

            if (yaml.TryGetValue("MapFormat", out temp))
            {
                newData.MapFormat = FieldLoader.GetValue <int>("MapFormat", temp.Value);
            }

            newData.Status = mapCompatibility == null || mapCompatibility.Contains(requiresMod) ?
                             MapStatus.Available : MapStatus.Unavailable;

            try
            {
                // Actor definitions may change if the map format changes
                if (yaml.TryGetValue("Actors", out var actorDefinitions))
                {
                    var spawns = new List <CPos>();
                    foreach (var kv in actorDefinitions.Nodes.Where(d => d.Value.Value == "mpspawn"))
                    {
                        var s = new ActorReference(kv.Value.Value, kv.Value.ToDictionary());
                        spawns.Add(s.Get <LocationInit>().Value);
                    }

                    newData.SpawnPoints = spawns.ToArray();
                }
                else
                {
                    newData.SpawnPoints = Array.Empty <CPos>();
                }
            }
            catch (Exception)
            {
                newData.SpawnPoints = Array.Empty <CPos>();
                newData.Status      = MapStatus.Unavailable;
            }

            try
            {
                // Player definitions may change if the map format changes
                if (yaml.TryGetValue("Players", out var playerDefinitions))
                {
                    newData.Players     = new MapPlayers(playerDefinitions.Nodes);
                    newData.PlayerCount = newData.Players.Players.Count(x => x.Value.Playable);
                }
            }
            catch (Exception)
            {
                newData.Status = MapStatus.Unavailable;
            }

            newData.SetCustomRules(modData, this, yaml);

            if (p.Contains("map.png"))
            {
                using (var dataStream = p.GetStream("map.png"))
                    newData.Preview = new Png(dataStream);
            }

            // Assign the new data atomically
            innerData = newData;
        }
Example #22
0
        public void UpdateFromMap(IReadOnlyPackage p, IReadOnlyPackage parent, MapClassification classification, string[] mapCompatibility, MapGridType gridType)
        {
            Dictionary <string, MiniYaml> yaml;

            using (var yamlStream = p.GetStream("map.yaml"))
            {
                if (yamlStream == null)
                {
                    throw new FileNotFoundException("Required file map.yaml not present in this map");
                }

                yaml = new MiniYaml(null, MiniYaml.FromStream(yamlStream, "map.yaml")).ToDictionary();
            }

            Package       = p;
            parentPackage = parent;

            var newData = innerData.Clone();

            newData.GridType = gridType;
            newData.Class    = classification;

            MiniYaml temp;

            if (yaml.TryGetValue("MapFormat", out temp))
            {
                var format = FieldLoader.GetValue <int>("MapFormat", temp.Value);
                if (format != Map.SupportedMapFormat)
                {
                    throw new InvalidDataException("Map format {0} is not supported.".F(format));
                }
            }

            if (yaml.TryGetValue("Title", out temp))
            {
                newData.Title = temp.Value;
            }

            if (yaml.TryGetValue("Categories", out temp))
            {
                newData.Categories = FieldLoader.GetValue <string[]>("Categories", temp.Value);
            }

            if (yaml.TryGetValue("Tileset", out temp))
            {
                newData.TileSet = temp.Value;
            }

            if (yaml.TryGetValue("Author", out temp))
            {
                newData.Author = temp.Value;
            }

            if (yaml.TryGetValue("Bounds", out temp))
            {
                newData.Bounds = FieldLoader.GetValue <Rectangle>("Bounds", temp.Value);
            }

            if (yaml.TryGetValue("Visibility", out temp))
            {
                newData.Visibility = FieldLoader.GetValue <MapVisibility>("Visibility", temp.Value);
            }

            string requiresMod = string.Empty;

            if (yaml.TryGetValue("RequiresMod", out temp))
            {
                requiresMod = temp.Value;
            }

            newData.Status = mapCompatibility == null || mapCompatibility.Contains(requiresMod) ?
                             MapStatus.Available : MapStatus.Unavailable;

            try
            {
                // Actor definitions may change if the map format changes
                MiniYaml actorDefinitions;
                if (yaml.TryGetValue("Actors", out actorDefinitions))
                {
                    var spawns = new List <CPos>();
                    foreach (var kv in actorDefinitions.Nodes.Where(d => d.Value.Value == "mpspawn"))
                    {
                        var s = new ActorReference(kv.Value.Value, kv.Value.ToDictionary());
                        spawns.Add(s.InitDict.Get <LocationInit>().Value(null));
                    }

                    newData.SpawnPoints = spawns.ToArray();
                }
                else
                {
                    newData.SpawnPoints = new CPos[0];
                }
            }
            catch (Exception)
            {
                newData.SpawnPoints = new CPos[0];
                newData.Status      = MapStatus.Unavailable;
            }

            try
            {
                // Player definitions may change if the map format changes
                MiniYaml playerDefinitions;
                if (yaml.TryGetValue("Players", out playerDefinitions))
                {
                    newData.Players     = new MapPlayers(playerDefinitions.Nodes);
                    newData.PlayerCount = newData.Players.Players.Count(x => x.Value.Playable);
                }
            }
            catch (Exception)
            {
                newData.Status = MapStatus.Unavailable;
            }

            newData.SetRulesetGenerator(modData, () =>
            {
                var ruleDefinitions          = LoadRuleSection(yaml, "Rules");
                var weaponDefinitions        = LoadRuleSection(yaml, "Weapons");
                var voiceDefinitions         = LoadRuleSection(yaml, "Voices");
                var musicDefinitions         = LoadRuleSection(yaml, "Music");
                var notificationDefinitions  = LoadRuleSection(yaml, "Notifications");
                var sequenceDefinitions      = LoadRuleSection(yaml, "Sequences");
                var modelSequenceDefinitions = LoadRuleSection(yaml, "ModelSequences");
                var rules = Ruleset.Load(modData, this, TileSet, ruleDefinitions, weaponDefinitions,
                                         voiceDefinitions, notificationDefinitions, musicDefinitions, sequenceDefinitions, modelSequenceDefinitions);
                var flagged = Ruleset.DefinesUnsafeCustomRules(modData, this, ruleDefinitions,
                                                               weaponDefinitions, voiceDefinitions, notificationDefinitions, sequenceDefinitions);
                return(Pair.New(rules, flagged));
            });

            if (p.Contains("map.png"))
            {
                using (var dataStream = p.GetStream("map.png"))
                    newData.Preview = new Bitmap(dataStream);
            }

            // Assign the new data atomically
            innerData = newData;
        }
Example #23
0
        // The standard constructor for most purposes
        public Map(string path)
        {
            Path      = path;
            Container = GlobalFileSystem.OpenPackage(path, null, int.MaxValue);

            AssertExists("map.yaml");
            AssertExists("map.bin");

            var yaml = new MiniYaml(null, MiniYaml.FromStream(Container.GetContent("map.yaml"), path));

            FieldLoader.Load(this, yaml);

            // Support for formats 1-3 dropped 2011-02-11.
            // Use release-20110207 to convert older maps to format 4
            // Use release-20110511 to convert older maps to format 5
            // Use release-20141029 to convert older maps to format 6
            if (MapFormat < 6)
            {
                throw new InvalidDataException("Map format {0} is not supported.\n File: {1}".F(MapFormat, path));
            }

            var nd = yaml.ToDictionary();

            // Format 6 -> 7 combined the Selectable and UseAsShellmap flags into the Class enum
            if (MapFormat < 7)
            {
                MiniYaml useAsShellmap;
                if (nd.TryGetValue("UseAsShellmap", out useAsShellmap) && bool.Parse(useAsShellmap.Value))
                {
                    Visibility = MapVisibility.Shellmap;
                }
                else if (Type == "Mission" || Type == "Campaign")
                {
                    Visibility = MapVisibility.MissionSelector;
                }
            }

            SpawnPoints = Exts.Lazy(() =>
            {
                var spawns = new List <CPos>();
                foreach (var kv in ActorDefinitions.Where(d => d.Value.Value == "mpspawn"))
                {
                    var s = new ActorReference(kv.Value.Value, kv.Value.ToDictionary());

                    spawns.Add(s.InitDict.Get <LocationInit>().Value(null));
                }

                return(spawns.ToArray());
            });

            RuleDefinitions          = MiniYaml.NodesOrEmpty(yaml, "Rules");
            SequenceDefinitions      = MiniYaml.NodesOrEmpty(yaml, "Sequences");
            VoxelSequenceDefinitions = MiniYaml.NodesOrEmpty(yaml, "VoxelSequences");
            WeaponDefinitions        = MiniYaml.NodesOrEmpty(yaml, "Weapons");
            VoiceDefinitions         = MiniYaml.NodesOrEmpty(yaml, "Voices");
            NotificationDefinitions  = MiniYaml.NodesOrEmpty(yaml, "Notifications");
            TranslationDefinitions   = MiniYaml.NodesOrEmpty(yaml, "Translations");
            PlayerDefinitions        = MiniYaml.NodesOrEmpty(yaml, "Players");

            ActorDefinitions  = MiniYaml.NodesOrEmpty(yaml, "Actors");
            SmudgeDefinitions = MiniYaml.NodesOrEmpty(yaml, "Smudges");

            MapTiles     = Exts.Lazy(LoadMapTiles);
            MapResources = Exts.Lazy(LoadResourceTiles);
            MapHeight    = Exts.Lazy(LoadMapHeight);

            TileShape      = Game.ModData.Manifest.TileShape;
            SubCellOffsets = Game.ModData.Manifest.SubCellOffsets;
            LastSubCell    = (SubCell)(SubCellOffsets.Length - 1);
            DefaultSubCell = (SubCell)Game.ModData.Manifest.SubCellDefaultIndex;

            if (Container.Exists("map.png"))
            {
                using (var dataStream = Container.GetContent("map.png"))
                    CustomPreview = new Bitmap(dataStream);
            }

            PostInit();

            // The Uid is calculated from the data on-disk, so
            // format changes must be flushed to disk.
            // TODO: this isn't very nice
            if (MapFormat < 7)
            {
                Save(path);
            }

            Uid = ComputeHash();
        }
Example #24
0
        // Support upgrading format 5 maps to a more
        // recent version by defining upgradeForMod.
        public Map(string path, string upgradeForMod)
        {
            Path      = path;
            Container = GlobalFileSystem.OpenPackage(path, null, int.MaxValue);

            AssertExists("map.yaml");
            AssertExists("map.bin");

            var yaml = new MiniYaml(null, MiniYaml.FromStream(Container.GetContent("map.yaml")));

            FieldLoader.Load(this, yaml);

            // Support for formats 1-3 dropped 2011-02-11.
            // Use release-20110207 to convert older maps to format 4
            // Use release-20110511 to convert older maps to format 5
            if (MapFormat < 5)
            {
                throw new InvalidDataException("Map format {0} is not supported.\n File: {1}".F(MapFormat, path));
            }

            // Format 5 -> 6 enforces the use of RequiresMod
            if (MapFormat == 5)
            {
                if (upgradeForMod == null)
                {
                    throw new InvalidDataException("Map format {0} is not supported, but can be upgraded.\n File: {1}".F(MapFormat, path));
                }

                Console.WriteLine("Upgrading {0} from Format 5 to Format 6", path);

                // TODO: This isn't very nice, but there is no other consistent way
                // of finding the mod early during the engine initialization.
                RequiresMod = upgradeForMod;
            }

            var nd = yaml.ToDictionary();

            // Load players
            foreach (var my in nd["Players"].ToDictionary().Values)
            {
                var player = new PlayerReference(my);
                Players.Add(player.Name, player);
            }

            Actors = Exts.Lazy(() =>
            {
                var ret = new Dictionary <string, ActorReference>();
                foreach (var kv in nd["Actors"].ToDictionary())
                {
                    ret.Add(kv.Key, new ActorReference(kv.Value.Value, kv.Value.ToDictionary()));
                }
                return(ret);
            });

            // Smudges
            Smudges = Exts.Lazy(() =>
            {
                var ret = new List <SmudgeReference>();
                foreach (var name in nd["Smudges"].ToDictionary().Keys)
                {
                    var vals = name.Split(' ');
                    var loc  = vals[1].Split(',');
                    ret.Add(new SmudgeReference(vals[0], new int2(
                                                    Exts.ParseIntegerInvariant(loc[0]),
                                                    Exts.ParseIntegerInvariant(loc[1])),
                                                Exts.ParseIntegerInvariant(vals[2])));
                }

                return(ret);
            });

            RuleDefinitions          = MiniYaml.NodesOrEmpty(yaml, "Rules");
            SequenceDefinitions      = MiniYaml.NodesOrEmpty(yaml, "Sequences");
            VoxelSequenceDefinitions = MiniYaml.NodesOrEmpty(yaml, "VoxelSequences");
            WeaponDefinitions        = MiniYaml.NodesOrEmpty(yaml, "Weapons");
            VoiceDefinitions         = MiniYaml.NodesOrEmpty(yaml, "Voices");
            NotificationDefinitions  = MiniYaml.NodesOrEmpty(yaml, "Notifications");
            TranslationDefinitions   = MiniYaml.NodesOrEmpty(yaml, "Translations");

            MapTiles     = Exts.Lazy(() => LoadMapTiles());
            MapResources = Exts.Lazy(() => LoadResourceTiles());
            TileShape    = Game.modData.Manifest.TileShape;

            // The Uid is calculated from the data on-disk, so
            // format changes must be flushed to disk.
            // TODO: this isn't very nice
            if (MapFormat < 6)
            {
                Save(path);
            }

            Uid = ComputeHash();

            if (Container.Exists("map.png"))
            {
                CustomPreview = new Bitmap(Container.GetContent("map.png"));
            }

            PostInit();
        }
Example #25
0
        /// <summary>
        /// Removes invalid mod registrations:
        /// * LaunchPath no longer exists
        /// * LaunchPath and mod id matches the active mod, but the version is different
        /// * Filename doesn't match internal key
        /// * Fails to parse as a mod registration
        /// </summary>
        internal void ClearInvalidRegistrations(ExternalMod activeMod, ModRegistration registration)
        {
            var sources = new List <string>();

            if (registration.HasFlag(ModRegistration.System))
            {
                sources.Add(Platform.SystemSupportDir);
            }

            if (registration.HasFlag(ModRegistration.User))
            {
                sources.Add(Platform.SupportDir);
            }

            foreach (var source in sources.Distinct())
            {
                var metadataPath = Path.Combine(source, "ModMetadata");
                if (!Directory.Exists(metadataPath))
                {
                    continue;
                }

                foreach (var path in Directory.GetFiles(metadataPath, "*.yaml"))
                {
                    string modKey = null;
                    try
                    {
                        var yaml = MiniYaml.FromStream(File.OpenRead(path), path).First().Value;
                        var m    = FieldLoader.Load <ExternalMod>(yaml);
                        modKey = ExternalMod.MakeKey(m);

                        // Continue to the next entry if this one is valid
                        if (File.Exists(m.LaunchPath) && Path.GetFileNameWithoutExtension(path) == modKey &&
                            !(activeMod != null && m.LaunchPath == activeMod.LaunchPath && m.Id == activeMod.Id && m.Version != activeMod.Version))
                        {
                            continue;
                        }
                    }
                    catch (Exception e)
                    {
                        Log.Write("debug", "Failed to parse mod metadata file '{0}'", path);
                        Log.Write("debug", e.ToString());
                    }

                    // Remove from the ingame mod switcher
                    if (Path.GetFileNameWithoutExtension(path) == modKey)
                    {
                        mods.Remove(modKey);
                    }

                    // Remove stale or corrupted metadata
                    try
                    {
                        File.Delete(path);
                        Log.Write("debug", "Removed invalid mod metadata file '{0}'", path);
                    }
                    catch (Exception e)
                    {
                        Log.Write("debug", "Failed to remove mod metadata file '{0}'", path);
                        Log.Write("debug", e.ToString());
                    }
                }
            }
        }