Пример #1
0
            public static void Update(OuiLoggedProgress progress, Entry version = null)
            {
                if (version == null)
                {
                    version = Newest;
                }
                if (version == null)
                {
                    // Exit immediately.
                    progress.Init <OuiModOptions>(Dialog.Clean("updater_title"), new Task(() => { }), 1).Progress = 1;
                    progress.LogLine("No update - cancelling.");
                    return;
                }

                progress.Init <OuiHelper_Shutdown>(Dialog.Clean("updater_title"), new Task(() => _UpdateStart(progress, version)), 0);
            }
Пример #2
0
            public static void Update(OuiLoggedProgress progress, Entry version = null)
            {
                if (!Flags.SupportUpdatingEverest)
                {
                    progress.Init <OuiModOptions>(Dialog.Clean("updater_title"), new Task(() => { }), 1).Progress = 1;
                    progress.LogLine(Dialog.Clean("EVERESTUPDATER_NOTSUPPORTED"));
                    return;
                }

                if (version == null)
                {
                    version = Newest;
                }
                if (version == null)
                {
                    // Exit immediately.
                    progress.Init <OuiModOptions>(Dialog.Clean("updater_title"), new Task(() => { }), 1).Progress = 1;
                    progress.LogLine(Dialog.Clean("EVERESTUPDATER_NOUPDATE"));
                    return;
                }

                progress.Init <OuiHelper_Shutdown>(Dialog.Clean("updater_title"), new Task(() => _UpdateStart(progress, version)), 0);
            }
