private void AddLinks(ModEntityData <T> data, ModEntityData <Tech> techs)
        {
            foreach (var entity in data.Entities)
            {
                // it is possible that the pre-reqs for a something do not exist
                // in this we do not add them to the populated list, but leave them in the ids list
                var prereqs = new List <Tech>();
                foreach (var prerequisiteId in entity.PrerequisiteIds)
                {
                    if (techs.ContainsEntityInTree(prerequisiteId))
                    {
                        var prereq = techs[prerequisiteId];
                        prereqs.Add(prereq);
                        data.Links.Add(new Link()
                        {
                            From = prereq, To = entity
                        });
                    }
                    else
                    {
                        Log.Logger.Warning("Could not find prerequisite {prerequisite} for {entityType} {entityId} in {filePath}",
                                           prerequisiteId, entity.GetType().Name, entity.Id, entity.FilePath);
                    }
                }

                entity.Prerequisites = prereqs;
            }
        }
Пример #2
0
        public VisData CreateRootNotes(ModEntityData <Tech> techsAndDependencies, string imagesPath)
        {
            var result = new VisData()
            {
                ModGroup = StellarisDirectoryHelper.StellarisCoreRootDirectory
            };
            var techAreas          = Enum.GetValues(typeof(TechArea)).Cast <TechArea>();
            var rootNodes          = new Dictionary <TechArea, VisNode>();
            var rootNodeCategories = new Dictionary <TechArea, HashSet <string> >();

            foreach (var tech in techsAndDependencies.AllEntities)
            {
                rootNodeCategories.ComputeIfAbsent(tech.Area, ignored => new HashSet <string>()).AddRange(tech.Categories);
            }

            foreach (var techArea in techAreas)
            {
                var rootNode = BuildRootNode(techArea, imagesPath);
                rootNode.categories = rootNodeCategories.ComputeIfAbsent(techArea, ignored => new HashSet <string>()).ToArray();
                rootNodes[techArea] = rootNode;
            }

            result.nodes.AddRange(rootNodes.Values);
            return(result);
        }
        public ModEntityData <T> ProcessDirectoryHelper(ModEntityData <T> previous, StellarisDirectoryHelper directoryHelper, ModEntityData <Tech> techs)
        {
            var directoryPath = GetDirectory(directoryHelper);

            if (Directory.Exists(directoryPath))
            {
                var result    = new ModEntityData <T>(directoryHelper, previous);
                var techFiles = DirectoryWalker.FindFilesInDirectoryTree(directoryPath, ParseFileMask, IgnoreFiles);
                Log.Logger.Debug("Directory {directory} produced files {files}", directoryPath, techFiles);
                var parsedTechFiles = CWParserHelper.ParseParadoxFiles(techFiles.Select(x => x.FullName).ToList(), true);
                foreach (var(file, cwNode) in parsedTechFiles)
                {
                    Log.Logger.Debug("Processing file {file}", file);
                    // top level nodes are files, so we process the immediate children of each file, which is the individual techs.
                    foreach (var node in cwNode.Nodes)
                    {
                        try {
                            var entity = Construct(node);
                            Initialise(entity, file, directoryHelper.ModName, directoryHelper.ModGroup, node);
                            SetVariables(entity, node);
                            if (ShouldInclude(entity))
                            {
                                result[entity.Id] = entity;
                            }
                            else
                            {
                                Log.Logger.Debug("File {file} contained node {key} was processed, but failed the include filter so is discarded", file, entity.Id);
                            }
                        }
                        catch (Exception e) {
                            if (AbortOnFailure)
                            {
                                throw new Exception($"Error Processing node {node.Key} in file: {file}", e);
                            }
                            Log.Logger.Error(e, "Error Processing node {node} in file: {file}", node.Key, file);
                        }
                    }
                }

                // special case for managing techs
                // techs are their own tech lookup.
                var latestTechData = result as ModEntityData <Tech>;
                if (latestTechData != null)
                {
                    AddLinks(result, latestTechData);
                }
                else
                {
                    AddLinks(result, techs);
                }

                return(result);
            }
            Log.Logger.Debug("{mod} did not have {directory}", directoryHelper.ModName, directoryPath.Replace(directoryHelper.Root, ""));
            return(previous);
        }
