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

Added progress bar for downloads #213

Merged
merged 2 commits into from
Nov 13, 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
Original file line number Diff line number Diff line change
Expand Up @@ -144,6 +144,16 @@ public void setHidingPasswordsSupported(boolean hidingPasswordsSupported) {
passwordContext.setHidingPasswordsSupported(hidingPasswordsSupported);
}

@Override
public Progressbar displayProgressBar(Progressbar.Configuration configuration) {
CommandLineReader commandLineReader = this.commandLineReader;
if (commandLineReader != null) {
return commandLineReader.displayProgressBar(configuration);
}

return Progressbar.dummy();
}

private static final class EmptyCommandContext implements CommandContext {
private static final EmptyCommandContext INSTANCE = new EmptyCommandContext();

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -83,4 +83,11 @@ default void close() throws IOException {
// see JLineCommandLineReader
}

/**
* @return a Progressbar to display progress with.
*/
default Progressbar displayProgressBar(Progressbar.Configuration configuration) {
return Progressbar.dummy();
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
package me.earth.headlessmc.api.command.line;

import lombok.Data;

public interface Progressbar extends AutoCloseable {
void stepBy(long n);

void stepTo(long n);

void step();

void maxHint(long n);

boolean isDummy();

@Override
void close();

static Progressbar dummy() {
return new Progressbar() {
@Override
public void close() {

}

@Override
public void stepBy(long n) {

}

@Override
public void stepTo(long n) {

}

@Override
public void step() {

}

@Override
public void maxHint(long n) {

}

@Override
public boolean isDummy() {
return true;
}
};
}

@Data
class Configuration {
private final String taskName;
private final long initialMax;
}

}
3 changes: 3 additions & 0 deletions headlessmc-jline/build.gradle
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
dependencies {
api project(':headlessmc-api')
api 'org.jline:jline:3.26.3'
api ('me.tongfei:progressbar:0.9.5') {
exclude module: 'jline'
}
implementation 'net.java.dev.jna:jna:5.14.0'
// TODO: maybe also package jansi for testing?
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,11 @@
import me.earth.headlessmc.api.HeadlessMc;
import me.earth.headlessmc.api.command.line.CommandLine;
import me.earth.headlessmc.api.command.line.CommandLineReader;
import me.earth.headlessmc.api.command.line.Progressbar;
import me.earth.headlessmc.api.config.Property;
import me.earth.headlessmc.api.process.InAndOutProvider;
import me.tongfei.progressbar.ProgressBarBuilder;
import me.tongfei.progressbar.ProgressBarStyle;
import org.jetbrains.annotations.Nullable;
import org.jline.reader.EndOfFileException;
import org.jline.reader.LineReader;
Expand Down Expand Up @@ -48,6 +51,9 @@ public class JLineCommandLineReader implements CommandLineReader {
*/
protected volatile boolean dumb;

protected volatile boolean enableProgressbar = Boolean.parseBoolean(System.getProperty(JLineProperties.ENABLE_PROGRESS_BAR.getName(), "true"));
protected volatile String progressBarStyle = System.getProperty(JLineProperties.PROGRESS_BAR_STYLE.getName());

@Override
public void read(HeadlessMc hmc) throws IOError {
CommandLine commandLine = hmc.getCommandLine();
Expand Down Expand Up @@ -99,6 +105,8 @@ public void read(HeadlessMc hmc) throws IOError {

@Override
public synchronized void open(HeadlessMc hmc) throws IOException {
enableProgressbar = hmc.getConfig().get(JLineProperties.ENABLE_PROGRESS_BAR, true);
progressBarStyle = hmc.getConfig().get(JLineProperties.PROGRESS_BAR_STYLE, null);
if (hmc.getConfig().get(JLineProperties.PREVENT_DEPRECATION_WARNING, true)) {
System.setProperty("org.jline.terminal.disableDeprecatedProviderWarning", "true");
}
Expand Down Expand Up @@ -140,6 +148,39 @@ public synchronized void close() throws IOException {
}
}

@Override
public Progressbar displayProgressBar(Progressbar.Configuration configuration) {
Terminal currentTerminal = terminal;
if (currentTerminal == null || !enableProgressbar) {
return Progressbar.dummy();
}

ProgressBarBuilder builder = new ProgressBarBuilder();
builder.setTaskName(configuration.getTaskName());
builder.setInitialMax(configuration.getInitialMax());
String style = progressBarStyle;
if (style != null) {
// ProgressBarStyle is not an enum anymore after 0.10.0
switch (style) {
case "COLORFUL_UNICODE_BLOCK":
builder.setStyle(ProgressBarStyle.COLORFUL_UNICODE_BLOCK);
break;
case "COLORFUL_UNICODE_BAR":
builder.setStyle(ProgressBarStyle.COLORFUL_UNICODE_BAR);
break;
case "UNICODE_BLOCK":
builder.setStyle(ProgressBarStyle.UNICODE_BLOCK);
break;
case "ASCII":
builder.setStyle(ProgressBarStyle.ASCII);
break;
default:
}
}

return new JlineProgressbar(builder.build());
}

protected Terminal buildTerminal(HeadlessMc hmc, boolean dumb, String providers, InAndOutProvider io) throws IOException {
return buildTerminalBuilder(hmc, dumb, providers, io).build();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,4 +28,7 @@ public interface JLineProperties {
Property<Boolean> EXEC = PropertyTypes.bool("hmc.jline.exec");
Property<Boolean> SYSTEM = PropertyTypes.bool("hmc.jline.system");

Property<Boolean> ENABLE_PROGRESS_BAR = PropertyTypes.bool("hmc.jline.enable.progressbar");
Property<String> PROGRESS_BAR_STYLE = PropertyTypes.string("hmc.jline.progressbar.style");

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
package me.earth.headlessmc.jline;

import lombok.Getter;
import lombok.RequiredArgsConstructor;
import me.earth.headlessmc.api.command.line.Progressbar;
import me.tongfei.progressbar.ProgressBar;

@Getter
@RequiredArgsConstructor
public class JlineProgressbar implements Progressbar {
private final ProgressBar progressBar;

@Override
public void stepBy(long n) {
progressBar.stepBy(n);
}

@Override
public void stepTo(long n) {
progressBar.stepTo(n);
}

@Override
public void step() {
progressBar.step();
}

@Override
public void maxHint(long n) {
progressBar.maxHint(n);
}

@Override
public void close() {
progressBar.close();
}

@Override
public boolean isDummy() {
return false;
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ public void execute(Version version, String... args) throws CommandException {
private void checkAssets(Version version, int[] values, String...args) throws IOException {
if (CommandUtil.hasFlag("-assets", args)) {
ctx.log("Checking assets of version " + version.getName());
AssetsDownloader assetsDownloader = new AssetsDownloader(ctx.getDownloadService(), ctx, ctx.getMcFiles(), version.getAssetsUrl(), version.getAssets()) {
AssetsDownloader assetsDownloader = new AssetsDownloader(ctx.getCommandLine(), ctx.getDownloadService(), ctx, ctx.getMcFiles(), version.getAssetsUrl(), version.getAssets()) {
@Override
protected void downloadAsset(String progress, String name, String hash, @Nullable Long size, boolean mapToResources) throws IOException {
val firstTwo = hash.substring(0, 2);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,12 +51,8 @@ public void execute(Version version, String... args) throws CommandException {
boolean quit = flag(ctx, "-quit", LauncherProperties.INVERT_QUIT_FLAG, LauncherProperties.ALWAYS_QUIT_FLAG, args);
int status = 0;
try {
if (!prepare) {
ctx.getCommandLine().close();
}

status = runProcess(version, files, quit, prepare, args);
} catch (IOException | LaunchException | AuthException e) {
} catch (LaunchException | AuthException e) {
status = -1;
log.error(e);
ctx.log(String.format("Couldn't launch %s: %s", version.getName(), e.getMessage()));
Expand Down Expand Up @@ -112,6 +108,7 @@ private int runProcess(Version version, FileManager files, boolean quit, boolean
.version(version)
.launcher(ctx)
.files(files)
.closeCommandLine(!prepare)
.parseFlags(ctx, quit, args)
.prepare(prepare)
.build()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,10 @@
import com.google.gson.JsonObject;
import lombok.CustomLog;
import lombok.RequiredArgsConstructor;
import lombok.Setter;
import lombok.val;
import me.earth.headlessmc.api.command.line.CommandLine;
import me.earth.headlessmc.api.command.line.Progressbar;
import me.earth.headlessmc.api.config.HasConfig;
import me.earth.headlessmc.launcher.LauncherProperties;
import me.earth.headlessmc.launcher.files.FileManager;
Expand All @@ -25,12 +28,16 @@ public class AssetsDownloader {
private final ChecksumService checksumService = new ChecksumService();
private final DummyAssets dummyAssets = new DummyAssets();

private final CommandLine commandLine;
private final DownloadService downloadService;
private final HasConfig config;
private final FileManager files;
private final String url;
private final String id;

@Setter
protected boolean shouldLog = true;

public void download() throws IOException {
Path index = files.getDir("assets").toPath().resolve("indexes").resolve(id + ".json");
// Why does this file always corrupt on CheerpJ?
Expand All @@ -51,18 +58,26 @@ public void download() throws IOException {
config.getConfig().get(LauncherProperties.ASSETS_BACKOFF, true)
);

objects.getAsJsonObject().entrySet().forEach(entry -> ioService.addTask(progress -> {
JsonObject jo = entry.getValue().getAsJsonObject();
downloadAsset(
progress,
entry.getKey(),
jo.get("hash").getAsString(),
jo.get("size") == null ? null : jo.get("size").getAsLong(),
jo.get("map_to_resources") != null && jo.get("map_to_resources").getAsBoolean()
);
}));

ioService.execute();
// TODO: provide better ETA, later assets take longer
try (Progressbar progressbar = commandLine.displayProgressBar(new Progressbar.Configuration("Downloading Assets", objects.size()))) {
ioService.setShouldLog(progressbar.isDummy());
shouldLog = progressbar.isDummy();

objects.getAsJsonObject().entrySet().forEach(entry -> ioService.addTask(progress -> {
JsonObject jo = entry.getValue().getAsJsonObject();
downloadAsset(
progress,
entry.getKey(),
jo.get("hash").getAsString(),
jo.get("size") == null ? null : jo.get("size").getAsLong(),
jo.get("map_to_resources") != null && jo.get("map_to_resources").getAsBoolean()
);

progressbar.step();
}));

ioService.execute();
}
}

protected void downloadAsset(String progress, String name, String hash, @Nullable Long size, boolean mapToResources) throws IOException {
Expand Down Expand Up @@ -94,7 +109,10 @@ protected void downloadAsset(String progress, String name, String hash, @Nullabl

protected byte @Nullable [] download(String firstTwo, String hash, String progress, String name, Path to, @Nullable Long size) throws IOException {
val from = URL + firstTwo + "/" + hash;
log.info(progress + " Downloading: " + name + " from " + from + " to " + to);
if (shouldLog) {
log.info(progress + " Downloading: " + name + " from " + from + " to " + to);
}

boolean checkHash = config.getConfig().get(LauncherProperties.ASSETS_CHECK_HASH, true);
boolean checkSize = checkHash || config.getConfig().get(LauncherProperties.ASSETS_CHECK_SIZE, true);
Long expectedSize = checkSize ? size : null;
Expand All @@ -115,7 +133,12 @@ protected void copyToLegacy(String name, Path file, String hash, @Nullable Long
if ("pre-1.6".equals(id)) {
// TODO: old versions have the map_to_resource thing, copy to resources
val legacy = files.getDir("assets").toPath().resolve("virtual").resolve("legacy").resolve(name);
log.info("Legacy version, copying to " + legacy);
if (shouldLog) {
log.info("Legacy version, copying to " + legacy);
} else {
log.debug("Legacy version, copying to " + legacy);
}

integrityCheck("Legacy", legacy, hash, size);
if (copy && !Files.exists(legacy)) {
Files.copy(file, legacy, StandardCopyOption.REPLACE_EXISTING);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import lombok.CustomLog;
import lombok.RequiredArgsConstructor;
import lombok.Setter;
import me.earth.headlessmc.api.config.HasConfig;
import me.earth.headlessmc.launcher.LauncherProperties;
import me.earth.headlessmc.launcher.os.OS;
Expand All @@ -18,10 +19,16 @@ public class LibraryDownloader {
private final HasConfig config;
private final OS os;

@Setter
private boolean shouldLog = true;

public void download(Library library, Path to) throws IOException {
String libPath = library.getPath(os);
String url = library.getUrl(libPath);
log.info(libPath + " is missing, downloading from " + url);
if (shouldLog) {
log.info(libPath + " is missing, downloading from " + url);
}

download(url, to, library.getSha1(), library.getSize());
}

Expand Down
Loading
Loading