package com.mojang.realmsclient.gui.task; import com.mojang.datafixers.util.Either; import com.mojang.logging.LogUtils; 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 javax.annotation.Nullable; import net.minecraft.util.TimeSource; import net.minecraftforge.api.distmarker.Dist; import net.minecraftforge.api.distmarker.OnlyIn; import org.slf4j.Logger; @OnlyIn(Dist.CLIENT) public class DataFetcher { static final Logger LOGGER = LogUtils.getLogger(); final Executor executor; final TimeUnit resolution; final TimeSource timeSource; public DataFetcher(Executor p_239381_, TimeUnit p_239382_, TimeSource p_239383_) { this.executor = p_239381_; this.resolution = p_239382_; this.timeSource = p_239383_; } public DataFetcher.Task createTask(String p_239623_, Callable p_239624_, Duration p_239625_, RepeatedDelayStrategy p_239626_) { long i = this.resolution.convert(p_239625_); if (i == 0L) { throw new IllegalArgumentException("Period of " + p_239625_ + " too short for selected resolution of " + this.resolution); } else { return new DataFetcher.Task<>(p_239623_, p_239624_, i, p_239626_); } } public DataFetcher.Subscription createSubscription() { return new DataFetcher.Subscription(); } @OnlyIn(Dist.CLIENT) static record ComputationResult(Either value, long time) { } @OnlyIn(Dist.CLIENT) class SubscribedTask { private final DataFetcher.Task task; private final Consumer output; private long lastCheckTime = -1L; SubscribedTask(final DataFetcher.Task p_239959_, final Consumer p_239960_) { this.task = p_239959_; this.output = p_239960_; } void update(long p_239226_) { this.task.updateIfNeeded(p_239226_); this.runCallbackIfNeeded(); } void runCallbackIfNeeded() { DataFetcher.SuccessfulComputationResult successfulcomputationresult = this.task.lastResult; if (successfulcomputationresult != null && this.lastCheckTime < successfulcomputationresult.time) { this.output.accept(successfulcomputationresult.value); this.lastCheckTime = successfulcomputationresult.time; } } void runCallback() { DataFetcher.SuccessfulComputationResult successfulcomputationresult = this.task.lastResult; if (successfulcomputationresult != null) { this.output.accept(successfulcomputationresult.value); this.lastCheckTime = successfulcomputationresult.time; } } void reset() { this.task.reset(); this.lastCheckTime = -1L; } } @OnlyIn(Dist.CLIENT) public class Subscription { private final List> subscriptions = new ArrayList<>(); public void subscribe(DataFetcher.Task p_239442_, Consumer p_239443_) { DataFetcher.SubscribedTask subscribedtask = DataFetcher.this.new SubscribedTask<>(p_239442_, p_239443_); this.subscriptions.add(subscribedtask); subscribedtask.runCallbackIfNeeded(); } public void forceUpdate() { for (DataFetcher.SubscribedTask subscribedtask : this.subscriptions) { subscribedtask.runCallback(); } } public void tick() { for (DataFetcher.SubscribedTask subscribedtask : this.subscriptions) { subscribedtask.update(DataFetcher.this.timeSource.get(DataFetcher.this.resolution)); } } public void reset() { for (DataFetcher.SubscribedTask subscribedtask : this.subscriptions) { subscribedtask.reset(); } } } @OnlyIn(Dist.CLIENT) static record SuccessfulComputationResult(T value, long time) { } @OnlyIn(Dist.CLIENT) public class Task { private final String id; private final Callable updater; private final long period; private final RepeatedDelayStrategy repeatStrategy; @Nullable private CompletableFuture> pendingTask; @Nullable DataFetcher.SuccessfulComputationResult lastResult; private long nextUpdate = -1L; Task(final String p_239074_, final Callable p_239075_, final long p_239076_, final RepeatedDelayStrategy p_239077_) { this.id = p_239074_; this.updater = p_239075_; this.period = p_239076_; this.repeatStrategy = p_239077_; } void updateIfNeeded(long p_239710_) { if (this.pendingTask != null) { DataFetcher.ComputationResult computationresult = this.pendingTask.getNow(null); if (computationresult == null) { return; } this.pendingTask = null; long i = computationresult.time; computationresult.value().ifLeft(p_239691_ -> { this.lastResult = new DataFetcher.SuccessfulComputationResult<>((T)p_239691_, i); this.nextUpdate = i + this.period * this.repeatStrategy.delayCyclesAfterSuccess(); }).ifRight(p_239281_ -> { long j = this.repeatStrategy.delayCyclesAfterFailure(); DataFetcher.LOGGER.warn("Failed to process task {}, will repeat after {} cycles", this.id, j, p_239281_); this.nextUpdate = i + this.period * j; }); } if (this.nextUpdate <= p_239710_) { this.pendingTask = CompletableFuture.supplyAsync(() -> { try { T t = this.updater.call(); long k = DataFetcher.this.timeSource.get(DataFetcher.this.resolution); return new DataFetcher.ComputationResult<>(Either.left(t), k); } catch (Exception exception) { long j = DataFetcher.this.timeSource.get(DataFetcher.this.resolution); return new DataFetcher.ComputationResult<>(Either.right(exception), j); } }, DataFetcher.this.executor); } } public void reset() { this.pendingTask = null; this.lastResult = null; this.nextUpdate = -1L; } } }