Пример #4
0
 public DependantsGraphCreator(ILocalisationApiHelper localisationApiHelper, ICWParserHelper cwParserHelper,
                               StellarisDirectoryHelper stellarisDirectoryHelper, IEnumerable <StellarisDirectoryHelper> modDirectoryHelpers,
                               ModEntityData <Tech> techsAndDependencies)
 {
     this.localisationApiHelper    = localisationApiHelper;
     this.cwParserHelper           = cwParserHelper;
     this.stellarisDirectoryHelper = stellarisDirectoryHelper;
     this.modDirectoryHelpers      = modDirectoryHelpers;
     this.techsAndDependencies     = techsAndDependencies;
 }
Пример #5
0
        private ModEntityData <T> CreateDependant <T>(EntityCreator <T> creator, ParseTarget parseTarget) where T : Entity
        {
            ModEntityData <T> entities = null;

            foreach (var modDirectoryHelper in StellarisDirectoryHelper.CreateCombinedList(stellarisDirectoryHelper, modDirectoryHelpers))
            {
                entities = creator.ProcessDirectoryHelper(entities, modDirectoryHelper, techsAndDependencies);
            }

            return(entities);
        }
Пример #6
0
        public ModEntityData <Tech> CreateTechnologyGraph()
        {
            ModEntityData <Tech> techs = null;

            foreach (var modDirectoryHelper in StellarisDirectoryHelper.CreateCombinedList(stellarisDirectoryHelper, modDirectoryHelpers))
            {
                techs = ProcessDirectoryHelper(techs, modDirectoryHelper, null);
            }

            // post process because while most things work on the principle of:
            // later mod override core
            // later mod override core
            // core
            // some have the core first, then additional features that depend on it (Zenith, I am looking at you)
            // so need to post process

            techs?.ApplyToChain((modTechs, modLinks) => {
                foreach (var(key, tech) in modTechs)
                {
                    if (tech.Prerequisites.Count == tech.PrerequisiteIds.Count())
                    {
                        continue;
                    }

                    Log.Logger.Debug("Tech {id} had missing pre-requisite, trying to find it in the complete listing", key);
                    var populatedPreReqs = tech.Prerequisites.Select(preReq => preReq.Id).ToHashSet();
                    foreach (var missingPreReq in tech.PrerequisiteIds.Where(x => !populatedPreReqs.Contains(x)))
                    {
                        if (techs.ContainsEntityInTree(missingPreReq))
                        {
                            Tech attemptToFindPreq = techs[missingPreReq];
                            tech.Prerequisites.Add(attemptToFindPreq);
                            modLinks.Add(new Link()
                            {
                                From = attemptToFindPreq, To = tech
                            });
                            Log.Logger.Debug("Found prereq {key} in file {file}", attemptToFindPreq.Id, attemptToFindPreq.FilePath);
                        }
                        else
                        {
                            Log.Logger.Debug("Still unable to find {prereqId} for Tech {id}", missingPreReq, key);
                        }
                    }
                }
            });

            return(techs);
        }
