TransitionHandler update
This commit is contained in:
@@ -125,8 +125,8 @@ public final class ForgeInstallTask extends TaskResult<Version> {
|
||||
|
||||
setResult(installProfile.getVersionInfo()
|
||||
.setInheritsFrom(version.getId())
|
||||
.resolve(provider)
|
||||
.setId(version.getId()).setLogging(Collections.EMPTY_MAP));
|
||||
.resolve(provider).setJar(null)
|
||||
.setId(version.getId()).setLogging(Collections.emptyMap()));
|
||||
|
||||
dependencies.add(new GameLibrariesTask(dependencyManager, installProfile.getVersionInfo()));
|
||||
}
|
||||
|
||||
@@ -71,9 +71,13 @@ public final class GameAssetDownloadTask extends Task {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void execute() {
|
||||
public void execute() throws Exception {
|
||||
int size = refreshTask.getResult().size();
|
||||
int downloaded = 0;
|
||||
for (Map.Entry<File, AssetObject> entry : refreshTask.getResult()) {
|
||||
if (Thread.interrupted())
|
||||
throw new InterruptedException();
|
||||
|
||||
File file = entry.getKey();
|
||||
AssetObject assetObject = entry.getValue();
|
||||
String url = dependencyManager.getDownloadProvider().getAssetBaseURL() + assetObject.getLocation();
|
||||
@@ -84,7 +88,6 @@ public final class GameAssetDownloadTask extends Task {
|
||||
if (file.isDirectory())
|
||||
continue;
|
||||
boolean flag = true;
|
||||
int downloaded = 0;
|
||||
try {
|
||||
// check the checksum of file to ensure that the file is not need to re-download.
|
||||
if (file.exists()) {
|
||||
|
||||
@@ -79,6 +79,9 @@ public final class GameAssetRefreshTask extends TaskResult<Collection<Pair<File,
|
||||
int progress = 0;
|
||||
if (index != null)
|
||||
for (AssetObject assetObject : index.getObjects().values()) {
|
||||
if (Thread.interrupted())
|
||||
throw new InterruptedException();
|
||||
|
||||
res.add(new Pair<>(dependencyManager.getGameRepository().getAssetObject(version.getId(), assetIndexInfo.getId(), assetObject), assetObject));
|
||||
updateProgress(++progress, index.getObjects().size());
|
||||
}
|
||||
|
||||
@@ -17,6 +17,8 @@
|
||||
*/
|
||||
package org.jackhuang.hmcl.game;
|
||||
|
||||
import org.jackhuang.hmcl.task.Task;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.Collection;
|
||||
@@ -69,6 +71,10 @@ public interface GameRepository extends VersionProvider {
|
||||
*/
|
||||
void refreshVersions();
|
||||
|
||||
default Task refreshVersionsAsync() {
|
||||
return Task.of(this::refreshVersions);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the root folder of specific version.
|
||||
* The root folders the versions must be unique.
|
||||
|
||||
@@ -19,6 +19,8 @@ package org.jackhuang.hmcl.launch;
|
||||
|
||||
import org.jackhuang.hmcl.auth.AuthInfo;
|
||||
import org.jackhuang.hmcl.game.*;
|
||||
import org.jackhuang.hmcl.task.SimpleTaskResult;
|
||||
import org.jackhuang.hmcl.task.Task;
|
||||
import org.jackhuang.hmcl.task.TaskResult;
|
||||
import org.jackhuang.hmcl.util.*;
|
||||
|
||||
@@ -274,23 +276,18 @@ public class DefaultLauncher extends Launcher {
|
||||
}
|
||||
|
||||
public final TaskResult<ManagedProcess> launchAsync() {
|
||||
return new TaskResult<ManagedProcess>() {
|
||||
@Override
|
||||
public String getId() {
|
||||
return LAUNCH_ASYNC_ID;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void execute() throws Exception {
|
||||
setResult(launch());
|
||||
}
|
||||
};
|
||||
return new SimpleTaskResult<>(LAUNCH_ASYNC_ID, this::launch);
|
||||
}
|
||||
|
||||
@Override
|
||||
public File makeLaunchScript(String file) throws IOException {
|
||||
public void makeLaunchScript(File scriptFile) throws IOException {
|
||||
boolean isWindows = OperatingSystem.WINDOWS == OperatingSystem.CURRENT_OS;
|
||||
File scriptFile = new File(file + (isWindows ? ".bat" : ".sh"));
|
||||
|
||||
if (isWindows && !FileUtils.getExtension(scriptFile).equals("bat"))
|
||||
throw new IOException("The extension of " + scriptFile + " is not 'bat' in Windows");
|
||||
else if (!isWindows && !FileUtils.getExtension(scriptFile).equals("sh"))
|
||||
throw new IOException("The extension of " + scriptFile + " is not 'sh' in macOS/Linux");
|
||||
|
||||
if (!FileUtils.makeFile(scriptFile))
|
||||
throw new IOException("Script file: " + scriptFile + " cannot be created.");
|
||||
try (BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(scriptFile)))) {
|
||||
@@ -310,7 +307,10 @@ public class DefaultLauncher extends Launcher {
|
||||
}
|
||||
if (!scriptFile.setExecutable(true))
|
||||
throw new IOException("Cannot make script file '" + scriptFile + "' executable.");
|
||||
return scriptFile;
|
||||
}
|
||||
|
||||
public final Task makeLaunchScriptAsync(File file) {
|
||||
return Task.of(() -> makeLaunchScript(file));
|
||||
}
|
||||
|
||||
private void startMonitors(ManagedProcess managedProcess) {
|
||||
@@ -367,4 +367,5 @@ public class DefaultLauncher extends Launcher {
|
||||
}
|
||||
|
||||
public static final String LAUNCH_ASYNC_ID = "process";
|
||||
public static final String LAUNCH_SCRIPT_ASYNC_ID = "script";
|
||||
}
|
||||
|
||||
@@ -61,10 +61,9 @@ public abstract class Launcher {
|
||||
}
|
||||
|
||||
/**
|
||||
* @param file The file path without extension
|
||||
* @return the actual file with extension sh or bat.
|
||||
* @param file the file path.
|
||||
*/
|
||||
public abstract File makeLaunchScript(String file) throws IOException;
|
||||
public abstract void makeLaunchScript(File file) throws IOException;
|
||||
|
||||
public abstract ManagedProcess launch() throws IOException, InterruptedException;
|
||||
|
||||
|
||||
@@ -212,6 +212,7 @@ public class FileDownloadTask extends Task {
|
||||
if (temp != null)
|
||||
temp.delete();
|
||||
Logging.LOG.log(Level.WARNING, "Unable to download file " + currentURL, e);
|
||||
exception = e;
|
||||
} finally {
|
||||
closeFiles();
|
||||
}
|
||||
|
||||
@@ -51,6 +51,7 @@ class SchedulerImpl extends Scheduler {
|
||||
} finally {
|
||||
latch.countDown();
|
||||
}
|
||||
Thread.interrupted(); // clear the `interrupted` flag to prevent from interrupting EventDispatch thread.
|
||||
});
|
||||
|
||||
return new Future<Void>() {
|
||||
|
||||
@@ -21,7 +21,7 @@ import java.util.concurrent.*;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author huang
|
||||
* @author huangyuhui
|
||||
*/
|
||||
public final class Schedulers {
|
||||
|
||||
@@ -33,7 +33,10 @@ public final class Schedulers {
|
||||
private static synchronized ExecutorService getCachedExecutorService() {
|
||||
if (CACHED_EXECUTOR == null)
|
||||
CACHED_EXECUTOR = new ThreadPoolExecutor(0, Integer.MAX_VALUE,
|
||||
60, TimeUnit.SECONDS, new SynchronousQueue<>());
|
||||
60, TimeUnit.SECONDS, new SynchronousQueue<>(), runnable -> {
|
||||
Thread thread = Executors.defaultThreadFactory().newThread(runnable);
|
||||
return thread;
|
||||
});
|
||||
|
||||
return CACHED_EXECUTOR;
|
||||
}
|
||||
@@ -42,8 +45,8 @@ public final class Schedulers {
|
||||
|
||||
private static synchronized ExecutorService getIOExecutorService() {
|
||||
if (IO_EXECUTOR == null)
|
||||
IO_EXECUTOR = Executors.newFixedThreadPool(6, r -> {
|
||||
Thread thread = Executors.defaultThreadFactory().newThread(r);
|
||||
IO_EXECUTOR = Executors.newFixedThreadPool(6, runnable -> {
|
||||
Thread thread = Executors.defaultThreadFactory().newThread(runnable);
|
||||
thread.setDaemon(true);
|
||||
return thread;
|
||||
});
|
||||
@@ -55,8 +58,8 @@ public final class Schedulers {
|
||||
|
||||
private static synchronized ExecutorService getSingleExecutorService() {
|
||||
if (SINGLE_EXECUTOR == null)
|
||||
SINGLE_EXECUTOR = Executors.newSingleThreadExecutor(r -> {
|
||||
Thread thread = Executors.defaultThreadFactory().newThread(r);
|
||||
SINGLE_EXECUTOR = Executors.newSingleThreadExecutor(runnable -> {
|
||||
Thread thread = Executors.defaultThreadFactory().newThread(runnable);
|
||||
thread.setDaemon(true);
|
||||
return thread;
|
||||
});
|
||||
|
||||
@@ -29,15 +29,18 @@ class SimpleTask extends Task {
|
||||
private final ExceptionalConsumer<AutoTypingMap<String>, ?> consumer;
|
||||
private final Scheduler scheduler;
|
||||
|
||||
public SimpleTask(ExceptionalConsumer<AutoTypingMap<String>, ?> consumer) {
|
||||
this(consumer, Schedulers.defaultScheduler());
|
||||
public SimpleTask(String name, ExceptionalConsumer<AutoTypingMap<String>, ?> consumer) {
|
||||
this(name, consumer, Schedulers.defaultScheduler());
|
||||
}
|
||||
|
||||
public SimpleTask(ExceptionalConsumer<AutoTypingMap<String>, ?> consumer, Scheduler scheduler) {
|
||||
public SimpleTask(String name, ExceptionalConsumer<AutoTypingMap<String>, ?> consumer, Scheduler scheduler) {
|
||||
this.consumer = consumer;
|
||||
this.scheduler = scheduler;
|
||||
|
||||
setName(consumer.toString());
|
||||
if (name == null)
|
||||
setSignificance(TaskSignificance.MINOR);
|
||||
else
|
||||
setName(name);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -0,0 +1,40 @@
|
||||
/*
|
||||
* Hello Minecraft! Launcher.
|
||||
* Copyright (C) 2017 huangyuhui <huanghongxun2008@126.com>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see {http://www.gnu.org/licenses/}.
|
||||
*/
|
||||
package org.jackhuang.hmcl.task;
|
||||
|
||||
import org.jackhuang.hmcl.util.ExceptionalSupplier;
|
||||
|
||||
public final class SimpleTaskResult<V> extends TaskResult<V> {
|
||||
private final String id;
|
||||
private final ExceptionalSupplier<V, ?> supplier;
|
||||
|
||||
public SimpleTaskResult(String id, ExceptionalSupplier<V, ?> supplier) {
|
||||
this.id = id;
|
||||
this.supplier = supplier;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void execute() throws Exception {
|
||||
setResult(supplier.get());
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getId() {
|
||||
return id;
|
||||
}
|
||||
}
|
||||
@@ -87,8 +87,9 @@ public abstract class Task {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
public Task setName(String name) {
|
||||
this.name = name;
|
||||
return this;
|
||||
}
|
||||
|
||||
private AutoTypingMap<String> variables = null;
|
||||
@@ -131,7 +132,7 @@ public abstract class Task {
|
||||
|
||||
private long lastTime = Long.MIN_VALUE;
|
||||
private final AtomicReference<Double> progressUpdate = new AtomicReference<>();
|
||||
private final ReadOnlyDoubleWrapper progress = new ReadOnlyDoubleWrapper(this, "progress", 0);
|
||||
private final ReadOnlyDoubleWrapper progress = new ReadOnlyDoubleWrapper(this, "progress", -1);
|
||||
|
||||
public ReadOnlyDoubleProperty progressProperty() {
|
||||
return progress.getReadOnlyProperty();
|
||||
@@ -244,20 +245,36 @@ public abstract class Task {
|
||||
});
|
||||
}
|
||||
|
||||
public static Task of(String name, ExceptionalRunnable<?> runnable) {
|
||||
return of(name, s -> runnable.run());
|
||||
}
|
||||
|
||||
public static Task of(ExceptionalRunnable<?> runnable) {
|
||||
return of(s -> runnable.run());
|
||||
}
|
||||
|
||||
public static Task of(String name, ExceptionalConsumer<AutoTypingMap<String>, ?> closure) {
|
||||
return of(name, Schedulers.defaultScheduler(), closure);
|
||||
}
|
||||
|
||||
public static Task of(ExceptionalConsumer<AutoTypingMap<String>, ?> closure) {
|
||||
return of(Schedulers.defaultScheduler(), closure);
|
||||
}
|
||||
|
||||
public static Task of(String name, Scheduler scheduler, ExceptionalConsumer<AutoTypingMap<String>, ?> closure) {
|
||||
return new SimpleTask(name, closure, scheduler);
|
||||
}
|
||||
|
||||
public static Task of(Scheduler scheduler, ExceptionalConsumer<AutoTypingMap<String>, ?> closure) {
|
||||
return new SimpleTask(closure, scheduler);
|
||||
return of(null, scheduler, closure);
|
||||
}
|
||||
|
||||
public static Task of(String name, Scheduler scheduler, ExceptionalRunnable<?> closure) {
|
||||
return new SimpleTask(name, i -> closure.run(), scheduler);
|
||||
}
|
||||
|
||||
public static Task of(Scheduler scheduler, ExceptionalRunnable<?> closure) {
|
||||
return new SimpleTask(i -> closure.run(), scheduler);
|
||||
return of(null, scheduler, closure);
|
||||
}
|
||||
|
||||
public static <V> TaskResult<V> ofResult(String id, Callable<V> callable) {
|
||||
|
||||
@@ -23,10 +23,7 @@ import org.jackhuang.hmcl.util.Lang;
|
||||
import org.jackhuang.hmcl.util.Logging;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.concurrent.ConcurrentLinkedQueue;
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
import java.util.concurrent.Future;
|
||||
import java.util.concurrent.RejectedExecutionException;
|
||||
import java.util.concurrent.*;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
import java.util.logging.Level;
|
||||
@@ -66,17 +63,22 @@ public final class TaskExecutor {
|
||||
this.scheduler = Objects.requireNonNull(scheduler);
|
||||
}
|
||||
|
||||
public void start() {
|
||||
public TaskExecutor start() {
|
||||
taskListeners.forEach(TaskListener::onStart);
|
||||
workerQueue.add(scheduler.schedule(() -> {
|
||||
if (!executeTasks(Collections.singleton(firstTask)))
|
||||
if (executeTasks(Collections.singleton(firstTask)))
|
||||
taskListeners.forEach(TaskListener::onSucceed);
|
||||
else
|
||||
taskListeners.forEach(it -> {
|
||||
it.onTerminate();
|
||||
it.onTerminate(variables);
|
||||
});
|
||||
}));
|
||||
return this;
|
||||
}
|
||||
|
||||
public boolean test() {
|
||||
taskListeners.forEach(TaskListener::onStart);
|
||||
AtomicBoolean flag = new AtomicBoolean(true);
|
||||
Future<?> future = scheduler.schedule(() -> {
|
||||
if (!executeTasks(Collections.singleton(firstTask))) {
|
||||
@@ -85,7 +87,8 @@ public final class TaskExecutor {
|
||||
it.onTerminate(variables);
|
||||
});
|
||||
flag.set(false);
|
||||
}
|
||||
} else
|
||||
taskListeners.forEach(TaskListener::onSucceed);
|
||||
});
|
||||
workerQueue.add(future);
|
||||
Lang.invoke(() -> future.get());
|
||||
@@ -128,7 +131,11 @@ public final class TaskExecutor {
|
||||
if (canceled)
|
||||
return false;
|
||||
|
||||
latch.await();
|
||||
try {
|
||||
latch.await();
|
||||
} catch (InterruptedException e) {
|
||||
return false;
|
||||
}
|
||||
return success.get() && !canceled;
|
||||
}
|
||||
|
||||
@@ -156,8 +163,10 @@ public final class TaskExecutor {
|
||||
variables.set(taskResult.getId(), taskResult.getResult());
|
||||
}
|
||||
|
||||
if (!executeTasks(task.getDependencies()) && task.isRelyingOnDependencies())
|
||||
throw new IllegalStateException("Subtasks failed for " + task.getName());
|
||||
if (!executeTasks(task.getDependencies()) && task.isRelyingOnDependencies()) {
|
||||
Logging.LOG.severe("Subtasks failed for " + task.getName());
|
||||
return false;
|
||||
}
|
||||
|
||||
flag = true;
|
||||
if (task.getSignificance().shouldLog()) {
|
||||
@@ -169,7 +178,7 @@ public final class TaskExecutor {
|
||||
} catch (InterruptedException e) {
|
||||
if (task.getSignificance().shouldLog()) {
|
||||
lastException = e;
|
||||
Logging.LOG.log(Level.FINE, "Task aborted: " + task.getName(), e);
|
||||
Logging.LOG.log(Level.FINE, "Task aborted: " + task.getName());
|
||||
task.onDone().fireEvent(new TaskEvent(this, task, true));
|
||||
taskListeners.forEach(it -> it.onFailed(task, e));
|
||||
}
|
||||
@@ -178,12 +187,10 @@ public final class TaskExecutor {
|
||||
} catch (RejectedExecutionException e) {
|
||||
return false;
|
||||
} catch (Exception e) {
|
||||
if (task.getSignificance().shouldLog()) {
|
||||
lastException = e;
|
||||
Logging.LOG.log(Level.FINE, "Task failed: " + task.getName(), e);
|
||||
task.onDone().fireEvent(new TaskEvent(this, task, true));
|
||||
taskListeners.forEach(it -> it.onFailed(task, e));
|
||||
}
|
||||
lastException = e;
|
||||
Logging.LOG.log(Level.FINE, "Task failed: " + task.getName(), e);
|
||||
task.onDone().fireEvent(new TaskEvent(this, task, true));
|
||||
taskListeners.forEach(it -> it.onFailed(task, e));
|
||||
} finally {
|
||||
task.setVariables(null);
|
||||
}
|
||||
@@ -209,7 +216,8 @@ public final class TaskExecutor {
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
Thread.currentThread().setName(task.getName());
|
||||
if (Thread.currentThread().getName().contains("pool"))
|
||||
Thread.currentThread().setName(task.getName());
|
||||
if (!executeTask(task))
|
||||
success.set(false);
|
||||
} finally {
|
||||
|
||||
@@ -27,6 +27,9 @@ import java.util.EventListener;
|
||||
*/
|
||||
public abstract class TaskListener implements EventListener {
|
||||
|
||||
public void onStart() {
|
||||
}
|
||||
|
||||
public void onReady(Task task) {
|
||||
}
|
||||
|
||||
@@ -42,6 +45,9 @@ public abstract class TaskListener implements EventListener {
|
||||
public void onTerminate(AutoTypingMap<String> variables) {
|
||||
}
|
||||
|
||||
public void onSucceed() {
|
||||
}
|
||||
|
||||
public static class DefaultTaskListener extends TaskListener {
|
||||
|
||||
public static final DefaultTaskListener INSTANCE = new DefaultTaskListener();
|
||||
|
||||
@@ -165,7 +165,7 @@ public final class JavaVersion implements Serializable {
|
||||
return JAVAS;
|
||||
}
|
||||
|
||||
public static synchronized void initialize() throws IOException, InterruptedException {
|
||||
public static synchronized void initialize() throws IOException {
|
||||
if (JAVAS != null)
|
||||
throw new IllegalStateException("JavaVersions have already been initialized.");
|
||||
HashMap<String, JavaVersion> temp = new HashMap<>();
|
||||
|
||||
@@ -219,16 +219,21 @@ public final class Lang {
|
||||
|
||||
public static <T> List<T> merge(Collection<T> a, Collection<T> b) {
|
||||
LinkedList<T> result = new LinkedList<>();
|
||||
result.addAll(a);
|
||||
result.addAll(b);
|
||||
if (a != null)
|
||||
result.addAll(a);
|
||||
if (b != null)
|
||||
result.addAll(b);
|
||||
return result;
|
||||
}
|
||||
|
||||
public static <T> List<T> merge(Collection<T> a, Collection<T> b, Collection<T> c) {
|
||||
LinkedList<T> result = new LinkedList<>();
|
||||
result.addAll(a);
|
||||
result.addAll(b);
|
||||
result.addAll(c);
|
||||
if (a != null)
|
||||
result.addAll(a);
|
||||
if (b != null)
|
||||
result.addAll(b);
|
||||
if (c != null)
|
||||
result.addAll(c);
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
@@ -17,6 +17,8 @@
|
||||
*/
|
||||
package org.jackhuang.hmcl.util;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.PrintStream;
|
||||
import java.util.Collection;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
@@ -62,6 +64,19 @@ public final class StringUtils {
|
||||
return str;
|
||||
}
|
||||
|
||||
public static String getStackTrace(Throwable throwable) {
|
||||
ByteArrayOutputStream stream = new ByteArrayOutputStream();
|
||||
throwable.printStackTrace(new PrintStream(stream));
|
||||
return stream.toString();
|
||||
}
|
||||
|
||||
public static String getStackTrace(StackTraceElement[] elements) {
|
||||
StringBuilder builder = new StringBuilder();
|
||||
for (StackTraceElement element : elements)
|
||||
builder.append("\tat ").append(element).append(OperatingSystem.LINE_SEPARATOR);
|
||||
return builder.toString();
|
||||
}
|
||||
|
||||
public static boolean isBlank(String str) {
|
||||
return str == null || str.trim().isEmpty();
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user