Пример #3
0
            private static void _UpdateStart(OuiLoggedProgress progress, Entry version)
            {
                // Last line printed on error.
                const string errorHint = "\nPlease create a new issue on GitHub @ https://github.com/EverestAPI/Everest\nor join the #game_modding channel on Discord (invite in the repo).\nMake sure to upload your log.txt";

                // Check if we're on an OS which supports manipulating Celeste.exe while it's used.
                bool canModWhileAlive =
                    Environment.OSVersion.Platform == PlatformID.Unix ||
                    Environment.OSVersion.Platform == PlatformID.MacOSX;

                string zipPath       = Path.Combine(PathGame, "everest-update.zip");
                string extractedPath = canModWhileAlive ? PathGame : Path.Combine(PathGame, "everest-update");

                progress.LogLine($"Updating to {version.Name} (branch: {version.Branch}) @ {version.URL}");

                progress.LogLine($"Downloading");
                DateTime timeStart = DateTime.Now;

                try {
                    if (File.Exists(zipPath))
                    {
                        File.Delete(zipPath);
                    }

                    // Manual buffered copy from web input to file output.
                    // Allows us to measure speed and progress.
                    using (WebClient wc = new WebClient())
                        using (Stream input = wc.OpenRead(version.URL))
                            using (FileStream output = File.OpenWrite(zipPath))  {
                                long length;
                                if (input.CanSeek)
                                {
                                    length = input.Length;
                                }
                                else
                                {
                                    length = _ContentLength(version.URL);
                                }
                                progress.Progress    = 0;
                                progress.ProgressMax = (int)length;

                                byte[]   buffer        = new byte[4096];
                                DateTime timeLastSpeed = timeStart;
                                int      read;
                                int      readForSpeed = 0;
                                int      pos          = 0;
                                int      speed        = 0;
                                TimeSpan td;
                                while (pos < length)
                                {
                                    read = input.Read(buffer, 0, (int)Math.Min(buffer.Length, length - pos));
                                    output.Write(buffer, 0, read);
                                    pos          += read;
                                    readForSpeed += read;

                                    td = DateTime.Now - timeLastSpeed;
                                    if (td.TotalMilliseconds > 100)
                                    {
                                        speed         = (int)((readForSpeed / 1024D) / td.TotalSeconds);
                                        readForSpeed  = 0;
                                        timeLastSpeed = DateTime.Now;
                                    }

                                    progress.Lines[progress.Lines.Count - 1] =
                                        $"Downloading: {((int) Math.Floor(100D * (pos / (double) length)))}% @ {speed} KiB/s";
                                    progress.Progress = pos;
                                }
                            }
                } catch (Exception e) {
                    progress.LogLine("Download failed!");
                    progress.LogLine(e.ToString());
                    progress.LogLine(errorHint);
                    progress.Progress    = 0;
                    progress.ProgressMax = 1;
                    return;
                }
                progress.LogLine("Download finished.");

                progress.LogLine("Extracting update .zip");
                try {
                    if (extractedPath != PathGame && Directory.Exists(extractedPath))
                    {
                        Directory.Delete(extractedPath, true);
                    }

                    // Don't use zip.ExtractAll because we want to keep track of the progress.
                    using (ZipFile zip = new ZipFile(zipPath)) {
                        progress.LogLine($"{zip.Entries.Count} entries");
                        progress.Progress    = 0;
                        progress.ProgressMax = zip.Entries.Count;

                        foreach (ZipEntry entry in zip.Entries)
                        {
                            if (entry.FileName.Replace('\\', '/').EndsWith("/"))
                            {
                                progress.Progress++;
                                continue;
                            }

                            string fullPath = Path.Combine(extractedPath, entry.FileName);
                            string fullDir  = Path.GetDirectoryName(fullPath);
                            if (!Directory.Exists(fullDir))
                            {
                                Directory.CreateDirectory(fullDir);
                            }
                            if (File.Exists(fullPath))
                            {
                                File.Delete(fullPath);
                            }
                            progress.LogLine($"{entry.FileName} -> {fullPath}");
                            entry.Extract(extractedPath); // Confusingly enough, this takes the base directory.
                            progress.Progress++;
                        }
                    }
                } catch (Exception e) {
                    progress.LogLine("Extraction failed!");
                    progress.LogLine(e.ToString());
                    progress.LogLine(errorHint);
                    progress.Progress    = 0;
                    progress.ProgressMax = 1;
                    return;
                }
                progress.LogLine("Extraction finished.");

                // Load MiniInstaller and run it in the current app domain on systems supporting this.
                Assembly installerAssembly = null;
                Type     installerType     = null;

                if (canModWhileAlive)
                {
                    progress.LogLine("Starting MiniInstaller");
                    progress.Progress    = 0;
                    progress.ProgressMax = 0;
                    Directory.SetCurrentDirectory(PathGame);

                    try {
                        installerAssembly = Assembly.LoadFrom(Path.Combine(extractedPath, "MiniInstaller.exe"));
                        installerType     = installerAssembly.GetType("MiniInstaller.Program");

                        // Set up any fields which we can set up by Everest.
                        FieldInfo f_AsmMonoMod = installerType.GetField("AsmMonoMod");
                        if (f_AsmMonoMod != null)
                        {
                            f_AsmMonoMod.SetValue(null, typeof(MonoModder).Assembly);
                        }
                        FieldInfo f_LogLine = installerType.GetField("LogLine");
                        if (f_LogLine != null)
                        {
                            f_LogLine.SetValue(null, new Action <string>(_ => progress.LogLine(_)).CastDelegate(f_LogLine.FieldType));
                        }

                        // Let's just run the mod installer... from our mod... while we're running the mod...
                        object exitObject = installerAssembly.EntryPoint.Invoke(null, new object[] { new string[] { } });
                        if (exitObject != null && exitObject is int && ((int)exitObject) != 0)
                        {
                            throw new Exception($"Return code != 0, but {exitObject}");
                        }
                    } catch (Exception e) {
                        progress.LogLine("Installer failed!");
                        progress.LogLine(e.ToString());
                        progress.LogLine(errorHint);
                        progress.Progress    = 0;
                        progress.ProgressMax = 1;
                        return;
                    }
                }

                progress.Progress    = 1;
                progress.ProgressMax = 1;
                progress.LogLine("Restarting");
                for (int i = 5; i > 0; --i)
                {
                    progress.Lines[progress.Lines.Count - 1] = $"Restarting in {i}";
                    Thread.Sleep(1000);
                }
                progress.Lines[progress.Lines.Count - 1] = $"Restarting";

                // Start MiniInstaller in a separate process on systems that don't support modding the game while it'S alive.
                if (!canModWhileAlive)
                {
                    try {
                        // We're on Windows or another OS which doesn't support manipulating Celeste.exe while it's used.
                        // Run MiniInstaller "out of body."
                        Process installer = new Process();
                        if (Type.GetType("Mono.Runtime") != null)
                        {
                            installer.StartInfo.FileName  = "mono";
                            installer.StartInfo.Arguments = "\"" + Path.Combine(extractedPath, "MiniInstaller.exe") + "\"";
                        }
                        else
                        {
                            installer.StartInfo.FileName = Path.Combine(extractedPath, "MiniInstaller.exe");
                        }
                        installer.StartInfo.WorkingDirectory = extractedPath;
                        installer.Start();
                    } catch (Exception e) {
                        progress.LogLine("Starting installer failed!");
                        progress.LogLine(e.ToString());
                        progress.LogLine(errorHint);
                        progress.Progress    = 0;
                        progress.ProgressMax = 1;
                    }
                }
                else
                {
                    // On Linux / macOS,
                    Events.Celeste.OnShutdown += () => {
                        // if the installer ships with an exposed StartGame method, run it.
                        MethodInfo m_StartGame = installerType.GetMethod("StartGame");
                        if (m_StartGame != null)
                        {
                            m_StartGame.Invoke(null, new object[0]);
                        }
                        else
                        {
                            // Otherwise run our own restart code on shutdown.
                            Process game = new Process();
                            // If the game was installed via Steam, it should restart in a Steam context on its own.
                            if (Environment.OSVersion.Platform == PlatformID.Unix ||
                                Environment.OSVersion.Platform == PlatformID.MacOSX)
                            {
                                // The Linux and macOS versions come with a wrapping bash script.
                                game.StartInfo.FileName  = "bash";
                                game.StartInfo.Arguments = "\"" + Path.Combine(PathGame, "Celeste") + "\"";
                            }
                            else
                            {
                                game.StartInfo.FileName = Path.Combine(PathGame, "Celeste.exe");
                            }
                            game.StartInfo.WorkingDirectory = PathGame;
                            game.Start();
                        }
                    };
                }
            }
