diff --git a/src/main/java/net/minecraftforge/installer/DownloadUtils.java b/src/main/java/net/minecraftforge/installer/DownloadUtils.java index 7ef8fa2..9b7a67a 100644 --- a/src/main/java/net/minecraftforge/installer/DownloadUtils.java +++ b/src/main/java/net/minecraftforge/installer/DownloadUtils.java @@ -462,7 +462,7 @@ static LocalSource fromResource() { static LocalSource detect() { try { final URL url = DownloadUtils.class.getProtectionDomain().getCodeSource().getLocation(); - if (url.getProtocol().equals("file") && Files.isDirectory(Paths.get(url.getPath()))) { // If we're running local IDE, use the resources dir + if (url.getProtocol().equals("file") && Files.isDirectory(Paths.get(url.toURI()))) { // If we're running local IDE, use the resources dir return walkFromClassesOut(Paths.get(url.toURI())); } diff --git a/src/main/java/net/minecraftforge/installer/InstallerPanel.java b/src/main/java/net/minecraftforge/installer/InstallerPanel.java index 721ae30..cd9bf4d 100644 --- a/src/main/java/net/minecraftforge/installer/InstallerPanel.java +++ b/src/main/java/net/minecraftforge/installer/InstallerPanel.java @@ -22,6 +22,7 @@ import net.minecraftforge.installer.actions.ActionCanceledException; import net.minecraftforge.installer.actions.Actions; import net.minecraftforge.installer.actions.ProgressCallback; +import net.minecraftforge.installer.actions.TargetValidator; import net.minecraftforge.installer.json.InstallV1; import net.minecraftforge.installer.json.OptionalLibrary; @@ -363,7 +364,7 @@ private void updateFilePath() } Action action = actions.get(choiceButtonGroup.getSelection().getActionCommand()).apply(null); - boolean valid = action.isPathValid(targetDir); + TargetValidator.ValidationResult valid = action.getTargetValidator().validate(targetDir); if (profile.getMirror() != null) { @@ -379,21 +380,20 @@ private void updateFilePath() { sponsorPanel.setVisible(false); } - if (valid) - { + + if (valid.valid) { selectedDirText.setForeground(null); infoLabel.setVisible(false); fileEntryPanel.setBorder(null); proceedButton.ifPresent(button -> button.setEnabled(true)); - } - else - { + } else { selectedDirText.setForeground(Color.RED); fileEntryPanel.setBorder(new LineBorder(Color.RED)); - infoLabel.setText(""+action.getFileError(targetDir)+""); + infoLabel.setText(""+valid.message+""); infoLabel.setVisible(true); - proceedButton.ifPresent(button -> button.setEnabled(false)); + proceedButton.ifPresent(button -> button.setEnabled(!valid.critical)); } + if (dialog!=null) { dialog.invalidate(); diff --git a/src/main/java/net/minecraftforge/installer/ProgressFrame.java b/src/main/java/net/minecraftforge/installer/ProgressFrame.java index 93c6259..14f76cb 100644 --- a/src/main/java/net/minecraftforge/installer/ProgressFrame.java +++ b/src/main/java/net/minecraftforge/installer/ProgressFrame.java @@ -152,6 +152,18 @@ public ProgressBar getGlobalProgress() { return globalProgressController; } + private String step; + @Override + public String getCurrentStep() { + return step; + } + + @Override + public void setCurrentStep(String step) { + message(step, MessagePriority.HIGH); + this.step = step; + } + @Override public void message(String message, MessagePriority priority) { diff --git a/src/main/java/net/minecraftforge/installer/SimpleInstaller.java b/src/main/java/net/minecraftforge/installer/SimpleInstaller.java index a995b85..5f43a3f 100644 --- a/src/main/java/net/minecraftforge/installer/SimpleInstaller.java +++ b/src/main/java/net/minecraftforge/installer/SimpleInstaller.java @@ -260,7 +260,7 @@ public void setPercentageProgress(double percentage) { @Override public void setStep(String step) { - monitor.message(step, ProgressCallback.MessagePriority.HIGH); + monitor.message(monitor.getCurrentStep() + ": " + step, ProgressCallback.MessagePriority.HIGH); } @Override diff --git a/src/main/java/net/minecraftforge/installer/actions/Action.java b/src/main/java/net/minecraftforge/installer/actions/Action.java index 68e04c9..c6ace21 100644 --- a/src/main/java/net/minecraftforge/installer/actions/Action.java +++ b/src/main/java/net/minecraftforge/installer/actions/Action.java @@ -56,8 +56,7 @@ protected void error(String message) { } public abstract boolean run(File target, Predicate optionals, File installer) throws ActionCanceledException; - public abstract boolean isPathValid(File targetDir); - public abstract String getFileError(File targetDir); + public abstract TargetValidator getTargetValidator(); public abstract String getSuccessMessage(); public String getSponsorMessage() { @@ -80,17 +79,18 @@ protected boolean downloadLibraries(File librariesDir, Predicate optiona libraries.addAll(Arrays.asList(processors.getLibraries())); StringBuilder output = new StringBuilder(); - final double steps = libraries.size(); - int progress = 1; + monitor.getStepProgress().setMaxProgress(libraries.size()); + int progress = 0; + final ProgressCallback targetMonitor = monitor.withoutDownloadProgress(); for (Library lib : libraries) { checkCancel(); - monitor.getGlobalProgress().percentageProgress(progress++ / steps); - if (!DownloadUtils.downloadLibrary(monitor, profile.getMirror(), lib, librariesDir, optionals, grabbed, additionalLibDirs)) { + if (!DownloadUtils.downloadLibrary(targetMonitor, profile.getMirror(), lib, librariesDir, optionals, grabbed, additionalLibDirs)) { LibraryDownload download = lib.getDownloads() == null ? null : lib.getDownloads().getArtifact(); if (download != null && !download.getUrl().isEmpty()) // If it doesn't have a URL we can't download it, assume we install it later output.append('\n').append(lib.getName()); } + monitor.getStepProgress().progress(++progress); } String bad = output.toString(); if (!bad.isEmpty()) { diff --git a/src/main/java/net/minecraftforge/installer/actions/ClientInstall.java b/src/main/java/net/minecraftforge/installer/actions/ClientInstall.java index 5ca7c93..c34f25c 100644 --- a/src/main/java/net/minecraftforge/installer/actions/ClientInstall.java +++ b/src/main/java/net/minecraftforge/installer/actions/ClientInstall.java @@ -193,19 +193,10 @@ private boolean injectProfile(File target) { } @Override - public boolean isPathValid(File targetDir) { - return targetDir.exists() && ( - new File(targetDir, "launcher_profiles.json").exists() || - new File(targetDir, "launcher_profiles_microsoft_store.json").exists() - ); - } - - @Override - public String getFileError(File targetDir) { - if (targetDir.exists()) - return "The directory is missing a launcher profile. Please run the minecraft launcher first"; - else - return "There is no minecraft directory set up. Either choose an alternative, or run the minecraft launcher to create one"; + public TargetValidator getTargetValidator() { + return TargetValidator.shouldExist(true) + .and(TargetValidator.isDirectory()) + .and(TargetValidator.isMCInstallationDirectory()); } @Override diff --git a/src/main/java/net/minecraftforge/installer/actions/ExtractAction.java b/src/main/java/net/minecraftforge/installer/actions/ExtractAction.java index de689c9..3e131bc 100644 --- a/src/main/java/net/minecraftforge/installer/actions/ExtractAction.java +++ b/src/main/java/net/minecraftforge/installer/actions/ExtractAction.java @@ -82,15 +82,9 @@ public boolean run(File target, Predicate optionals, File Installer) } @Override - public boolean isPathValid(File targetDir) - { - return targetDir.exists() && targetDir.isDirectory(); - } - - @Override - public String getFileError(File targetDir) - { - return !targetDir.exists() ? "Target directory does not exist" : !targetDir.isDirectory() ? "Target is not a directory" : ""; + public TargetValidator getTargetValidator() { + return TargetValidator.shouldExist(true) + .and(TargetValidator.isDirectory()); } @Override diff --git a/src/main/java/net/minecraftforge/installer/actions/PostProcessors.java b/src/main/java/net/minecraftforge/installer/actions/PostProcessors.java index 169f264..c62441a 100644 --- a/src/main/java/net/minecraftforge/installer/actions/PostProcessors.java +++ b/src/main/java/net/minecraftforge/installer/actions/PostProcessors.java @@ -126,7 +126,7 @@ public boolean process(File librariesDir, File minecraft, File root, File instal procName += (" -> " + proc.getArgs()[Arrays.asList(proc.getArgs()).indexOf("--task") + 1]); } - monitor.message("Processor: " + procName, MessagePriority.HIGH); + monitor.setCurrentStep("Processor: " + procName); Map outputs = new HashMap<>(); if (!proc.getOutputs().isEmpty()) { diff --git a/src/main/java/net/minecraftforge/installer/actions/ProgressCallback.java b/src/main/java/net/minecraftforge/installer/actions/ProgressCallback.java index 89d1148..e2b0cf4 100644 --- a/src/main/java/net/minecraftforge/installer/actions/ProgressCallback.java +++ b/src/main/java/net/minecraftforge/installer/actions/ProgressCallback.java @@ -70,6 +70,9 @@ default void message(String message) */ void message(String message, MessagePriority priority); + void setCurrentStep(String step); + String getCurrentStep(); + default ProgressBar getGlobalProgress() { return ProgressBar.NOOP; } @@ -124,18 +127,32 @@ public long skip(long n) throws IOException { } static ProgressCallback TO_STD_OUT = new ProgressCallback() { + private String currentStep; @Override public void message(String message, MessagePriority priority) { System.out.println(message); } + + @Override + public String getCurrentStep() { + return currentStep; + } + + @Override + public void setCurrentStep(String step) { + message(step, MessagePriority.HIGH); + this.currentStep = step; + } }; static ProgressCallback withOutputs(OutputStream... streams) { return new ProgressCallback() { + private String step; + @Override public void message(String message, MessagePriority priority) { @@ -153,6 +170,77 @@ public void message(String message, MessagePriority priority) } } } + + @Override + public void setCurrentStep(String step) { + message(step, MessagePriority.HIGH); + this.step = step; + } + + @Override + public String getCurrentStep() { + return step; + } + }; + } + + default ProgressCallback withoutDownloadProgress() { + final ProgressCallback self = this; + return new ProgressCallback() { + @Override + public void start(String label) { + self.start(label); + } + + @Override + public void stage(String message, boolean withProgress) { + self.stage(message, withProgress); + } + + @Override + public void stage(String message) { + self.stage(message); + } + + @Override + public void message(String message) { + self.message(message); + } + + @Override + public ProgressBar getGlobalProgress() { + return self.getGlobalProgress(); + } + + @Override + public ProgressBar getStepProgress() { + return self.getStepProgress(); + } + + @Override + public InputStream wrapStepDownload(URLConnection connection) throws IOException { + return connection.getInputStream(); + } + + @Override + public InputStream wrapStepDownload(InputStream in) { + return in; + } + + @Override + public void message(String message, MessagePriority priority) { + self.message(message, priority); + } + + @Override + public void setCurrentStep(String step) { + self.setCurrentStep(step); + } + + @Override + public String getCurrentStep() { + return self.getCurrentStep(); + } }; } diff --git a/src/main/java/net/minecraftforge/installer/actions/ServerInstall.java b/src/main/java/net/minecraftforge/installer/actions/ServerInstall.java index 7172367..7471afc 100644 --- a/src/main/java/net/minecraftforge/installer/actions/ServerInstall.java +++ b/src/main/java/net/minecraftforge/installer/actions/ServerInstall.java @@ -126,18 +126,10 @@ public boolean run(File target, Predicate optionals, File installer) thr } @Override - public boolean isPathValid(File targetDir) { - return targetDir.exists() && targetDir.isDirectory() && targetDir.list().length == 0; - } - - @Override - public String getFileError(File targetDir) { - if (!targetDir.exists()) - return "The specified directory does not exist
It will be created"; - else if (!targetDir.isDirectory()) - return "The specified path needs to be a directory"; - else - return "There are already files in the target directory"; + public TargetValidator getTargetValidator() { + return TargetValidator.shouldExist(false) + .and(TargetValidator.isDirectory()) + .and(TargetValidator.shouldBeEmpty()); } @Override diff --git a/src/main/java/net/minecraftforge/installer/actions/TargetValidator.java b/src/main/java/net/minecraftforge/installer/actions/TargetValidator.java new file mode 100644 index 0000000..96c9b5b --- /dev/null +++ b/src/main/java/net/minecraftforge/installer/actions/TargetValidator.java @@ -0,0 +1,58 @@ +package net.minecraftforge.installer.actions; + +import java.io.File; +import java.util.Objects; +import java.util.function.Supplier; + +@FunctionalInterface +public interface TargetValidator { + + ValidationResult validate(File target); + + default TargetValidator and(TargetValidator other) { + return target -> validate(target) + .combine(() -> other.validate(target)); + } + + static TargetValidator isDirectory() { + return target -> target.isDirectory() ? ValidationResult.valid() : ValidationResult.invalid(true, "The specified path needs to be a directory"); + } + + static TargetValidator shouldExist(boolean critical) { + return target -> target.exists() ? ValidationResult.valid() : ValidationResult.invalid(critical, "The specified directory does not exist" + (critical ? "" : "
It will be created")); + } + + static TargetValidator shouldBeEmpty() { + return target -> Objects.requireNonNull(target.list()).length == 0 ? ValidationResult.valid() : ValidationResult.invalid(false, "There are already files in the target directory"); + } + + static TargetValidator isMCInstallationDirectory() { + return target -> (new File(target, "launcher_profiles.json").exists() || + new File(target, "launcher_profiles_microsoft_store.json").exists()) ? ValidationResult.valid() : ValidationResult.invalid(true, "The directory is missing a launcher profile. Please run the minecraft launcher first"); + } + + class ValidationResult { + private static final ValidationResult VALID = new ValidationResult(true, false, ""); + + public final boolean valid, critical; + public final String message; + + private ValidationResult(boolean valid, boolean critical, String message) { + this.valid = valid; + this.critical = critical; + this.message = message; + } + + public static ValidationResult valid() { + return VALID; + } + + public static ValidationResult invalid(boolean critical, String message) { + return new ValidationResult(false, critical, message); + } + + public ValidationResult combine(Supplier other) { + return this.valid ? other.get() : this; + } + } +}