예제 #1
0
        public MO2Compiler(AbsolutePath sourcePath, AbsolutePath downloadsPath, string mo2Profile, AbsolutePath outputFile)
            : base(21, mo2Profile, sourcePath, downloadsPath, outputFile)
        {
            MO2Profile = mo2Profile;
            MO2Ini     = SourcePath.Combine("ModOrganizer.ini").LoadIniFile();
            var mo2game = (string)MO2Ini.General.gameName;

            CompilingGame = GameRegistry.Games.First(g => g.Value.MO2Name == mo2game).Value;
            GamePath      = CompilingGame.GameLocation();
        }
예제 #2
0
        protected override async Task <bool> _Begin(CancellationToken cancel)
        {
            await Metrics.Send("begin_compiling", MO2Profile ?? "unknown");

            if (cancel.IsCancellationRequested)
            {
                return(false);
            }

            DesiredThreads.OnNext(DiskThreads);
            FileExtractor2.FavorPerfOverRAM = FavorPerfOverRam;

            UpdateTracker.Reset();
            UpdateTracker.NextStep("Gathering information");

            Utils.Log("Loading compiler Settings");
            Settings = await CompilerSettings.Load(MO2ProfileDir);

            Settings.IncludedGames = Settings.IncludedGames.Add(CompilingGame.Game);

            Info("Looking for other profiles");
            var otherProfilesPath = MO2ProfileDir.Combine("otherprofiles.txt");

            SelectedProfiles = new HashSet <string>();
            if (otherProfilesPath.Exists)
            {
                SelectedProfiles = (await otherProfilesPath.ReadAllLinesAsync()).ToHashSet();
            }

            SelectedProfiles.Add(MO2Profile !);

            Info("Using Profiles: " + string.Join(", ", SelectedProfiles.OrderBy(p => p)));

            Utils.Log($"Compiling Game: {CompilingGame.Game}");
            Utils.Log("Games from setting files:");
            foreach (var game in Settings.IncludedGames)
            {
                Utils.Log($"- {game}");
            }

            Utils.Log($"VFS File Location: {VFSCacheName}");
            Utils.Log($"MO2 Folder: {SourcePath}");
            Utils.Log($"Downloads Folder: {DownloadsPath}");
            Utils.Log($"Game Folder: {GamePath}");

            var watcher = new DiskSpaceWatcher(cancel,
                                               new[] { SourcePath, DownloadsPath, GamePath, AbsolutePath.EntryPoint }, (long)2 << 31,
                                               drive =>
                    {
                    Utils.Log($"Aborting due to low space on {drive.Name}");
                    Abort();
                });
            var watcherTask = watcher.Start();

            if (cancel.IsCancellationRequested)
            {
                return(false);
            }

            List <AbsolutePath> roots;

            if (UseGamePaths)
            {
                roots = new List <AbsolutePath> {
                    SourcePath, GamePath, DownloadsPath
                };
                roots.AddRange(Settings.IncludedGames.Select(g => g.MetaData().GameLocation()));
            }
            else
            {
                roots = new List <AbsolutePath> {
                    SourcePath, DownloadsPath
                };
            }

            // TODO: make this generic so we can add more paths

            var lootPath = (AbsolutePath)Path.Combine(
                Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData),
                "LOOT");
            IEnumerable <RawSourceFile> lootFiles = new List <RawSourceFile>();

            if (lootPath.Exists)
            {
                roots.Add(lootPath);
            }

            UpdateTracker.NextStep("Indexing folders");

            if (cancel.IsCancellationRequested)
            {
                return(false);
            }

            await VFS.AddRoots(roots);

            if (lootPath.Exists)
            {
                if (CompilingGame.MO2Name == null)
                {
                    throw new ArgumentException("Compiling game had no MO2 name specified.");
                }

                var lootGameDirs = new[]
                {
                    CompilingGame.MO2Name,                 // most of the games use the MO2 name
                    CompilingGame.MO2Name.Replace(" ", "") //eg: Fallout 4 -> Fallout4
                };

                var lootGameDir = lootGameDirs.Select(x => lootPath.Combine(x))
                                  .FirstOrDefault(p => p.IsDirectory);

                if (lootGameDir != default)
                {
                    Utils.Log($"Found LOOT game folder at {lootGameDir}");
                    lootFiles = lootGameDir.EnumerateFiles(false)
                                .Where(p => p.FileName == (RelativePath)"userlist.yaml")
                                .Where(p => p.IsFile)
                                .Select(p => new RawSourceFile(VFS.Index.ByRootPath[p],
                                                               Consts.LOOTFolderFilesDir.Combine(p.RelativeTo(lootPath))));

                    if (!lootFiles.Any())
                    {
                        Utils.Log(
                            $"Found no LOOT user data for {CompilingGame.HumanFriendlyGameName} at {lootGameDir}!");
                    }
                }
            }

            if (cancel.IsCancellationRequested)
            {
                return(false);
            }

            UpdateTracker.NextStep("Cleaning output folder");
            await ModListOutputFolder.DeleteDirectory();

            if (cancel.IsCancellationRequested)
            {
                return(false);
            }

            UpdateTracker.NextStep("Inferring metas for game file downloads");
            await InferMetas();

            if (cancel.IsCancellationRequested)
            {
                return(false);
            }

            UpdateTracker.NextStep("Reindexing downloads after meta inferring");
            await VFS.AddRoot(DownloadsPath);

            if (cancel.IsCancellationRequested)
            {
                return(false);
            }

            UpdateTracker.NextStep("Pre-validating Archives");


            // Find all Downloads
            IndexedArchives = (await DownloadsPath.EnumerateFiles()
                               .Where(f => f.WithExtension(Consts.MetaFileExtension).Exists)
                               .PMap(Queue, UpdateTracker,
                                     async f => new IndexedArchive(VFS.Index.ByRootPath[f])
            {
                Name = (string)f.FileName,
                IniData = f.WithExtension(Consts.MetaFileExtension).LoadIniFile(),
                Meta = await f.WithExtension(Consts.MetaFileExtension).ReadAllTextAsync()
            })).ToList();


            await IndexGameFileHashes();

            IndexedArchives = IndexedArchives.DistinctBy(a => a.File.AbsoluteName).ToList();

            await CleanInvalidArchivesAndFillState();

            UpdateTracker.NextStep("Finding Install Files");
            ModListOutputFolder.CreateDirectory();

            var mo2Files = SourcePath.EnumerateFiles()
                           .Where(p => p.IsFile)
                           .Select(p =>
            {
                if (!VFS.Index.ByRootPath.ContainsKey(p))
                {
                    Utils.Log($"WELL THERE'S YOUR PROBLEM: {p} {VFS.Index.ByRootPath.Count}");
                }

                return(new RawSourceFile(VFS.Index.ByRootPath[p], p.RelativeTo(SourcePath)));
            });

            // If Game Folder Files exists, ignore the game folder
            IndexedFiles = IndexedArchives.SelectMany(f => f.File.ThisAndAllChildren)
                           .OrderBy(f => f.NestingFactor)
                           .GroupBy(f => f.Hash)
                           .ToDictionary(f => f.Key, f => f.AsEnumerable());

            AllFiles.SetTo(mo2Files
                           .Concat(lootFiles)
                           .DistinctBy(f => f.Path));

            Info($"Found {AllFiles.Count} files to build into mod list");

            if (cancel.IsCancellationRequested)
            {
                return(false);
            }

            UpdateTracker.NextStep("Verifying destinations");

            var dups = AllFiles.GroupBy(f => f.Path)
                       .Where(fs => fs.Count() > 1)
                       .Select(fs =>
            {
                Utils.Log(
                    $"Duplicate files installed to {fs.Key} from : {String.Join(", ", fs.Select(f => f.AbsolutePath))}");
                return(fs);
            }).ToList();

            if (dups.Count > 0)
            {
                Error($"Found {dups.Count} duplicates, exiting");
            }

            if (cancel.IsCancellationRequested)
            {
                return(false);
            }

            UpdateTracker.NextStep("Loading INIs");

            ModInis.SetTo(SourcePath.Combine(Consts.MO2ModFolderName)
                          .EnumerateDirectories()
                          .Select(f =>
            {
                var modName  = f.FileName;
                var metaPath = f.Combine("meta.ini");
                return(metaPath.Exists ? (mod_name: f, metaPath.LoadIniFile()) : default);
    protected void Construct(string name, bool isExe)
    {
        FileName = name + (isExe ? ".exe" : ".dll");

        Framework.Add(c => ShouldTargetTinyCorlib(c, this), Bee.DotNet.Framework.FrameworkNone);
        References.Add(c => ShouldTargetTinyCorlib(c, this), Il2Cpp.TinyCorlib);

        Framework.Add(c => !ShouldTargetTinyCorlib(c, this), Bee.DotNet.Framework.Framework471);
        References.Add(c => !ShouldTargetTinyCorlib(c, this), new SystemReference("System"));

        ProjectFile.Path = new NPath(FileName).ChangeExtension(".csproj");

        ProjectFile.ReferenceModeCallback = arg =>
        {
            if (arg == Il2Cpp.TinyCorlib)
            {
                return(ProjectFile.ReferenceMode.ByCSProj);
            }

            //most projects are AsmDefBasedDotsRuntimeCSharpProgram. The remained are things like ZeroJobs. For them we'll look up their packagestatus by the fact that we know
            //it's in the same package as Unity.Entities.CPlusPlus
            var asmdefDotsProgram = (arg as AsmDefBasedDotsRuntimeCSharpProgram)?.AsmDefDescription ?? BuildProgramConfigFile.AsmDefDescriptionFor("Unity.Entities.CPlusPlus");

            switch (asmdefDotsProgram.PackageSource)
            {
            case "NoPackage":
            case "Embedded":
            case "Local":
                return(ProjectFile.ReferenceMode.ByCSProj);

            default:
                return(ProjectFile.ReferenceMode.ByDotNetAssembly);
            }
        };

        LanguageVersion = "7.3";
        Defines.Add(
            "UNITY_2018_3_OR_NEWER",
            "UNITY_DOTSPLAYER",
            "UNITY_ZEROPLAYER", //<-- this was used for a while, let's keep it around to not break people's incoming PR's.
            "NET_TINY",
            "NET_DOTS",
            "UNITY_USE_TINYMATH",
            "UNITY_BINDGEM"
            );

        Defines.Add(c => (c as DotsRuntimeCSharpProgramConfiguration)?.Platform is WebGLPlatform, "UNITY_WEBGL");
        Defines.Add(c => (c as DotsRuntimeCSharpProgramConfiguration)?.Platform is WindowsPlatform, "UNITY_WINDOWS");
        Defines.Add(c => (c as DotsRuntimeCSharpProgramConfiguration)?.Platform is MacOSXPlatform, "UNITY_MACOSX");
        Defines.Add(c => (c as DotsRuntimeCSharpProgramConfiguration)?.Platform is LinuxPlatform, "UNITY_LINUX");
        Defines.Add(c => (c as DotsRuntimeCSharpProgramConfiguration)?.Platform is IosPlatform, "UNITY_IOS");
        Defines.Add(c => (c as DotsRuntimeCSharpProgramConfiguration)?.Platform is AndroidPlatform, "UNITY_ANDROID");

        CopyReferencesNextToTarget = false;

        WarningsAsErrors = false;
        //hack, fix this in unity.mathematics
        if (SourcePath.FileName == "Unity.Mathematics")
        {
            Sources.Add(SourcePath.Files("*.cs", true).Where(f => f.FileName != "math_unity_conversion.cs" && f.FileName != "PropertyAttributes.cs"));
        }
        else
        {
            var csFilesForDirectory = CSFilesForDirectory(SourcePath).ToList();
            if (csFilesForDirectory.Count == 0)
            {
                csFilesForDirectory.Add(BuildProgram.BeeRoot.Combine("CSharpSupport/PlaceHolderForEmptyProject.cs"));
            }
            Sources.Add(csFilesForDirectory);
        }

        var cppFolder     = SourcePath.Combine("cpp~");
        var prejsFolder   = SourcePath.Combine("prejs~");
        var jsFolder      = SourcePath.Combine("js~");
        var postjsFolder  = SourcePath.Combine("postjs~");
        var beeFolder     = SourcePath.Combine("bee~");
        var includeFolder = cppFolder.Combine("include");

        NPath[] cppFiles = Array.Empty <NPath>();
        if (cppFolder.DirectoryExists())
        {
            cppFiles = cppFolder.Files("*.c*", true);
            ProjectFile.AdditionalFiles.AddRange(cppFolder.Files(true));
            GetOrMakeNativeProgram().Sources.Add(cppFiles);
        }

        if (prejsFolder.DirectoryExists())
        {
            var jsFiles = prejsFolder.Files("*.js", true);
            ProjectFile.AdditionalFiles.AddRange(prejsFolder.Files(true));
            GetOrMakeNativeProgram().Libraries.Add(jsFiles.Select(jsFile => new PreJsLibrary(jsFile)));
        }

        //todo: get rid of having both a regular js and a prejs folder
        if (jsFolder.DirectoryExists())
        {
            var jsFiles = jsFolder.Files("*.js", true);
            ProjectFile.AdditionalFiles.AddRange(jsFolder.Files(true));
            GetOrMakeNativeProgram().Libraries.Add(jsFiles.Select(jsFile => new JavascriptLibrary(jsFile)));
        }

        if (postjsFolder.DirectoryExists())
        {
            var jsFiles = postjsFolder.Files("*.js", true);
            ProjectFile.AdditionalFiles.AddRange(postjsFolder.Files(true));
            GetOrMakeNativeProgram().Libraries.Add(jsFiles.Select(jsFile => new PostJsLibrary(jsFile)));
        }

        if (beeFolder.DirectoryExists())
        {
            ProjectFile.AdditionalFiles.AddRange(beeFolder.Files("*.cs"));
        }

        if (includeFolder.DirectoryExists())
        {
            GetOrMakeNativeProgram().PublicIncludeDirectories.Add(includeFolder);
        }

        SupportFiles.Add(SourcePath.Files().Where(f => f.HasExtension("jpg", "png", "wav", "mp3", "jpeg", "mp4", "webm", "ogg")));


        Defines.Add(c => c.CodeGen == CSharpCodeGen.Debug, "DEBUG");

        Defines.Add(c => ((DotsRuntimeCSharpProgramConfiguration)c).EnableUnityCollectionsChecks, "ENABLE_UNITY_COLLECTIONS_CHECKS");

        Defines.Add(
            c => (c as DotsRuntimeCSharpProgramConfiguration)?.ScriptingBackend == ScriptingBackend.TinyIl2cpp,
            "UNITY_DOTSPLAYER_IL2CPP");
        Defines.Add(c => (c as DotsRuntimeCSharpProgramConfiguration)?.ScriptingBackend == ScriptingBackend.Dotnet, "UNITY_DOTSPLAYER_DOTNET");

        ProjectFile.RedirectMSBuildBuildTargetToBee = true;
        ProjectFile.AddCustomLinkRoot(SourcePath, ".");
        ProjectFile.RootNameSpace = "";

        DotsRuntimeCSharpProgramCustomizer.RunAllCustomizersOn(this);
    }