From 8acb1ee6dd652637e1d15c9c11b685020b35dfef Mon Sep 17 00:00:00 2001 From: Ray Greenwell Date: Thu, 8 Mar 2012 00:53:17 +0000 Subject: [PATCH] Unified progress bar. Part 1: the unification. Two things actually: - If there are auxiliary groups, the resources from all groups were downloaded all together, but patched individually. So each patcher would restart the progress bar from 0-100%. Created a unified progress observer that merely gives each patching phase an even division of the percentage. - I enumerated the major steps involved in getting down, and assigned a completely arbitrary max progress percentage to each. All percent progress reports are now bounded to the range of the max for the current step and previous step's max reported progress. Backwards progress is ignored, so if getdown goes back and re-does some steps the bar may "stall". If a step is skipped, the progress bar doesn't jump ahead, rather it will use the range of any skipped steps to more fully report the 0-100% progress of the current step. I'm going to try this out and make adjustments. This feature isn't done, part 2 is moving any time estimate up to the "status" label (instead of the "progress" label). --- .../threerings/getdown/launcher/Getdown.java | 112 +++++++++++++++++- 1 file changed, 108 insertions(+), 4 deletions(-) diff --git a/src/main/java/com/threerings/getdown/launcher/Getdown.java b/src/main/java/com/threerings/getdown/launcher/Getdown.java index ba3e78b..248ab84 100644 --- a/src/main/java/com/threerings/getdown/launcher/Getdown.java +++ b/src/main/java/com/threerings/getdown/launcher/Getdown.java @@ -92,6 +92,31 @@ import static com.threerings.getdown.Log.log; public abstract class Getdown extends Thread implements Application.StatusDisplay, ImageLoader { + /** + * The major steps. + */ + public enum Step + { + START(1), + UPDATE_JAVA(10), + VERIFY_METADATA(15), + DOWNLOAD(50), + PATCH(70), + VERIFY_RESOURCES(80), + REDOWNLOAD_RESOURCES(85), + UNPACK(90), + LAUNCH(99); + + /** What is the final percent value for this step? */ + public final int finalPercent; + + /** Enum constructor. */ + Step (int finalPercent) + { + this.finalPercent = finalPercent; + } + } + public static void main (String[] args) { // legacy support @@ -418,6 +443,7 @@ public abstract class Getdown extends Thread // we'll keep track of all the resources we unpack Set unpacked = new HashSet(); + setStep(Step.START); for (int ii = 0; ii < MAX_LOOPS; ii++) { // if we aren't running in a JVM that meets our version requirements, either // complain or attempt to download and install the appropriate version @@ -425,6 +451,7 @@ public abstract class Getdown extends Thread // download and install the necessary version of java, then loop back again and // reverify everything; if we can't download java; we'll throw an exception log.info("Attempting to update Java VM..."); + setStep(Step.UPDATE_JAVA); _enableTracking = true; // always track JVM downloads try { updateJava(); @@ -435,6 +462,7 @@ public abstract class Getdown extends Thread } // make sure we have the desired version and that the metadata files are valid... + setStep(Step.VERIFY_METADATA); setStatus("m.validating", -1, -1L, false); if (_app.verifyMetadata(this)) { log.info("Application requires update."); @@ -444,6 +472,7 @@ public abstract class Getdown extends Thread } // now verify our resources... + setStep(Step.VERIFY_RESOURCES); setStatus("m.validating", -1, -1L, false); List failures = _app.verifyResources(_progobs, alreadyValid, unpacked); if (failures == null) { @@ -455,7 +484,8 @@ public abstract class Getdown extends Thread File ufile = _app.getLocalPath("unpacked.dat"); if (!ufile.exists()) { log.info("Performing initial unpack."); - setStatus("m.validating", -1, -1L, true); + setStep(Step.UNPACK); + updateStatus("m.validating"); _app.unpackResources(_progobs, unpacked); ufile.createNewFile(); } @@ -485,6 +515,7 @@ public abstract class Getdown extends Thread // redownload any that are corrupt or invalid... log.info(failures.size() + " of " + _app.getAllResources().size() + " rsrcs require update (" + alreadyValid[0] + " assumed valid)."); + setStep(Step.REDOWNLOAD_RESOURCES); download(failures); reportTrackingEvent("app_complete", -1); @@ -648,14 +679,19 @@ public abstract class Getdown extends Thread } // download the patch files... + setStep(Step.DOWNLOAD); download(list); // and apply them... + setStep(Step.PATCH); updateStatus("m.patching"); + + // create a new ProgressObserver that divides the different patching phases + UnifiedProgressObserver uProgObs = new UnifiedProgressObserver(list.size(), _progobs); for (Resource prsrc : list) { try { Patcher patcher = new Patcher(); - patcher.patch(prsrc.getLocal().getParentFile(), prsrc.getLocal(), _progobs); + patcher.patch(prsrc.getLocal().getParentFile(), prsrc.getLocal(), uProgObs); } catch (Exception e) { log.warning("Failed to apply patch", "prsrc", prsrc, e); } @@ -665,6 +701,7 @@ public abstract class Getdown extends Thread log.warning("Failed to delete '" + prsrc + "'."); prsrc.getLocal().deleteOnExit(); } + uProgObs.stepComplete(); } } @@ -742,6 +779,7 @@ public abstract class Getdown extends Thread */ protected void launch () { + setStep(Step.LAUNCH); setStatus("m.launching", 100, -1L, false); try { @@ -929,6 +967,25 @@ public abstract class Getdown extends Thread setStatus(message, 0, -1L, true); } + /** + * Set the current step, which will be used to globalize per-step percentages. + */ + protected void setStep (Step step) + { + if (step.finalPercent < _stepMaxPercent) { + // we've gone backwards. Ignore it. + return; + } + _stepMaxPercent = step.finalPercent; + _stepMinPercent = _lastGlobalPercent; + } + + /** + * Update the status of the current step. + * + * Any percent specified will be "globalized" so that the user sees one unified, always + * increasing, progress bar. + */ protected void setStatus ( final String message, final int percent, final long remaining, boolean createUI) { @@ -936,6 +993,12 @@ public abstract class Getdown extends Thread createInterface(false); } + // maybe update the global percent, never letting it go backwards + if (percent >= 0) { + _lastGlobalPercent = Math.max(_lastGlobalPercent, + _stepMinPercent + (percent * (_stepMaxPercent - _stepMinPercent)) / 100); + } + EventQueue.invokeLater(new Runnable() { public void run () { if (_status == null) { @@ -950,7 +1013,7 @@ public abstract class Getdown extends Thread if (_dead) { _status.setProgress(0, -1L); } else if (percent >= 0) { - _status.setProgress(percent, remaining); + _status.setProgress(_lastGlobalPercent, remaining); } } }); @@ -1083,11 +1146,48 @@ public abstract class Getdown extends Thread /** Used to pass progress on to our user interface. */ protected ProgressObserver _progobs = new ProgressObserver() { - public void progress (final int percent) { + public void progress (int percent) { setStatus(null, percent, -1L, false); } }; + /** A simple combiner of multiple 0->100 progresses into one. */ + protected static class UnifiedProgressObserver + implements ProgressObserver + { + /** + * Constructor. + */ + public UnifiedProgressObserver (int steps, ProgressObserver delegate) + { + _steps = steps; + _delegate = delegate; + } + + /** + * Call prior to moving on to a next step. + */ + public void stepComplete () + { + _step++; + } + + // from ProgressObserver + public void progress (int percent) + { + _delegate.progress(((_step * 100) + percent) / _steps); + } + + /** The total number of steps. */ + protected final int _steps; + + /** Our delegate observer. */ + protected final ProgressObserver _delegate; + + /** The current step. */ + protected int _step; + } + protected Application _app; protected Application.UpdateInterface _ifc = new Application.UpdateInterface(); @@ -1110,6 +1210,10 @@ public abstract class Getdown extends Thread /** Number of minutes to wait after startup before beginning any real heavy lifting. */ protected int _delay; + protected int _stepMaxPercent; + protected int _stepMinPercent; + protected int _lastGlobalPercent; + protected static final int MAX_LOOPS = 5; protected static final long MIN_EXIST_TIME = 5000L; protected static final long FALLBACK_CHECK_TIME = 1000L;