Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Improve progress reporting and general polish of installer #4

Merged
merged 8 commits into from
Jan 5, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ plugins {

repositories {
mavenCentral()
maven gradleutils.maven
}

group = 'net.neoforged'
Expand Down Expand Up @@ -67,6 +68,7 @@ ext {
dependencies {
implementation 'net.sf.jopt-simple:jopt-simple:5.0.4'
implementation 'com.google.code.gson:gson:2.8.7'
implementation 'net.neoforged.installertools:cli-utils:2.1.0'
testImplementation(platform('org.junit:junit-bom:5.7.2'))
testImplementation('org.junit.jupiter:junit-jupiter')
compileOnly 'org.jetbrains:annotations:24.1.0'
Expand Down Expand Up @@ -139,6 +141,10 @@ jar {
manifest.from(MANIFEST)
}

processResources {
exclude 'maven/**'
}

tasks.register('testJar', Jar) {
dependsOn shrinkJar
from zipTree(shrinkJar.archiveFile.get().asFile)
Expand Down
2 changes: 1 addition & 1 deletion proguard.pro
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
-dontwarn javax.annotation.**
-dontwarn javax.inject.**
-dontwarn com.google.gson.**

-ignorewarnings

# Keep - Applications. Keep all application classes, along with their 'main'
# methods.
Expand Down
77 changes: 76 additions & 1 deletion src/main/java/net/minecraftforge/installer/DownloadUtils.java
Original file line number Diff line number Diff line change
Expand Up @@ -24,10 +24,14 @@
import java.net.HttpURLConnection;
import java.net.InetAddress;
import java.net.MalformedURLException;
import java.net.URISyntaxException;
import java.net.URL;
import java.net.URLConnection;
import java.net.UnknownHostException;
import java.nio.file.Files;
import java.nio.file.NoSuchFileException;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.StandardCopyOption;
import java.util.Arrays;
import java.util.List;
Expand All @@ -44,12 +48,14 @@
import net.minecraftforge.installer.json.Version.Download;
import net.minecraftforge.installer.json.Version.Library;
import net.minecraftforge.installer.json.Version.LibraryDownload;
import org.jetbrains.annotations.Nullable;

public class DownloadUtils {
public static final String LIBRARIES_URL = "https://libraries.minecraft.net/";
public static final String MANIFEST_URL = "https://launchermeta.mojang.com/mc/game/version_manifest.json";

public static boolean OFFLINE_MODE = false;
private static final LocalSource LOCAL_SOURCE = LocalSource.detect();

public static boolean downloadLibrary(ProgressCallback monitor, Mirror mirror, Library library, File root, Predicate<String> optional, List<Artifact> grabbed, List<File> additionalLibraryDirs) {
Artifact artifact = library.getName();
Expand Down Expand Up @@ -90,7 +96,7 @@ public static boolean downloadLibrary(ProgressCallback monitor, Mirror mirror, L
target.getParentFile().mkdirs();

// Try extracting first
try (final InputStream input = DownloadUtils.class.getResourceAsStream("/maven/" + artifact.getPath())) {
try (final InputStream input = LOCAL_SOURCE.getArtifact(artifact.getPath())) {
if (input != null) {
monitor.message(" Extracting library from /maven/" + artifact.getPath());
Files.copy(input, target.toPath(), StandardCopyOption.REPLACE_EXISTING);
Expand Down Expand Up @@ -397,4 +403,73 @@ public static boolean extractFile(String name, File target) {
return false;
}
}

public interface LocalSource {

@Nullable
InputStream getArtifact(String path) throws IOException;

default LocalSource fallbackWith(@Nullable LocalSource other) {
if (other == null) {
return this;
}

return p -> {
final InputStream art = getArtifact(p);
return art != null ? art : other.getArtifact(p);
};
}

static LocalSource walkFromClassesOut(Path out) {
// The local path would be LegacyInstaller/build/classes/java/main, so walk upwards 4 times
for (int i = 0; i < 3; i++) {
out = out.getParent();
}

// The maven src dir is in src/main/resources/maven
final Path base = out.resolve("src/main/resources/maven");
return fromDir(base);
}

@Nullable
static LocalSource walkFromLibs(Path source) {
source = source.getParent();
if (source == null || !source.getFileName().toString().equals("libs")) return null;
source = source.getParent();
if (source == null || !source.getFileName().toString().equals("build")) return null;
source = source.getParent();
if (source == null) return null;

final Path base = source.resolve("src/main/resources/maven");
return Files.isDirectory(base) ? fromDir(base) : null;
}

static LocalSource fromDir(Path base) {
return p -> {
final Path children = base.resolve(p);
try {
return Files.newInputStream(children);
} catch (NoSuchFileException ex) {
return null;
}
};
}

static LocalSource fromResource() {
return p -> DownloadUtils.class.getResourceAsStream("/maven/" + p);
}

static LocalSource detect() {
try {
final URL url = DownloadUtils.class.getProtectionDomain().getCodeSource().getLocation();
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()));
}

return fromResource().fallbackWith(walkFromLibs(Paths.get(url.toURI())));
} catch (URISyntaxException e) {
throw new RuntimeException(e);
}
}
}
}
16 changes: 8 additions & 8 deletions src/main/java/net/minecraftforge/installer/InstallerPanel.java
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down Expand Up @@ -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)
{
Expand All @@ -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("<html>"+action.getFileError(targetDir)+"</html>");
infoLabel.setText("<html>"+valid.message+"</html>");
infoLabel.setVisible(true);
proceedButton.ifPresent(button -> button.setEnabled(false));
proceedButton.ifPresent(button -> button.setEnabled(!valid.critical));
}

if (dialog!=null)
{
dialog.invalidate();
Expand Down
18 changes: 17 additions & 1 deletion src/main/java/net/minecraftforge/installer/ProgressFrame.java
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,7 @@ public void stage(String message, boolean withProgress)

this.stepProgress.setIndeterminate(!withProgress);
this.stepProgress.setMaximum(100);
this.stepProgress.setValue(0);
this.stepProgress.setToolTipText(message);
}

Expand All @@ -151,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)
{
Expand All @@ -173,7 +186,10 @@ private static ProgressBar wrapSwing(JProgressBar bar) {
return new ProgressBar() {
@Override
public void setMaxProgress(int maximum) {
bar.setMaximum(maximum);
if (maximum != -1) {
bar.setMaximum(maximum);
bar.setIndeterminate(false);
}
}

@Override
Expand Down
68 changes: 51 additions & 17 deletions src/main/java/net/minecraftforge/installer/SimpleInstaller.java
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,9 @@
import net.minecraftforge.installer.actions.ProgressCallback;
import net.minecraftforge.installer.json.InstallV1;
import net.minecraftforge.installer.json.Util;
import net.neoforged.cliutils.progress.ProgressInterceptor;
import net.neoforged.cliutils.progress.ProgressManager;
import net.neoforged.cliutils.progress.ProgressReporter;

public class SimpleInstaller
{
Expand Down Expand Up @@ -201,6 +204,7 @@ private static OutputStream getLog() throws FileNotFoundException
File output;
if (f.isFile()) output = new File(f.getName() + ".log");
else output = new File("installer.log");
System.out.println("Outputting log to file " + output);

return new BufferedOutputStream(new FileOutputStream(output));
}
Expand All @@ -209,33 +213,63 @@ static void hookStdOut(ProgressCallback monitor)
{
final Pattern endingWhitespace = Pattern.compile("\\r?\\n$");
final OutputStream monitorStream = new OutputStream() {

private StringBuffer buffer = new StringBuffer();
shartte marked this conversation as resolved.
Show resolved Hide resolved
@Override
public void write(byte[] buf, int off, int len)
{
byte[] toWrite = new byte[len];
System.arraycopy(buf, off, toWrite, 0, len);
write(toWrite);
public void write(byte[] buf, int off, int len) {
for (int i = off; i < off + len; i++) {
write(buf[i]);
}
}

@Override
public void write(byte[] b)
{
String toWrite = new String(b);
toWrite = endingWhitespace.matcher(toWrite).replaceAll("");
if (!toWrite.isEmpty()) {
monitor.message(toWrite);
}
public void write(byte[] b) {
write(b, 0, b.length);
}

@Override
public void write(int b)
{
write(new byte[] { (byte) b });
public void write(int b) {
if (b == '\r') return; // Ignore CR
if (b == '\n') {
final String message = endingWhitespace.matcher(buffer.toString()).replaceAll("");
if (!message.isEmpty()) {
monitor.message(message);
}

buffer = new StringBuffer();
} else {
buffer.append((char) b);
}
}
};

System.setOut(new PrintStream(monitorStream));
System.setErr(new PrintStream(monitorStream));
System.setErr(new PrintStream(new ProgressInterceptor(monitorStream, new ProgressManager() {
@Override
public void setMaxProgress(int maxProgress) {
monitor.getStepProgress().setMaxProgress(maxProgress);
}

@Override
public void setProgress(int progress) {
monitor.getStepProgress().progress(progress);
}

@Override
public void setPercentageProgress(double percentage) {
monitor.getStepProgress().percentageProgress(percentage);
}

@Override
public void setStep(String step) {
monitor.message(monitor.getCurrentStep() + ": " + step, ProgressCallback.MessagePriority.HIGH);
}

@Override
public void setIndeterminate(boolean indeterminate) {
monitor.getStepProgress().setIndeterminate(false);
}
})));

System.setProperty(ProgressReporter.ENABLED_PROPERTY, "true");
}
}
12 changes: 6 additions & 6 deletions src/main/java/net/minecraftforge/installer/actions/Action.java
Original file line number Diff line number Diff line change
Expand Up @@ -56,8 +56,7 @@ protected void error(String message) {
}

public abstract boolean run(File target, Predicate<String> 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() {
Expand All @@ -80,17 +79,18 @@ protected boolean downloadLibraries(File librariesDir, Predicate<String> 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()) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
Loading