Пример #7
0
        private ModEntityData <T> ProcessDependant <T>(EntityCreator <T> creator, ParseTarget parseTarget) where T : Entity
        {
            ModEntityData <T> entities = CreateDependant(creator, parseTarget);

            entities?.ApplyToChain((ents, links) => {
                var invalidEntities = ents.Where(x => !x.Value.Prerequisites.Any()).Select(x => x.Value).ToList();
                foreach (var invalidEntity in invalidEntities)
                {
                    Log.Logger.Warning("Removing {entityId} from {file} dependant entities as we were unable to locate its specified pre-requisite techs", invalidEntity.Id,
                                       invalidEntity.FilePath);
                    ents.Remove(invalidEntity.Id);
                    var invalidLinks = links.Where(x => x.To.Id == invalidEntity.Id).ToList();
                    links.RemoveAll(invalidLinks);
                }
            });

            if (entities != null)
            {
                foreach (var entity in entities.AllEntities)
                {
                    foreach (var prerequisite in entity.Prerequisites)
                    {
                        if (prerequisite.ExtraDesc != null)
                        {
                            entity.ExtraDesc = prerequisite.ExtraDesc;
                        }
                    }
                }

                Log.Logger.Debug("Processed {entityCount} {parseTarget} with {linkCount} links", entities.EntityCount, parseTarget, entities.LinkCount);
            }
            else
            {
                Log.Logger.Warning("{parseTarget} had no items in any of the sources");
            }

            return(entities);
        }
        public void Parse(IEnumerable <ParseTarget> parseTargets)
        {
            var techTreeGraphCreator      = new TechTreeGraphCreator(Localisation, CWParser, StellarisDirectoryHelper, ModDirectoryHelpers);
            ModEntityData <Tech> techData = techTreeGraphCreator.CreateTechnologyGraph();

            Log.Logger.Debug("Processed {entityCount} techs with {linkCount} Links", techData.EntityCount, techData.LinkCount);
            // process technolgoies first
            var techImageOutputDir = OutputDirectoryHelper.GetImagesPath(ParseTarget.Technologies.ImagesDirectory());

            if (CopyImages)
            {
                CopyMainImages(techData.AllEntities, ParseTarget.Technologies.ImagesDirectory(),
                               techImageOutputDir);

                // because the image copying only get the most recent version of the entity, make sure that the image flag is set on all
                // relevant for the vanilla graph display
                var currentTechs = techData.AllEntitiesByKey;
                techData.ApplyToChain((techs, links) =>
                {
                    foreach (var tech in techs.Values)
                    {
                        tech.IconFound = currentTechs[tech.Id].IconFound;
                    }
                });

                var techAreas = Enum.GetValues(typeof(TechArea)).Cast <TechArea>();
                var areaDir   = Path.Combine(techImageOutputDir, "areas");

                // tech areas
                Directory.CreateDirectory(areaDir);
                foreach (var techArea in techAreas)
                {
                    var inputPath = Path.Combine(StellarisDirectoryHelper.Icons, "resources", techArea.ToString().ToLowerInvariant() + "_research.dds");
                    // icon to be displayed on the 3 root nodes
                    ImageOutput.TransformAndOutputImage(
                        inputPath,
                        Path.Combine(techImageOutputDir, techArea + "-root.png"));

                    // area icon
                    ImageOutput.TransformAndOutputImage(
                        inputPath,
                        Path.Combine(areaDir, techArea.ToString().ToLowerInvariant() + ".png"));
                }

                // tech categories
                CopyCategoryImages(techImageOutputDir);
            }

            // process dependant objects
            ObjectsDependantOnTechs dependants = null;
            var parseTargetsWithoutTechs       = parseTargets.WithoutTechs().ToList();

            if (parseTargetsWithoutTechs.Any())
            {
                var dependantsGraphCreator = new DependantsGraphCreator(Localisation, CWParser, StellarisDirectoryHelper, ModDirectoryHelpers, techData);
                dependants = dependantsGraphCreator.CreateDependantGraph(parseTargetsWithoutTechs);

                if (CopyImages)
                {
                    foreach (var parseTarget in parseTargetsWithoutTechs)
                    {
                        var imageOutputDir = OutputDirectoryHelper.GetImagesPath(parseTarget.ImagesDirectory());
                        var entityData     = dependants.Get(parseTarget);
                        CopyMainImages(entityData, parseTarget.ImagesDirectory(), imageOutputDir);
                    }
                    dependants.FixImages();
                }
            }

            var visDataMarshaler = new VisDataMarshaler(localisation, OutputDirectoryHelper);
            IDictionary <string, VisData> techVisResults = visDataMarshaler.CreateTechVisData(techData, techImageOutputDir);
            ModEntityData <Tech>          coreGameTech   = techData.FindCoreGameData();

            if (coreGameTech != null)
            {
                IDictionary <string, VisData> techVisData = visDataMarshaler.CreateTechVisData(coreGameTech, techImageOutputDir);
                techVisResults["Stellaris-No-Mods"] = techVisData[StellarisDirectoryHelper.StellarisCoreRootDirectory];
            }
            else
            {
                Log.Logger.Warning("Could not find core game tech files to generate vanilla tree");
            }

            VisData rootNotes = visDataMarshaler.CreateRootNotes(techData, techImageOutputDir);

            techVisResults["Tech-Root-Nodes"] = rootNotes;
            WriteVisData(techVisResults, true);

            if (dependants != null)
            {
                var dependantVisResults = visDataMarshaler.CreateGroupedVisDependantData(techVisResults, dependants, parseTargetsWithoutTechs);
                var coreDependantsOnly  = dependants.CopyOnlyCore();

                // also do a no-mods lookup
                var stellarisNoModsTechVisResult            = techVisResults["Stellaris-No-Mods"];
                IDictionary <string, VisData> visLookupData =
                    stellarisNoModsTechVisResult != null ? new Dictionary <string, VisData>()
                {
                    { "Stellaris-No-Mods", stellarisNoModsTechVisResult }
                } : techVisResults;
                var coreDependantData = visDataMarshaler.CreateGroupedVisDependantData(visLookupData, coreDependantsOnly, parseTargetsWithoutTechs);

                if (coreDependantData.ContainsKey(StellarisDirectoryHelper.StellarisCoreRootDirectory))
                {
                    dependantVisResults["Stellaris-No-Mods"] = coreDependantData[StellarisDirectoryHelper.StellarisCoreRootDirectory];
                }
                else
                {
                    Log.Logger.Warning("Could not find core game dependant files to generate vanilla tree");
                }

                WriteVisData(dependantVisResults, false);
            }
        }
 public VisData CreateRootNotes(ModEntityData <Tech> techsAndDependencies, string imagesPath)
 {
     return(techsVisMarshaler.CreateRootNotes(techsAndDependencies, imagesPath));
 }
 public IDictionary <string, VisData> CreateTechVisData(ModEntityData <Tech> techsAndDependencies, string imagesPath)
 {
     return(techsVisMarshaler.CreateTechVisData(techsAndDependencies, imagesPath));
 }