Пример #4
0
            private static void _UpdateStart(OuiLoggedProgress progress, Entry version)
            {
                // Last line printed on error.
                const string errorHint = "\nPlease create a new issue on GitHub @ https://github.com/EverestAPI/Everest\nor join the #game_modding channel on Discord (invite in the repo).\nMake sure to upload your log.txt";

                string zipPath       = Path.Combine(PathGame, "everest-update.zip");
                string extractedPath = Path.Combine(PathGame, "everest-update");

                progress.LogLine($"Updating to {version.Name} (branch: {version.Branch}) @ {version.URL}");

                progress.LogLine($"Downloading");
                try {
                    DownloadFileWithProgress(version.URL, zipPath, (position, length, speed) => {
                        if (length > 0)
                        {
                            progress.Lines[progress.Lines.Count - 1] =
                                $"Downloading: {((int)Math.Floor(100D * (position / (double)length)))}% @ {speed} KiB/s";
                            progress.Progress = position;
                        }
                        else
                        {
                            progress.Lines[progress.Lines.Count - 1] =
                                $"Downloading: {((int)Math.Floor(position / 1000D))}KiB @ {speed} KiB/s";
                        }

                        progress.ProgressMax = (int)length;
                    });
                }
                catch (Exception e) {
                    progress.LogLine("Download failed!");
                    e.LogDetailed();
                    progress.LogLine(errorHint);
                    progress.Progress    = 0;
                    progress.ProgressMax = 1;
                    return;
                }
                progress.LogLine("Download finished.");

                progress.LogLine("Extracting update .zip");
                try {
                    if (extractedPath != PathGame && Directory.Exists(extractedPath))
                    {
                        Directory.Delete(extractedPath, true);
                    }

                    // Don't use zip.ExtractAll because we want to keep track of the progress.
                    using (ZipFile zip = new ZipFile(zipPath)) {
                        progress.LogLine($"{zip.Entries.Count} entries");
                        progress.Progress    = 0;
                        progress.ProgressMax = zip.Entries.Count;

                        foreach (ZipEntry entry in zip.Entries)
                        {
                            if (entry.FileName.Replace('\\', '/').EndsWith("/"))
                            {
                                progress.Progress++;
                                continue;
                            }

                            string entryName = entry.FileName;
                            if (entryName.StartsWith("main/"))
                            {
                                entryName = entryName.Substring(5);
                            }
                            string fullPath = Path.Combine(extractedPath, entryName);
                            string fullDir  = Path.GetDirectoryName(fullPath);
                            if (!Directory.Exists(fullDir))
                            {
                                Directory.CreateDirectory(fullDir);
                            }
                            if (File.Exists(fullPath))
                            {
                                File.Delete(fullPath);
                            }
                            progress.LogLine($"{entry.FileName} -> {fullPath}");
                            using (Stream stream = File.OpenWrite(fullPath))
                                entry.Extract(stream);
                            progress.Progress++;
                        }
                    }
                } catch (Exception e) {
                    progress.LogLine("Extraction failed!");
                    e.LogDetailed();
                    progress.LogLine(errorHint);
                    progress.Progress    = 0;
                    progress.ProgressMax = 1;
                    return;
                }
                progress.LogLine("Extraction finished.");

                progress.Progress    = 1;
                progress.ProgressMax = 1;
                progress.LogLine("Restarting");
                for (int i = 3; i > 0; --i)
                {
                    progress.Lines[progress.Lines.Count - 1] = $"Restarting in {i}";
                    Thread.Sleep(1000);
                }
                progress.Lines[progress.Lines.Count - 1] = $"Restarting";

                // Start MiniInstaller in a separate process.
                try {
                    Process installer     = new Process();
                    string  installerPath = Path.Combine(extractedPath, "MiniInstaller.exe");
                    installer.StartInfo.FileName = installerPath;
                    if (Type.GetType("Mono.Runtime") != null)
                    {
                        installer.StartInfo.FileName  = "mono";
                        installer.StartInfo.Arguments = $"\"{installerPath}\"";
                        if (File.Exists("/bin/sh"))
                        {
                            installer.StartInfo.FileName  = "/bin/sh";
                            installer.StartInfo.Arguments = $"-c \"cd '{extractedPath}'; mono MiniInstaller.exe\"";
                        }
                    }
                    installer.StartInfo.WorkingDirectory = extractedPath;
                    installer.Start();
                } catch (Exception e) {
                    progress.LogLine("Starting installer failed!");
                    e.LogDetailed();
                    progress.LogLine(errorHint);
                    progress.Progress    = 0;
                    progress.ProgressMax = 1;
                }
            }
