mirror of
https://github.com/HMCL-dev/HMCL.git
synced 2025-09-16 07:16:27 -04:00
rewrite MultiStepBinding.asyncMap
This commit is contained in:
parent
46d27a04a2
commit
bc9a9ffb72
@ -35,6 +35,7 @@ import java.util.EnumMap;
|
|||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
|
import java.util.concurrent.CompletableFuture;
|
||||||
import java.util.concurrent.ThreadPoolExecutor;
|
import java.util.concurrent.ThreadPoolExecutor;
|
||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
import java.util.logging.Level;
|
import java.util.logging.Level;
|
||||||
@ -163,20 +164,23 @@ public final class TexturesLoader {
|
|||||||
return Optional.empty();
|
return Optional.empty();
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.flatMap(it -> Optional.ofNullable(it.get(TextureType.SKIN))))
|
.flatMap(it -> Optional.ofNullable(it.get(TextureType.SKIN)))
|
||||||
|
.filter(it -> StringUtils.isNotBlank(it.getUrl())))
|
||||||
.asyncMap(it -> {
|
.asyncMap(it -> {
|
||||||
if (it.isPresent()) {
|
if (it.isPresent()) {
|
||||||
Texture texture = it.get();
|
Texture texture = it.get();
|
||||||
try {
|
return CompletableFuture.supplyAsync(() -> {
|
||||||
return loadTexture(texture);
|
try {
|
||||||
} catch (IOException e) {
|
return loadTexture(texture);
|
||||||
LOG.log(Level.WARNING, "Failed to load texture " + texture.getUrl() + ", using fallback texture", e);
|
} catch (IOException e) {
|
||||||
return getDefaultSkin(TextureModel.detectModelName(texture.getMetadata()));
|
LOG.log(Level.WARNING, "Failed to load texture " + texture.getUrl() + ", using fallback texture", e);
|
||||||
}
|
return uuidFallback;
|
||||||
|
}
|
||||||
|
}, POOL);
|
||||||
} else {
|
} else {
|
||||||
return uuidFallback;
|
return CompletableFuture.completedFuture(uuidFallback);
|
||||||
}
|
}
|
||||||
}, uuidFallback, POOL);
|
}, uuidFallback);
|
||||||
}
|
}
|
||||||
// ====
|
// ====
|
||||||
|
|
||||||
|
@ -220,4 +220,8 @@ public final class Lang {
|
|||||||
consumer.accept(t);
|
consumer.accept(t);
|
||||||
return t;
|
return t;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static void handleUncaughtException(Throwable e) {
|
||||||
|
Thread.currentThread().getUncaughtExceptionHandler().uncaughtException(Thread.currentThread(), e);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -18,13 +18,13 @@
|
|||||||
package org.jackhuang.hmcl.util.javafx;
|
package org.jackhuang.hmcl.util.javafx;
|
||||||
|
|
||||||
import static java.util.Objects.requireNonNull;
|
import static java.util.Objects.requireNonNull;
|
||||||
|
import static org.jackhuang.hmcl.util.Lang.handleUncaughtException;
|
||||||
|
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
import java.util.concurrent.Executor;
|
import java.util.concurrent.CompletableFuture;
|
||||||
import java.util.function.Function;
|
import java.util.function.Function;
|
||||||
import java.util.function.Supplier;
|
import java.util.function.Supplier;
|
||||||
|
|
||||||
import org.jackhuang.hmcl.util.InvocationDispatcher;
|
|
||||||
|
|
||||||
import javafx.application.Platform;
|
import javafx.application.Platform;
|
||||||
import javafx.beans.binding.ObjectBinding;
|
import javafx.beans.binding.ObjectBinding;
|
||||||
@ -58,8 +58,8 @@ public abstract class MultiStepBinding<T, U> extends ObjectBinding<U> {
|
|||||||
return new FlatMappedBinding<>(map(mapper), nullAlternative);
|
return new FlatMappedBinding<>(map(mapper), nullAlternative);
|
||||||
}
|
}
|
||||||
|
|
||||||
public <V> MultiStepBinding<?, V> asyncMap(Function<U, V> mapper, V initial, Executor executor) {
|
public <V> MultiStepBinding<?, V> asyncMap(Function<U, CompletableFuture<V>> mapper, V initial) {
|
||||||
return new AsyncMappedBinding<>(this, mapper, executor, initial);
|
return new AsyncMappedBinding<>(this, mapper, initial);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static class SimpleBinding<T> extends MultiStepBinding<T, T> {
|
private static class SimpleBinding<T> extends MultiStepBinding<T, T> {
|
||||||
@ -79,8 +79,8 @@ public abstract class MultiStepBinding<T, U> extends ObjectBinding<U> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public <V> MultiStepBinding<?, V> asyncMap(Function<T, V> mapper, V initial, Executor executor) {
|
public <V> MultiStepBinding<?, V> asyncMap(Function<T, CompletableFuture<V>> mapper, V initial) {
|
||||||
return new AsyncMappedBinding<>(predecessor, mapper, executor, initial);
|
return new AsyncMappedBinding<>(predecessor, mapper, initial);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -136,49 +136,79 @@ public abstract class MultiStepBinding<T, U> extends ObjectBinding<U> {
|
|||||||
|
|
||||||
private static class AsyncMappedBinding<T, U> extends MultiStepBinding<T, U> {
|
private static class AsyncMappedBinding<T, U> extends MultiStepBinding<T, U> {
|
||||||
|
|
||||||
private final InvocationDispatcher<T> dispatcher;
|
|
||||||
|
|
||||||
private boolean initialized = false;
|
private boolean initialized = false;
|
||||||
private T prev;
|
private T prev;
|
||||||
private U value;
|
private U value;
|
||||||
|
|
||||||
public AsyncMappedBinding(ObservableValue<T> predecessor, Function<T, U> mapper, Executor executor, U initial) {
|
private final Function<T, CompletableFuture<U>> mapper;
|
||||||
|
private T computingPrev;
|
||||||
|
private boolean computing = false;
|
||||||
|
|
||||||
|
public AsyncMappedBinding(ObservableValue<T> predecessor, Function<T, CompletableFuture<U>> mapper, U initial) {
|
||||||
super(predecessor);
|
super(predecessor);
|
||||||
this.value = initial;
|
this.value = initial;
|
||||||
|
this.mapper = mapper;
|
||||||
|
}
|
||||||
|
|
||||||
dispatcher = InvocationDispatcher.runOn(executor, arg -> {
|
private void tryUpdateValue(T currentPrev) {
|
||||||
synchronized (this) {
|
synchronized (this) {
|
||||||
if (initialized && Objects.equals(arg, prev)) {
|
if ((initialized && Objects.equals(prev, currentPrev))
|
||||||
return;
|
|| isComputing(currentPrev)) {
|
||||||
}
|
return;
|
||||||
}
|
}
|
||||||
U newValue = mapper.apply(arg);
|
computing = true;
|
||||||
synchronized (this) {
|
computingPrev = currentPrev;
|
||||||
prev = arg;
|
}
|
||||||
value = newValue;
|
|
||||||
initialized = true;
|
CompletableFuture<U> task;
|
||||||
|
try {
|
||||||
|
task = requireNonNull(mapper.apply(currentPrev));
|
||||||
|
} catch (Throwable e) {
|
||||||
|
valueUpdateFailed(currentPrev);
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
|
|
||||||
|
task.handle((result, e) -> {
|
||||||
|
if (e == null) {
|
||||||
|
valueUpdate(currentPrev, result);
|
||||||
|
Platform.runLater(this::invalidate);
|
||||||
|
} else {
|
||||||
|
handleUncaughtException(e);
|
||||||
|
valueUpdateFailed(currentPrev);
|
||||||
}
|
}
|
||||||
Platform.runLater(this::invalidate);
|
return null;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// called on FX thread, this method is serial
|
private void valueUpdate(T currentPrev, U computed) {
|
||||||
@Override
|
|
||||||
protected U computeValue() {
|
|
||||||
T currentPrev = predecessor.getValue();
|
|
||||||
U value;
|
|
||||||
boolean updateNeeded = false;
|
|
||||||
synchronized (this) {
|
synchronized (this) {
|
||||||
value = this.value;
|
if (isComputing(currentPrev)) {
|
||||||
if (!initialized || !Objects.equals(currentPrev, prev)) {
|
computing = false;
|
||||||
updateNeeded = true;
|
computingPrev = null;
|
||||||
|
prev = currentPrev;
|
||||||
|
value = computed;
|
||||||
|
initialized = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (updateNeeded) {
|
|
||||||
dispatcher.accept(currentPrev);
|
|
||||||
}
|
|
||||||
return value;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void valueUpdateFailed(T currentPrev) {
|
||||||
|
synchronized (this) {
|
||||||
|
if (isComputing(currentPrev)) {
|
||||||
|
computing = false;
|
||||||
|
computingPrev = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean isComputing(T prev) {
|
||||||
|
return computing && Objects.equals(prev, computingPrev);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected U computeValue() {
|
||||||
|
tryUpdateValue(predecessor.getValue());
|
||||||
|
return value;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user