Пример #11
0
        public IDictionary <string, VisData> CreateTechVisData(ModEntityData <Tech> allTechsAndDependencies, string imagesPath)
        {
            var modGroups = allTechsAndDependencies.AllLinks.Select(x => x.To.ModGroup).Distinct().ToList();

            var coreData = allTechsAndDependencies.FindCoreGameData();

            // link to supernodes
            var results = new Dictionary <string, VisData>();

            foreach (var modGroup in modGroups)
            {
                var techsForGroup = allTechsAndDependencies.FindByModGroup(modGroup).Reverse().ToList();

                var current = coreData;
                foreach (var forGroup in techsForGroup)
                {
                    current = forGroup.Copy(current);
                }

                var techsAndDependencies = current;

                // perform longest path analysis to find out how many levels we want in each tech
                var maxPathPerTier = new Dictionary <int, int>();

                List <Tech> techsWithNoPrereqs = new List <Tech>();
                foreach (var tech in techsAndDependencies.AllEntities)
                {
                    if (!tech.Tier.HasValue)
                    {
                        throw new InvalidOperationException("All Techs must have Tiers to create vis data.  " + tech.Id);
                    }

                    int pathLength = NumberOfPrereqsInSameTier(tech);

                    var currentMaxForTier = maxPathPerTier.ComputeIfAbsent(tech.Tier.Value, ignored => 0);
                    if (pathLength > currentMaxForTier)
                    {
                        maxPathPerTier[tech.Tier.Value] = pathLength;
                    }

                    // need to link to a supernode to make it look good
                    if (!tech.Prerequisites.Any())
                    {
                        techsWithNoPrereqs.Add(tech);
                    }
                }

                Log.Logger.Debug("Max path per tier {@maxPaths} Number of techs with no pre-requiste {noPreqCount}", maxPathPerTier, techsWithNoPrereqs.Count);


                // determine the base levels in the graph that each node will be on.
                var minimumLevelForTier = CalculateMinimumLevelForTier(maxPathPerTier);
                Log.Logger.Debug("Minimum level per tier {@minLevels}", minimumLevelForTier);

                //sort the input by area as it produces a nicer graph.
                var techList = techsAndDependencies.AllEntities.ToList();
                techList.Sort((tech1, tech2) => {
                    var primary = tech1.Area - tech2.Area;
                    return(primary != 0 ? primary : string.Compare(tech1.Id, tech2.Id, StringComparison.Ordinal));
                });

                var result = new VisData()
                {
                    nodes = techList.Where(x => Filter(x, modGroup)).Select(x => MarshalTech(x, minimumLevelForTier, imagesPath)).ToList(),
                    edges = techsAndDependencies.AllLinks.Where(x => Filter(x.To, modGroup)).Select(VisHelpers.MarshalLink).ToList()
                };

                foreach (var tech in techsWithNoPrereqs.Where(x => Filter(x, modGroup)))
                {
                    result.edges.Add(BuildRootLink(tech.Area, tech.Id));
                }

                results[modGroup] = result;
            }

            return(results);
        }