Пример #5
0
            private static void _UpdateStart(OuiLoggedProgress progress, Entry version)
            {
                // Last line printed on error.
                string errorHint = $"\n{Dialog.Clean("EVERESTUPDATER_ERRORHINT1")}\n{Dialog.Clean("EVERESTUPDATER_ERRORHINT2")}\n{Dialog.Clean("EVERESTUPDATER_ERRORHINT3")}";

                string zipPath       = Path.Combine(PathGame, "everest-update.zip");
                string extractedPath = Path.Combine(PathGame, "everest-update");

                progress.LogLine(string.Format(Dialog.Get("EVERESTUPDATER_UPDATING"), version.Name, version.Branch, version.URL));

                progress.LogLine(Dialog.Clean("EVERESTUPDATER_DOWNLOADING"));
                try {
                    DownloadFileWithProgress(version.URL, zipPath, (position, length, speed) => {
                        if (length > 0)
                        {
                            progress.Lines[progress.Lines.Count - 1] =
                                $"{Dialog.Clean("EVERESTUPDATER_DOWNLOADING_PROGRESS")} {((int) Math.Floor(100D * (position / (double) length)))}% @ {speed} KiB/s";
                            progress.Progress = position;
                        }
                        else
                        {
                            progress.Lines[progress.Lines.Count - 1] =
                                $"{Dialog.Clean("EVERESTUPDATER_DOWNLOADING_PROGRESS")} {((int) Math.Floor(position / 1000D))}KiB @ {speed} KiB/s";
                        }

                        progress.ProgressMax = (int)length;
                        return(true); // continue downloading
                    });
                } catch (Exception e) {
                    progress.LogLine(Dialog.Clean("EVERESTUPDATER_DOWNLOADFAILED"));
                    e.LogDetailed();
                    progress.LogLine(errorHint);
                    progress.Progress    = 0;
                    progress.ProgressMax = 1;
                    return;
                }
                progress.LogLine(Dialog.Clean("EVERESTUPDATER_DOWNLOADFINISHED"));

                progress.LogLine(Dialog.Clean("EVERESTUPDATER_EXTRACTING"));
                try {
                    if (extractedPath != PathGame && Directory.Exists(extractedPath))
                    {
                        Directory.Delete(extractedPath, true);
                    }

                    // Don't use zip.ExtractAll because we want to keep track of the progress.
                    using (ZipFile zip = new ZipFile(zipPath)) {
                        progress.LogLine($"{zip.Entries.Count} {Dialog.Clean("EVERESTUPDATER_ZIPENTRIES")}");
                        progress.Progress    = 0;
                        progress.ProgressMax = zip.Entries.Count;

                        foreach (ZipEntry entry in zip.Entries)
                        {
                            if (entry.FileName.Replace('\\', '/').EndsWith("/"))
                            {
                                progress.Progress++;
                                continue;
                            }

                            string entryName = entry.FileName;
                            if (entryName.StartsWith("main/"))
                            {
                                entryName = entryName.Substring(5);
                            }
                            string fullPath = Path.Combine(extractedPath, entryName);
                            string fullDir  = Path.GetDirectoryName(fullPath);
                            if (!Directory.Exists(fullDir))
                            {
                                Directory.CreateDirectory(fullDir);
                            }
                            if (File.Exists(fullPath))
                            {
                                File.Delete(fullPath);
                            }
                            progress.LogLine($"{entry.FileName} -> {fullPath}");
                            using (Stream stream = File.OpenWrite(fullPath))
                                entry.Extract(stream);
                            progress.Progress++;
                        }
                    }
                } catch (Exception e) {
                    progress.LogLine(Dialog.Clean("EVERESTUPDATER_EXTRACTIONFAILED"));
                    e.LogDetailed();
                    progress.LogLine(errorHint);
                    progress.Progress    = 0;
                    progress.ProgressMax = 1;
                    return;
                }
                progress.LogLine(Dialog.Clean("EVERESTUPDATER_EXTRACTIONFINISHED"));

                progress.Progress    = 1;
                progress.ProgressMax = 1;
                string action = Dialog.Clean("EVERESTUPDATER_RESTARTING");

                progress.LogLine(action);
                for (int i = 3; i > 0; --i)
                {
                    progress.Lines[progress.Lines.Count - 1] = string.Format(Dialog.Get("EVERESTUPDATER_RESTARTINGIN"), i);
                    Thread.Sleep(1000);
                }
                progress.Lines[progress.Lines.Count - 1] = action;

                // Start MiniInstaller in a separate process.
                try {
                    Process installer     = new Process();
                    string  installerPath = Path.Combine(extractedPath, "MiniInstaller.exe");
                    installer.StartInfo.FileName = installerPath;
                    if (Type.GetType("Mono.Runtime") != null)
                    {
                        installer.StartInfo.FileName  = "mono";
                        installer.StartInfo.Arguments = $"\"{installerPath}\"";
                        if (File.Exists("/bin/sh"))
                        {
                            string pid = Process.GetCurrentProcess().Id.ToString();
                            installer.StartInfo.FileName = "/bin/sh";
                            string pathToMono = "mono";
                            if (File.Exists("/Library/Frameworks/Mono.framework/Versions/Current/Commands/mono"))
                            {
                                pathToMono = "/Library/Frameworks/Mono.framework/Versions/Current/Commands/mono";
                            }
                            installer.StartInfo.Arguments = $"-c \"kill -0 {pid}; while [ $? = \\\"0\\\" ]; do sleep 1; kill -0 {pid}; done; unset MONO_PATH LD_LIBRARY_PATH LC_ALL MONO_CONFIG; {pathToMono} MiniInstaller.exe\"";
                        }
                    }
                    installer.StartInfo.WorkingDirectory = extractedPath;
                    if (Environment.OSVersion.Platform == PlatformID.Unix)
                    {
                        installer.StartInfo.UseShellExecute = false;
                        installer.Start();
                    }
                    else
                    {
                        installer.Start();
                    }
                } catch (Exception e) {
                    progress.LogLine(Dialog.Clean("EVERESTUPDATER_STARTINGFAILED"));
                    e.LogDetailed();
                    progress.LogLine(errorHint);
                    progress.Progress    = 0;
                    progress.ProgressMax = 1;
                }
            }
