TransitionHandler update

This commit is contained in:
huangyuhui
2018-01-25 22:41:15 +08:00
parent 056c0901f2
commit 12fa94627d
66 changed files with 646 additions and 522 deletions

View File

@@ -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()));
}

View File

@@ -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()) {

View File

@@ -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());
}

View File

@@ -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.

View File

@@ -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";
}

View File

@@ -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;

View File

@@ -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();
}

View File

@@ -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>() {

View File

@@ -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;
});

View File

@@ -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

View File

@@ -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;
}
}

View File

@@ -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) {

View File

@@ -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 {

View File

@@ -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();

View File

@@ -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<>();

View File

@@ -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;
}

View File

@@ -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();
}