package com.replaymod.pathing.player; import com.google.common.base.Function; import com.google.common.collect.Iterables; import com.google.common.collect.Ordering; import com.google.common.primitives.Longs; import com.google.common.util.concurrent.ListenableFuture; import com.google.common.util.concurrent.SettableFuture; import com.replaymod.core.utils.WrappedTimer; import com.replaymod.replay.ReplayHandler; import com.replaymod.replaystudio.pathing.path.Keyframe; import com.replaymod.replaystudio.pathing.path.Path; import com.replaymod.replaystudio.pathing.path.Timeline; import net.minecraft.client.Minecraft; import net.minecraftforge.common.MinecraftForge; import net.minecraftforge.fml.common.eventhandler.SubscribeEvent; import javax.annotation.Nullable; import java.util.Iterator; /** * Plays a timeline. */ public abstract class AbstractTimelinePlayer { private final Minecraft mc = Minecraft.getMinecraft(); private final ReplayHandler replayHandler; private Timeline timeline; protected long startOffset; private long lastTime; private long lastTimestamp; private ListenableFuture future; private SettableFuture settableFuture; public AbstractTimelinePlayer(ReplayHandler replayHandler) { this.replayHandler = replayHandler; } public ListenableFuture start(Timeline timeline, long from) { startOffset = from; return start(timeline); } public ListenableFuture start(Timeline timeline) { this.timeline = timeline; Iterator iter = Iterables.concat(Iterables.transform(timeline.getPaths(), new Function>() { @Nullable @Override public Iterable apply(@Nullable Path input) { assert input != null; return input.getKeyframes(); } })).iterator(); if (!iter.hasNext()) { lastTimestamp = 0; } else { lastTimestamp = new Ordering() { @Override public int compare(@Nullable Keyframe left, @Nullable Keyframe right) { assert left != null; assert right != null; return Longs.compare(left.getTime(), right.getTime()); } }.max(iter).getTime(); } replayHandler.getReplaySender().setSyncModeAndWait(); MinecraftForge.EVENT_BUS.register(this); lastTime = 0; mc.timer = new ReplayTimer(mc.timer); mc.timer.tickLength = WrappedTimer.DEFAULT_MS_PER_TICK; mc.timer.renderPartialTicks = mc.timer.elapsedTicks = 0; return future = settableFuture = SettableFuture.create(); } public ListenableFuture getFuture() { return future; } public boolean isActive() { return future != null && !future.isDone(); } @SubscribeEvent public void onTick(ReplayTimer.UpdatedEvent event) { if (future.isDone()) { mc.timer = ((ReplayTimer) mc.timer).getWrapped(); replayHandler.getReplaySender().setReplaySpeed(0); replayHandler.getReplaySender().setAsyncMode(true); MinecraftForge.EVENT_BUS.unregister(this); return; } long time = getTimePassed(); if (time > lastTimestamp) { time = lastTimestamp; } // Apply to timeline timeline.applyToGame(time, replayHandler); // Update minecraft timer long replayTime = replayHandler.getReplaySender().currentTimeStamp(); if (lastTime == 0) { // First frame, no change yet lastTime = replayTime; } float timeInTicks = replayTime / 50f; float previousTimeInTicks = lastTime / 50f; float passedTicks = timeInTicks - previousTimeInTicks; mc.timer.renderPartialTicks += passedTicks; mc.timer.elapsedTicks = (int) mc.timer.renderPartialTicks; mc.timer.renderPartialTicks -= mc.timer.elapsedTicks; lastTime = replayTime; if (time >= lastTimestamp) { settableFuture.set(null); } } public abstract long getTimePassed(); }