0db1b9eFix opening a replay multiple times by quickly double clicking (fixes #25)9ff767bFix spectated player having the arm model of the camera playerd82cef3Make sure video folder exists before rendering (fixes #24)cc65b26Replace custom update system with forge update checkerc8b71dcAdd tooltips to ingame buttonsb240f8aFix camera path always playing from the beginning regardless of ctrl key4805e5eFix crash when setting two keyframes to the same time via the edit keyframe gui18f8303Fix crash when moving the last keyframefcd4c07Add error popup when trying to play path with reversed time keyframes Update jGui9c605baUpdate jGui Fix handling of ESC key in popups (fixes #17)721fdc4Allow moving the keyframe timeline cursor by dragging (fixes #18)1851e97Allow deletion of selected keyframes by pressing the DELETE key (fixes #19)d547098Fix markers not being removedb2c7faaFix marker not being deselected when clicking anywhere else4e6b387Update jGui Fix popups being drawn outside of the actually visible element (fixes #21)21bbeb2Fix bitrate field always being active regardless of selected preset (fixes #20)b5a6d8bFix camera player head being visible while in third persondd0537bCenter star between resolution fields in render settings guie35bfcaIncrease spacing between rows in the render settings gui6d0cdfbUpdate jGui (fixes #15)
124 lines
4.2 KiB
Java
124 lines
4.2 KiB
Java
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.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<Void> future;
|
|
private SettableFuture<Void> settableFuture;
|
|
|
|
public AbstractTimelinePlayer(ReplayHandler replayHandler) {
|
|
this.replayHandler = replayHandler;
|
|
}
|
|
|
|
public ListenableFuture<Void> start(Timeline timeline, long from) {
|
|
startOffset = from;
|
|
return start(timeline);
|
|
}
|
|
|
|
public ListenableFuture<Void> start(Timeline timeline) {
|
|
this.timeline = timeline;
|
|
|
|
Iterator<Keyframe> iter = Iterables.concat(Iterables.transform(timeline.getPaths(),
|
|
new Function<Path, Iterable<Keyframe>>() {
|
|
@Nullable
|
|
@Override
|
|
public Iterable<Keyframe> apply(@Nullable Path input) {
|
|
assert input != null;
|
|
return input.getKeyframes();
|
|
}
|
|
})).iterator();
|
|
if (!iter.hasNext()) {
|
|
lastTimestamp = 0;
|
|
} else {
|
|
lastTimestamp = new Ordering<Keyframe>() {
|
|
@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.timerSpeed = 1;
|
|
mc.timer.elapsedPartialTicks = mc.timer.elapsedTicks = 0;
|
|
return future = settableFuture = SettableFuture.create();
|
|
}
|
|
|
|
public ListenableFuture<Void> 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.elapsedPartialTicks += passedTicks;
|
|
mc.timer.elapsedTicks = (int) mc.timer.elapsedPartialTicks;
|
|
mc.timer.elapsedPartialTicks -= mc.timer.elapsedTicks;
|
|
mc.timer.renderPartialTicks = mc.timer.elapsedPartialTicks;
|
|
|
|
lastTime = replayTime;
|
|
|
|
if (time >= lastTimestamp) {
|
|
settableFuture.set(null);
|
|
}
|
|
}
|
|
|
|
public abstract long getTimePassed();
|
|
}
|