Пример #6
0
            private static void _UpdateStart(OuiLoggedProgress progress, Entry version)
            {
                // Last line printed on error.
                const string errorHint = "\nPlease create a new issue on GitHub @ https://github.com/EverestAPI/Everest\nor join the #game_modding channel on Discord (invite in the repo).\nMake sure to upload your log.txt";

                // Check if we're on an OS which supports manipulating Celeste.exe while it's used.
                bool canModWhileAlive =
                    System.Environment.OSVersion.Platform == PlatformID.Unix;

                if (canModWhileAlive)
                {
                    // Check if we can even read-write the file.
                    Exception eLast = null;
                    string    path  = typeof(Celeste).Assembly.Location;
                    for (int i = 2048; i > -1; --i)
                    {
                        try {
                            using (FileStream stream = new FileStream(path, FileMode.Open, FileAccess.ReadWrite))
                                break;
                        } catch (Exception e) {
                            eLast = e;
                        }
                    }
                    if (eLast != null)
                    {
                        progress.LogLine("Note: You're on a platform that should support\nread-writing Celeste while running, but it doesn't.\nCheck your log.txt to find out why.\n");
                        Logger.Log(LogLevel.Warn, "updater", $"Failed read-writing {path} on platform that should support it: " + eLast.ToString());
                        canModWhileAlive = false;
                    }
                }

                string zipPath       = Path.Combine(PathGame, "everest-update.zip");
                string extractedPath = canModWhileAlive ? PathGame : Path.Combine(PathGame, "everest-update");

                progress.LogLine($"Updating to {version.Name} (branch: {version.Branch}) @ {version.URL}");

                progress.LogLine($"Downloading");
                DateTime timeStart = DateTime.Now;

                try {
                    if (File.Exists(zipPath))
                    {
                        File.Delete(zipPath);
                    }

                    // Manual buffered copy from web input to file output.
                    // Allows us to measure speed and progress.
                    using (WebClient wc = new WebClient())
                        using (Stream input = wc.OpenRead(version.URL))
                            using (FileStream output = File.OpenWrite(zipPath))  {
                                long length;
                                if (input.CanSeek)
                                {
                                    length = input.Length;
                                }
                                else
                                {
                                    length = _ContentLength(version.URL);
                                }
                                progress.Progress    = 0;
                                progress.ProgressMax = (int)length;

                                byte[]   buffer        = new byte[4096];
                                DateTime timeLastSpeed = timeStart;
                                int      read;
                                int      readForSpeed = 0;
                                int      pos          = 0;
                                int      speed        = 0;
                                TimeSpan td;
                                while (pos < length)
                                {
                                    read = input.Read(buffer, 0, (int)Math.Min(buffer.Length, length - pos));
                                    output.Write(buffer, 0, read);
                                    pos          += read;
                                    readForSpeed += read;

                                    td = DateTime.Now - timeLastSpeed;
                                    if (td.TotalMilliseconds > 100)
                                    {
                                        speed         = (int)((readForSpeed / 1024D) / td.TotalSeconds);
                                        readForSpeed  = 0;
                                        timeLastSpeed = DateTime.Now;
                                    }

                                    progress.Lines[progress.Lines.Count - 1] =
                                        $"Downloading: {((int) Math.Floor(100D * (pos / (double) length)))}% @ {speed} KiB/s";
                                    progress.Progress = pos;
                                }
                            }
                } catch (Exception e) {
                    progress.LogLine("Download failed!");
                    e.LogDetailed();
                    progress.LogLine(errorHint);
                    progress.Progress    = 0;
                    progress.ProgressMax = 1;
                    return;
                }
                progress.LogLine("Download finished.");

                progress.LogLine("Extracting update .zip");
                try {
                    if (extractedPath != PathGame && Directory.Exists(extractedPath))
                    {
                        Directory.Delete(extractedPath, true);
                    }

                    // Don't use zip.ExtractAll because we want to keep track of the progress.
                    using (ZipFile zip = new ZipFile(zipPath)) {
                        progress.LogLine($"{zip.Entries.Count} entries");
                        progress.Progress    = 0;
                        progress.ProgressMax = zip.Entries.Count;

                        foreach (ZipEntry entry in zip.Entries)
                        {
                            if (entry.FileName.Replace('\\', '/').EndsWith("/"))
                            {
                                progress.Progress++;
                                continue;
                            }

                            string fullPath = Path.Combine(extractedPath, entry.FileName);
                            string fullDir  = Path.GetDirectoryName(fullPath);
                            if (!Directory.Exists(fullDir))
                            {
                                Directory.CreateDirectory(fullDir);
                            }
                            if (File.Exists(fullPath))
                            {
                                File.Delete(fullPath);
                            }
                            progress.LogLine($"{entry.FileName} -> {fullPath}");
                            entry.Extract(extractedPath); // Confusingly enough, this takes the base directory.
                            progress.Progress++;
                        }
                    }
                } catch (Exception e) {
                    progress.LogLine("Extraction failed!");
                    e.LogDetailed();
                    progress.LogLine(errorHint);
                    progress.Progress    = 0;
                    progress.ProgressMax = 1;
                    return;
                }
                progress.LogLine("Extraction finished.");

                // Load MiniInstaller and run it in a new app domain on systems supporting this.
                if (canModWhileAlive)
                {
                    progress.LogLine("Starting MiniInstaller");
                    progress.Progress    = 0;
                    progress.ProgressMax = 0;
                    Directory.SetCurrentDirectory(PathGame);

                    try {
                        AppDomainSetup nestInfo = new AppDomainSetup();
                        nestInfo.ApplicationBase = Path.GetDirectoryName(extractedPath);

                        AppDomain nest = AppDomain.CreateDomain(
                            AppDomain.CurrentDomain.FriendlyName + " - MiniInstaller",
                            AppDomain.CurrentDomain.Evidence,
                            nestInfo,
                            AppDomain.CurrentDomain.PermissionSet
                            );

                        // nest.DoCallBack(Boot);
                        ((MiniInstallerProxy)nest.CreateInstanceFromAndUnwrap(
                             typeof(MiniInstallerProxy).Assembly.Location,
                             typeof(MiniInstallerProxy).FullName
                             )).Boot(new MiniInstallerBridge {
                            Progress      = progress,
                            ExtractedPath = extractedPath
                        });

                        AppDomain.Unload(nest);
                    } catch (Exception e) {
                        progress.LogLine("MiniInstaller failed!");
                        e.LogDetailed();
                        progress.LogLine(errorHint);
                        progress.Progress    = 0;
                        progress.ProgressMax = 1;
                        return;
                    }
                }

                progress.Progress    = 1;
                progress.ProgressMax = 1;
                progress.LogLine("Restarting");
                for (int i = 5; i > 0; --i)
                {
                    progress.Lines[progress.Lines.Count - 1] = $"Restarting in {i}";
                    Thread.Sleep(1000);
                }
                progress.Lines[progress.Lines.Count - 1] = $"Restarting";

                // Start MiniInstaller in a separate process on systems that don't support modding the game while it'S alive.
                if (!canModWhileAlive)
                {
                    try {
                        // We're on Windows or another OS which doesn't support manipulating Celeste.exe while it's used.
                        // Run MiniInstaller "out of body."
                        Process installer = new Process();
                        installer.StartInfo.FileName = Path.Combine(extractedPath, "MiniInstaller.exe");
                        if (Type.GetType("Mono.Runtime") != null)
                        {
                            installer.StartInfo.Arguments = $"\"{installer.StartInfo.FileName}\"";
                            installer.StartInfo.FileName  = "mono";
                            if (File.Exists("/bin/sh"))
                            {
                                installer.StartInfo.Arguments = $"-c {installer.StartInfo.FileName} {installer.StartInfo.Arguments}";
                                installer.StartInfo.FileName  = "/bin/sh";
                            }
                        }
                        installer.StartInfo.WorkingDirectory = extractedPath;
                        installer.Start();
                    } catch (Exception e) {
                        progress.LogLine("Starting installer failed!");
                        e.LogDetailed();
                        progress.LogLine(errorHint);
                        progress.Progress    = 0;
                        progress.ProgressMax = 1;
                    }
                }
                else
                {
                    // On Linux / macOS, restart the game after it shuts down.
                    Events.Celeste.OnShutdown += () => {
                        Process game = new Process();
                        // If the game was installed via Steam, it should restart in a Steam context on its own.
                        if (System.Environment.OSVersion.Platform == PlatformID.Unix ||
                            System.Environment.OSVersion.Platform == PlatformID.MacOSX)
                        {
                            // The Linux and macOS versions come with a wrapping bash script.
                            game.StartInfo.FileName  = "bash";
                            game.StartInfo.Arguments = "\"" + Path.Combine(PathGame, "Celeste") + "\"";
                        }
                        else
                        {
                            game.StartInfo.FileName = Path.Combine(PathGame, "Celeste.exe");
                        }
                        game.StartInfo.WorkingDirectory = PathGame;
                        game.Start();
                    };
                }
            }
