178 lines
6.5 KiB
Java
178 lines
6.5 KiB
Java
/*
|
|
* Decompiled with CFR 0.152.
|
|
*
|
|
* Could not load the following classes:
|
|
* com.mojang.datafixers.util.Either
|
|
* com.mojang.logging.LogUtils
|
|
* org.jspecify.annotations.Nullable
|
|
* org.slf4j.Logger
|
|
*/
|
|
package com.mojang.realmsclient.gui.task;
|
|
|
|
import com.mojang.datafixers.util.Either;
|
|
import com.mojang.logging.LogUtils;
|
|
import com.mojang.realmsclient.gui.task.RepeatedDelayStrategy;
|
|
import java.time.Duration;
|
|
import java.util.ArrayList;
|
|
import java.util.List;
|
|
import java.util.concurrent.Callable;
|
|
import java.util.concurrent.CompletableFuture;
|
|
import java.util.concurrent.Executor;
|
|
import java.util.concurrent.TimeUnit;
|
|
import java.util.function.Consumer;
|
|
import net.minecraft.util.TimeSource;
|
|
import org.jspecify.annotations.Nullable;
|
|
import org.slf4j.Logger;
|
|
|
|
public class DataFetcher {
|
|
private static final Logger LOGGER = LogUtils.getLogger();
|
|
private final Executor executor;
|
|
private final TimeUnit resolution;
|
|
private final TimeSource timeSource;
|
|
|
|
public DataFetcher(Executor executor, TimeUnit resolution, TimeSource timeSource) {
|
|
this.executor = executor;
|
|
this.resolution = resolution;
|
|
this.timeSource = timeSource;
|
|
}
|
|
|
|
public <T> Task<T> createTask(String id, Callable<T> updater, Duration period, RepeatedDelayStrategy repeatStrategy) {
|
|
long periodInUnit = this.resolution.convert(period);
|
|
if (periodInUnit == 0L) {
|
|
throw new IllegalArgumentException("Period of " + String.valueOf(period) + " too short for selected resolution of " + String.valueOf((Object)this.resolution));
|
|
}
|
|
return new Task<T>(id, updater, periodInUnit, repeatStrategy);
|
|
}
|
|
|
|
public Subscription createSubscription() {
|
|
return new Subscription();
|
|
}
|
|
|
|
public class Task<T> {
|
|
private final String id;
|
|
private final Callable<T> updater;
|
|
private final long period;
|
|
private final RepeatedDelayStrategy repeatStrategy;
|
|
private @Nullable CompletableFuture<ComputationResult<T>> pendingTask;
|
|
private @Nullable SuccessfulComputationResult<T> lastResult;
|
|
private long nextUpdate = -1L;
|
|
|
|
private Task(String id, Callable<T> updater, long period, RepeatedDelayStrategy repeatStrategy) {
|
|
this.id = id;
|
|
this.updater = updater;
|
|
this.period = period;
|
|
this.repeatStrategy = repeatStrategy;
|
|
}
|
|
|
|
private void updateIfNeeded(long currentTime) {
|
|
if (this.pendingTask != null) {
|
|
ComputationResult result = this.pendingTask.getNow(null);
|
|
if (result == null) {
|
|
return;
|
|
}
|
|
this.pendingTask = null;
|
|
long completionTime = result.time;
|
|
result.value().ifLeft(value -> {
|
|
this.lastResult = new SuccessfulComputationResult<Object>(value, completionTime);
|
|
this.nextUpdate = completionTime + this.period * this.repeatStrategy.delayCyclesAfterSuccess();
|
|
}).ifRight(e -> {
|
|
long cycles = this.repeatStrategy.delayCyclesAfterFailure();
|
|
LOGGER.warn("Failed to process task {}, will repeat after {} cycles", new Object[]{this.id, cycles, e});
|
|
this.nextUpdate = completionTime + this.period * cycles;
|
|
});
|
|
}
|
|
if (this.nextUpdate <= currentTime) {
|
|
this.pendingTask = CompletableFuture.supplyAsync(() -> {
|
|
try {
|
|
T result = this.updater.call();
|
|
long completionTime = DataFetcher.this.timeSource.get(DataFetcher.this.resolution);
|
|
return new ComputationResult(Either.left(result), completionTime);
|
|
}
|
|
catch (Exception e) {
|
|
long completionTime = DataFetcher.this.timeSource.get(DataFetcher.this.resolution);
|
|
return new ComputationResult(Either.right((Object)e), completionTime);
|
|
}
|
|
}, DataFetcher.this.executor);
|
|
}
|
|
}
|
|
|
|
public void reset() {
|
|
this.pendingTask = null;
|
|
this.lastResult = null;
|
|
this.nextUpdate = -1L;
|
|
}
|
|
}
|
|
|
|
public class Subscription {
|
|
private final List<SubscribedTask<?>> subscriptions = new ArrayList();
|
|
|
|
public <T> void subscribe(Task<T> task, Consumer<T> output) {
|
|
SubscribedTask<T> subscription = new SubscribedTask<T>(DataFetcher.this, task, output);
|
|
this.subscriptions.add(subscription);
|
|
subscription.runCallbackIfNeeded();
|
|
}
|
|
|
|
public void forceUpdate() {
|
|
for (SubscribedTask<?> subscription : this.subscriptions) {
|
|
subscription.runCallback();
|
|
}
|
|
}
|
|
|
|
public void tick() {
|
|
for (SubscribedTask<?> subscription : this.subscriptions) {
|
|
subscription.update(DataFetcher.this.timeSource.get(DataFetcher.this.resolution));
|
|
}
|
|
}
|
|
|
|
public void reset() {
|
|
for (SubscribedTask<?> subscription : this.subscriptions) {
|
|
subscription.reset();
|
|
}
|
|
}
|
|
}
|
|
|
|
private class SubscribedTask<T> {
|
|
private final Task<T> task;
|
|
private final Consumer<T> output;
|
|
private long lastCheckTime = -1L;
|
|
|
|
private SubscribedTask(DataFetcher dataFetcher, Task<T> task, Consumer<T> output) {
|
|
this.task = task;
|
|
this.output = output;
|
|
}
|
|
|
|
private void update(long currentTime) {
|
|
this.task.updateIfNeeded(currentTime);
|
|
this.runCallbackIfNeeded();
|
|
}
|
|
|
|
private void runCallbackIfNeeded() {
|
|
SuccessfulComputationResult lastResult = this.task.lastResult;
|
|
if (lastResult != null && this.lastCheckTime < lastResult.time) {
|
|
this.output.accept(lastResult.value);
|
|
this.lastCheckTime = lastResult.time;
|
|
}
|
|
}
|
|
|
|
private void runCallback() {
|
|
SuccessfulComputationResult lastResult = this.task.lastResult;
|
|
if (lastResult != null) {
|
|
this.output.accept(lastResult.value);
|
|
this.lastCheckTime = lastResult.time;
|
|
}
|
|
}
|
|
|
|
private void reset() {
|
|
this.task.reset();
|
|
this.lastCheckTime = -1L;
|
|
}
|
|
}
|
|
|
|
private record SuccessfulComputationResult<T>(T value, long time) {
|
|
}
|
|
|
|
private record ComputationResult<T>(Either<T, Exception> value, long time) {
|
|
}
|
|
}
|
|
|