/// <inheritdoc /> public bool UpdateToVersion(int major, int minor) { lock (ByondLock) { if (!BusyCheck()) { updateStat = ByondStatus.Starting; RevisionStaging = new Thread(new ParameterizedThreadStart(UpdateToVersionImpl)) { IsBackground = true //don't slow me down }; RevisionStaging.Start(String.Format("{0}.{1}", major, minor)); return(true); } return(false); } }
/// <summary> /// Attempts to move the staged update from <see cref="StagingDirectory"/> to <see cref="ByondDirectory"/>. Sets <see cref="lastError"/> on failure /// </summary> /// <returns><see langword="true"/> on success, <see langword="false"/> on failure</returns> bool ApplyStagedUpdate() { lock (CompilerLock) { if (compilerCurrentStatus == CompilerStatus.Compiling) { return(false); } lock (ByondLock) { if (updateStat != ByondStatus.Staged) { return(false); } updateStat = ByondStatus.Updating; } try { var rbd = RelativePath(ByondDirectory); Helpers.DeleteDirectory(rbd); Directory.Move(RelativePath(StagingDirectoryInner), rbd); Helpers.DeleteDirectory(RelativePath(StagingDirectory)); lastError = null; SendMessage("BYOND: Update completed!", MessageType.DeveloperInfo); WriteInfo(String.Format("BYOND update {0} completed!", GetVersion(ByondVersion.Installed)), EventID.BYONDUpdateComplete); return(true); } catch (Exception e) { lastError = e.ToString(); SendMessage("BYOND: Update failed!", MessageType.DeveloperInfo); WriteError("BYOND update failed! Error: " + e.ToString(), EventID.BYONDUpdateFail); return(false); } finally { lock (ByondLock) { updateStat = ByondStatus.Idle; } } } }
/// <summary> /// Downloads and unzips a BYOND revision. Calls <see cref="ApplyStagedUpdate"/> afterwards if the <see cref="Instance"/> isn't running, otherwise, calls <see cref="RequestRestart"/>. Sets <see cref="lastError"/> on failure /// </summary> /// <param name="param">Stringified BYOND revision</param> public void UpdateToVersionImpl(object param) { lock (ByondLock) { if (updateStat != ByondStatus.Starting) { return; } updateStat = ByondStatus.Downloading; } bool staged = false; try { CleanByondStaging(); var vi = ((string)param).Split('.'); var major = Convert.ToInt32(vi[0]); var minor = Convert.ToInt32(vi[1]); var rrdp = RelativePath(RevisionDownloadPath); using (var client = new WebClient()) { SendMessage(String.Format("BYOND: Updating to version {0}.{1}...", major, minor), MessageType.DeveloperInfo); //DOWNLOADING try { client.DownloadFile(String.Format(ByondRevisionsURL, major, minor), rrdp); } catch { SendMessage("BYOND: Update download failed. Does the specified version exist?", MessageType.DeveloperInfo); lastError = String.Format("Download of BYOND version {0}.{1} failed! Does it exist?", major, minor); WriteWarning(String.Format("Failed to update BYOND to version {0}.{1}!", major, minor), EventID.BYONDUpdateFail); lock (ByondLock) { updateStat = ByondStatus.Idle; } return; } } lock (ByondLock) { updateStat = ByondStatus.Staging; } //STAGING ZipFile.ExtractToDirectory(rrdp, RelativePath(StagingDirectory)); lock (ByondLock) { File.WriteAllText(RelativePath(StagingDirectoryInner + VersionFile), String.Format("{0}.{1}", major, minor)); //IMPORTANT: SET THE BYOND CONFIG TO NOT PROMPT FOR TRUSTED MODE REEE Directory.CreateDirectory(RelativePath(ByondConfigDir)); File.WriteAllText(RelativePath(ByondDDConfig), ByondNoPromptTrustedMode); } File.Delete(RevisionDownloadPath); lock (ByondLock) { updateStat = ByondStatus.Staged; } switch (DaemonStatus()) { case DreamDaemonStatus.Offline: if (ApplyStagedUpdate()) { lastError = null; } else { lastError = "Failed to apply update!"; } break; default: //Compile() will call RequestRestart() lastError = "Update staged. Awaiting server restart..."; SendMessage(String.Format("BYOND: Staging complete. Awaiting server restart...", major, minor), MessageType.DeveloperInfo); WriteInfo(String.Format("BYOND update {0}.{1} staged", major, minor), EventID.BYONDUpdateStaged); staged = true; break; } } catch (ThreadAbortException) { return; } catch (Exception e) { WriteError("Revision staging errror: " + e.ToString(), EventID.BYONDUpdateFail); lock (ByondLock) { updateStat = ByondStatus.Idle; lastError = e.ToString(); RevisionStaging = null; } } if (staged) { lock (ByondLock) { //Some fuckery to trick the compiler into starting even though we're staged //Won't f**k up the actual compiler thread though updateStat = ByondStatus.Idle; Compile(); updateStat = ByondStatus.Staged; } } }
/// <summary> /// Downloads and unzips a BYOND revision. Calls <see cref="ApplyStagedUpdate"/> afterwards if the <see cref="Instance"/> isn't running, otherwise, calls <see cref="RequestRestart"/>. Sets <see cref="lastError"/> on failure /// </summary> /// <param name="param">Stringified BYOND revision</param> public void UpdateToVersionImpl(object param) { lock (ByondLock) { if (updateStat != ByondStatus.Starting) { return; } updateStat = ByondStatus.Downloading; } bool staged = false; try { CleanByondStaging(); var vi = ((string)param).Split('.'); var major = Convert.ToInt32(vi[0]); var minor = Convert.ToInt32(vi[1]); var rrdp = RelativePath(RevisionDownloadPath); using (var client = new WebClient()) { SendMessage(String.Format("BYOND: Updating to version {0}.{1}...", major, minor), MessageType.DeveloperInfo); //DOWNLOADING try { client.DownloadFile(String.Format(ByondRevisionsURL, major, minor), rrdp); } catch { SendMessage("BYOND: Update download failed. Does the specified version exist?", MessageType.DeveloperInfo); lastError = String.Format("Download of BYOND version {0}.{1} failed! Does it exist?", major, minor); WriteWarning(String.Format("Failed to update BYOND to version {0}.{1}!", major, minor), EventID.BYONDUpdateFail); lock (ByondLock) { updateStat = ByondStatus.Idle; } return; } } lock (ByondLock) { updateStat = ByondStatus.Staging; } //STAGING ZipFile.ExtractToDirectory(rrdp, RelativePath(StagingDirectory)); File.Delete(rrdp); //IMPORTANT: SET THE BYOND CONFIG TO NOT PROMPT FOR TRUSTED MODE REEE Directory.CreateDirectory(RelativePath(ByondConfigDir)); File.WriteAllText(RelativePath(ByondDDConfig), ByondNoPromptTrustedMode); if (major >= 512 && minor >= 1427) { if (Monitor.TryEnter(DirectXInstallLock)) { try { //always install it, it's pretty fast and will do better redundancy checking than us using (var p = new Process()) { p.StartInfo.Arguments = "/silent"; var rbdx = RelativePath(ByondDXDir); p.StartInfo.FileName = rbdx + "/DXSETUP.exe"; p.StartInfo.UseShellExecute = false; p.StartInfo.WorkingDirectory = rbdx; p.Start(); try { p.WaitForExit(); } finally { try { p.Kill(); p.WaitForExit(); } catch (InvalidOperationException) { } } if (p.ExitCode != 0) { throw new Exception("Failed to install included DirectX! Exit code: " + p.ExitCode); } } } finally { Monitor.Exit(DirectXInstallLock); } } } lock (ByondLock) File.WriteAllText(RelativePath(StagingDirectoryInner + VersionFile), String.Format("{0}.{1}", major, minor)); lock (ByondLock) { updateStat = ByondStatus.Staged; } switch (DaemonStatus()) { case DreamDaemonStatus.Offline: if (ApplyStagedUpdate()) { lastError = null; } else { lastError = "Failed to apply update!"; } break; default: //Compile() will call RequestRestart() lastError = "Update staged. Awaiting server restart..."; SendMessage(String.Format("BYOND: Staging complete. Awaiting server restart...", major, minor), MessageType.DeveloperInfo); WriteInfo(String.Format("BYOND update {0}.{1} staged", major, minor), EventID.BYONDUpdateStaged); staged = true; break; } } catch (ThreadAbortException) { return; } catch (Exception e) { WriteError("Revision staging errror: " + e.ToString(), EventID.BYONDUpdateFail); lock (ByondLock) { updateStat = ByondStatus.Idle; lastError = e.ToString(); RevisionStaging = null; } } if (staged) { lock (ByondLock) { //Some fuckery to trick the compiler into starting even though we're staged //Won't f**k up the actual compiler thread though updateStat = ByondStatus.Idle; Compile(); updateStat = ByondStatus.Staged; } } }