Пример #7
0
            private static void _UpdateStart(OuiLoggedProgress progress, Entry version)
            {
                // Last line printed on error.
                const string errorHint = "\nPlease create a new issue on GitHub @ https://github.com/EverestAPI/Everest\nor join the #modding_help channel on Discord (invite in the repo).\nMake sure to upload your log.txt";

                string zipPath       = Path.Combine(PathGame, "everest-update.zip");
                string extractedPath = Path.Combine(PathGame, "everest-update");

                progress.LogLine($"Updating to {version.Name} (branch: {version.Branch}) @ {version.URL}");

                progress.LogLine($"Downloading");
                try {
                    DownloadFileWithProgress(version.URL, zipPath, (position, length, speed) => {
                        if (length > 0)
                        {
                            progress.Lines[progress.Lines.Count - 1] =
                                $"Downloading: {((int) Math.Floor(100D * (position / (double) length)))}% @ {speed} KiB/s";
                            progress.Progress = position;
                        }
                        else
                        {
                            progress.Lines[progress.Lines.Count - 1] =
                                $"Downloading: {((int) Math.Floor(position / 1000D))}KiB @ {speed} KiB/s";
                        }

                        progress.ProgressMax = (int)length;
                        return(true); // continue downloading
                    });
                } catch (Exception e) {
                    progress.LogLine("Download failed!");
                    e.LogDetailed();
                    progress.LogLine(errorHint);
                    progress.Progress    = 0;
                    progress.ProgressMax = 1;
                    return;
                }
                progress.LogLine("Download finished.");

                progress.LogLine("Extracting update .zip");
                try {
                    if (extractedPath != PathGame && Directory.Exists(extractedPath))
                    {
                        Directory.Delete(extractedPath, true);
                    }

                    // Don't use zip.ExtractAll because we want to keep track of the progress.
                    using (ZipFile zip = new ZipFile(zipPath)) {
                        progress.LogLine($"{zip.Entries.Count} entries");
                        progress.Progress    = 0;
                        progress.ProgressMax = zip.Entries.Count;

                        foreach (ZipEntry entry in zip.Entries)
                        {
                            if (entry.FileName.Replace('\\', '/').EndsWith("/"))
                            {
                                progress.Progress++;
                                continue;
                            }

                            string entryName = entry.FileName;
                            if (entryName.StartsWith("main/"))
                            {
                                entryName = entryName.Substring(5);
                            }
                            string fullPath = Path.Combine(extractedPath, entryName);
                            string fullDir  = Path.GetDirectoryName(fullPath);
                            if (!Directory.Exists(fullDir))
                            {
                                Directory.CreateDirectory(fullDir);
                            }
                            if (File.Exists(fullPath))
                            {
                                File.Delete(fullPath);
                            }
                            progress.LogLine($"{entry.FileName} -> {fullPath}");
                            using (Stream stream = File.OpenWrite(fullPath))
                                entry.Extract(stream);
                            progress.Progress++;
                        }
                    }
                } catch (Exception e) {
                    progress.LogLine("Extraction failed!");
                    e.LogDetailed();
                    progress.LogLine(errorHint);
                    progress.Progress    = 0;
                    progress.ProgressMax = 1;
                    return;
                }
                progress.LogLine("Extraction finished.");

                progress.Progress    = 1;
                progress.ProgressMax = 1;
                String action = "Restarting";

                if (Environment.OSVersion.Platform == PlatformID.Unix)
                {
                    action = "Updating";
                }
                progress.LogLine(action);
                for (int i = 3; i > 0; --i)
                {
                    progress.Lines[progress.Lines.Count - 1] = $"{action} in {i}";
                    Thread.Sleep(1000);
                }
                progress.Lines[progress.Lines.Count - 1] = action;

                // Start MiniInstaller in a separate process.
                try {
                    Process installer     = new Process();
                    string  installerPath = Path.Combine(extractedPath, "MiniInstaller.exe");
                    installer.StartInfo.FileName = installerPath;
                    if (Type.GetType("Mono.Runtime") != null)
                    {
                        installer.StartInfo.FileName  = "mono";
                        installer.StartInfo.Arguments = $"\"{installerPath}\"";
                        if (File.Exists("/bin/sh"))
                        {
                            string pid = Process.GetCurrentProcess().Id.ToString();
                            installer.StartInfo.FileName = "/bin/sh";
                            string pathToMono = "mono";
                            if (File.Exists("/Library/Frameworks/Mono.framework/Versions/Current/Commands/mono"))
                            {
                                pathToMono = "/Library/Frameworks/Mono.framework/Versions/Current/Commands/mono";
                            }
                            installer.StartInfo.Arguments = $"-c \"kill -0 {pid}; while [ $? = \\\"0\\\" ]; do sleep 1; kill -0 {pid}; done; unset MONO_PATH LD_LIBRARY_PATH LC_ALL MONO_CONFIG; {pathToMono} MiniInstaller.exe\"";
                        }
                    }
                    installer.StartInfo.WorkingDirectory = extractedPath;
                    if (Environment.OSVersion.Platform == PlatformID.Unix)
                    {
                        installer.StartInfo.UseShellExecute = false;
                        installer.Start();
                        progress.LogLine("Patching the game in-place");
                        progress.LogLine("Restarting");
                    }
                    else
                    {
                        installer.Start();
                    }
                } catch (Exception e) {
                    progress.LogLine("Starting installer failed!");
                    e.LogDetailed();
                    progress.LogLine(errorHint);
                    progress.Progress    = 0;
                    progress.ProgressMax = 1;
                }
            }
Пример #8
0
            private static void _UpdateStart(OuiLoggedProgress progress, Entry version)
            {
                // Last line printed on error.
                const string errorHint = "\nPlease create a new issue on GitHub @ https://github.com/EverestAPI/Everest\nor join the #game_modding channel on Discord (invite in the repo).\nMake sure to upload your log.txt";

                string zipPath       = Path.Combine(PathGame, "everest-update.zip");
                string extractedPath = Path.Combine(PathGame, "everest-update");

                progress.LogLine($"Updating to {version.Name} (branch: {version.Branch}) @ {version.URL}");

                progress.LogLine($"Downloading");
                DateTime timeStart = DateTime.Now;

                try {
                    if (File.Exists(zipPath))
                    {
                        File.Delete(zipPath);
                    }

                    // Manual buffered copy from web input to file output.
                    // Allows us to measure speed and progress.
                    using (WebClient wc = new WebClient())
                        using (Stream input = wc.OpenRead(version.URL))
                            using (FileStream output = File.OpenWrite(zipPath))  {
                                long length;
                                if (input.CanSeek)
                                {
                                    length = input.Length;
                                }
                                else
                                {
                                    length = _ContentLength(version.URL);
                                }
                                progress.Progress    = 0;
                                progress.ProgressMax = (int)length;

                                byte[]   buffer        = new byte[4096];
                                DateTime timeLastSpeed = timeStart;
                                int      read;
                                int      readForSpeed = 0;
                                int      pos          = 0;
                                int      speed        = 0;
                                TimeSpan td;
                                while (pos < length)
                                {
                                    read = input.Read(buffer, 0, (int)Math.Min(buffer.Length, length - pos));
                                    output.Write(buffer, 0, read);
                                    pos          += read;
                                    readForSpeed += read;

                                    td = DateTime.Now - timeLastSpeed;
                                    if (td.TotalMilliseconds > 100)
                                    {
                                        speed         = (int)((readForSpeed / 1024D) / td.TotalSeconds);
                                        readForSpeed  = 0;
                                        timeLastSpeed = DateTime.Now;
                                    }

                                    progress.Lines[progress.Lines.Count - 1] =
                                        $"Downloading: {((int) Math.Floor(100D * (pos / (double) length)))}% @ {speed} KiB/s";
                                    progress.Progress = pos;
                                }
                            }
                } catch (Exception e) {
                    progress.LogLine("Download failed!");
                    e.LogDetailed();
                    progress.LogLine(errorHint);
                    progress.Progress    = 0;
                    progress.ProgressMax = 1;
                    return;
                }
                progress.LogLine("Download finished.");

                progress.LogLine("Extracting update .zip");
                try {
                    if (extractedPath != PathGame && Directory.Exists(extractedPath))
                    {
                        Directory.Delete(extractedPath, true);
                    }

                    // Don't use zip.ExtractAll because we want to keep track of the progress.
                    using (ZipFile zip = new ZipFile(zipPath)) {
                        progress.LogLine($"{zip.Entries.Count} entries");
                        progress.Progress    = 0;
                        progress.ProgressMax = zip.Entries.Count;

                        foreach (ZipEntry entry in zip.Entries)
                        {
                            if (entry.FileName.Replace('\\', '/').EndsWith("/"))
                            {
                                progress.Progress++;
                                continue;
                            }

                            string fullPath = Path.Combine(extractedPath, entry.FileName);
                            string fullDir  = Path.GetDirectoryName(fullPath);
                            if (!Directory.Exists(fullDir))
                            {
                                Directory.CreateDirectory(fullDir);
                            }
                            if (File.Exists(fullPath))
                            {
                                File.Delete(fullPath);
                            }
                            progress.LogLine($"{entry.FileName} -> {fullPath}");
                            entry.Extract(extractedPath); // Confusingly enough, this takes the base directory.
                            progress.Progress++;
                        }
                    }
                } catch (Exception e) {
                    progress.LogLine("Extraction failed!");
                    e.LogDetailed();
                    progress.LogLine(errorHint);
                    progress.Progress    = 0;
                    progress.ProgressMax = 1;
                    return;
                }
                progress.LogLine("Extraction finished.");

                // Load MiniInstaller and run it in a new app domain on systems supporting this.
                progress.Progress    = 1;
                progress.ProgressMax = 1;
                progress.LogLine("Restarting");
                for (int i = 3; i > 0; --i)
                {
                    progress.Lines[progress.Lines.Count - 1] = $"Restarting in {i}";
                    Thread.Sleep(1000);
                }
                progress.Lines[progress.Lines.Count - 1] = $"Restarting";

                // Start MiniInstaller in a separate process.
                try {
                    Process installer     = new Process();
                    string  installerPath = Path.Combine(extractedPath, "MiniInstaller.exe");
                    installer.StartInfo.FileName = installerPath;
                    if (Type.GetType("Mono.Runtime") != null)
                    {
                        installer.StartInfo.FileName  = "mono";
                        installer.StartInfo.Arguments = $"\"{installerPath}\"";
                        if (File.Exists("/bin/sh"))
                        {
                            installer.StartInfo.FileName  = "/bin/sh";
                            installer.StartInfo.Arguments = $"-c \"cd '{extractedPath}'; mono MiniInstaller.exe\"";
                        }
                    }
                    installer.StartInfo.WorkingDirectory = extractedPath;
                    installer.Start();
                } catch (Exception e) {
                    progress.LogLine("Starting installer failed!");
                    e.LogDetailed();
                    progress.LogLine(errorHint);
                    progress.Progress    = 0;
                    progress.ProgressMax = 1;
                }
            }