Fix (character, portal, enchantment) animations during speed up / slowed down path playback and rendering

This commit is contained in:
johni0702
2016-09-02 12:50:18 +02:00
parent 8d1a3f28d1
commit 0691ef9886
11 changed files with 107 additions and 74 deletions

View File

@@ -10,9 +10,9 @@ 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.fml.common.FMLCommonHandler;
import net.minecraftforge.fml.common.eventhandler.SubscribeEvent;
import net.minecraftforge.fml.common.gameevent.TickEvent;
import javax.annotation.Nullable;
import java.util.Iterator;
@@ -21,8 +21,10 @@ import java.util.Iterator;
* Plays a timeline.
*/
public abstract class AbstractTimelinePlayer {
private final Minecraft mc = Minecraft.getMinecraft();
private final ReplayHandler replayHandler;
private Timeline timeline;
private long lastTime;
private long lastTimestamp;
private ListenableFuture<Void> future;
private SettableFuture<Void> settableFuture;
@@ -58,6 +60,10 @@ public abstract class AbstractTimelinePlayer {
replayHandler.getReplaySender().setSyncModeAndWait();
FMLCommonHandler.instance().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();
}
@@ -70,8 +76,9 @@ public abstract class AbstractTimelinePlayer {
}
@SubscribeEvent
public void onTick(TickEvent.RenderTickEvent event) {
public void onTick(ReplayTimer.UpdatedEvent event) {
if (future.isDone()) {
mc.timer = ((ReplayTimer) mc.timer).getWrapped();
replayHandler.getReplaySender().setAsyncMode(true);
FMLCommonHandler.instance().bus().unregister(this);
return;
@@ -80,8 +87,27 @@ public abstract class AbstractTimelinePlayer {
if (time > lastTimestamp) {
time = lastTimestamp;
}
// Apply to timeline
timeline.applyToGame(time, replayHandler);
if (time == lastTimestamp) {
// 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);
}
}

View File

@@ -3,7 +3,6 @@ package com.replaymod.pathing.player;
import com.google.common.util.concurrent.ListenableFuture;
import com.replaymod.replay.ReplayHandler;
import com.replaymod.replaystudio.pathing.path.Timeline;
import net.minecraftforge.fml.common.gameevent.TickEvent;
/**
* Timeline player using the system time.
@@ -33,7 +32,7 @@ public class RealtimeTimelinePlayer extends AbstractTimelinePlayer {
}
@Override
public void onTick(TickEvent.RenderTickEvent event) {
public void onTick(ReplayTimer.UpdatedEvent event) {
if (secondFrame) {
secondFrame = false;
startTime = System.currentTimeMillis();

View File

@@ -1,15 +1,17 @@
package com.replaymod.render.hooks;
package com.replaymod.pathing.player;
import com.replaymod.core.utils.WrappedTimer;
import net.minecraft.util.Timer;
import net.minecraftforge.fml.common.FMLCommonHandler;
import net.minecraftforge.fml.common.eventhandler.Event;
/**
* Wrapper around the current timer that prevents the timer from advancing by itself.
*/
public class RenderReplayTimer extends WrappedTimer {
public class ReplayTimer extends WrappedTimer {
private final Timer state = new Timer(0);
public RenderReplayTimer(Timer wrapped) {
public ReplayTimer(Timer wrapped) {
super(wrapped);
}
@@ -18,9 +20,13 @@ public class RenderReplayTimer extends WrappedTimer {
copy(this, state); // Save our current state
super.updateTimer(); // Update current state
copy(state, this); // Restore our old state
FMLCommonHandler.instance().bus().post(new UpdatedEvent());
}
public Timer getWrapped() {
return wrapped;
}
public static class UpdatedEvent extends Event {
}
}

View File

@@ -1,6 +1,7 @@
package com.replaymod.render.rendering;
import com.replaymod.core.ReplayMod;
import com.replaymod.pathing.player.AbstractTimelinePlayer;
import com.replaymod.pathing.properties.TimestampProperty;
import com.replaymod.render.RenderSettings;
import com.replaymod.render.VideoWriter;
@@ -8,7 +9,6 @@ import com.replaymod.render.capturer.RenderInfo;
import com.replaymod.render.frame.RGBFrame;
import com.replaymod.render.gui.GuiVideoRenderer;
import com.replaymod.render.hooks.ChunkLoadingRenderGlobal;
import com.replaymod.render.hooks.RenderReplayTimer;
import com.replaymod.render.metadata.MetadataInjector;
import com.replaymod.replay.ReplayHandler;
import com.replaymod.replaystudio.pathing.path.Keyframe;
@@ -29,6 +29,7 @@ import java.util.Collection;
import java.util.EnumMap;
import java.util.Map;
import java.util.Optional;
import java.util.concurrent.Future;
import java.util.concurrent.FutureTask;
import static com.google.common.collect.Iterables.getLast;
@@ -49,6 +50,7 @@ public class VideoRenderer implements RenderInfo {
private boolean debugInfoWasShown;
private Map originalSoundLevels;
private Future<Void> timelinePlayerFuture;
private ChunkLoadingRenderGlobal chunkLoadingRenderGlobal;
private int framesDone;
@@ -130,7 +132,8 @@ public class VideoRenderer implements RenderInfo {
drawGui();
}
timeline.applyToGame(getVideoTime(), replayHandler);
// Updating the timer will cause the timeline player to update the game state
mc.timer.updateTimer();
int elapsedTicks = mc.timer.elapsedTicks;
while (elapsedTicks-- > 0) {
@@ -147,7 +150,7 @@ public class VideoRenderer implements RenderInfo {
}
private void setup() {
replayHandler.getReplaySender().setSyncModeAndWait();
timelinePlayerFuture = new TimelinePlayer(replayHandler).start(timeline);
if (!OpenGlHelper.isFramebufferEnabled()) {
Display.setResizable(false);
@@ -160,8 +163,6 @@ public class VideoRenderer implements RenderInfo {
mouseWasGrabbed = true;
}
Mouse.setGrabbed(false);
mc.timer = new RenderReplayTimer(mc.timer);
mc.timer.timerSpeed = 1;
// Mute all sounds except GUI sounds (buttons, etc.)
Map<SoundCategory, Float> mutedSounds = new EnumMap<>(SoundCategory.class);
@@ -196,7 +197,9 @@ public class VideoRenderer implements RenderInfo {
}
private void finish() {
replayHandler.getReplaySender().setAsyncMode(true);
if (!timelinePlayerFuture.isDone()) {
timelinePlayerFuture.cancel(false);
}
if (!OpenGlHelper.isFramebufferEnabled()) {
Display.setResizable(true);
@@ -205,7 +208,6 @@ public class VideoRenderer implements RenderInfo {
if (mouseWasGrabbed) {
mc.mouseHelper.grabMouseCursor();
}
mc.timer = ((RenderReplayTimer) mc.timer).getWrapped();
mc.gameSettings.mapSoundLevels = originalSoundLevels;
mc.displayGuiScreen(null);
if (chunkLoadingRenderGlobal != null) {
@@ -309,4 +311,15 @@ public class VideoRenderer implements RenderInfo {
this.cancelled = true;
renderingPipeline.cancel();
}
private class TimelinePlayer extends AbstractTimelinePlayer {
public TimelinePlayer(ReplayHandler replayHandler) {
super(replayHandler);
}
@Override
public long getTimePassed() {
return getVideoTime();
}
}
}

View File

@@ -87,6 +87,11 @@ public class ReplaySender extends ChannelInboundHandlerAdapter {
*/
protected int lastTimeStamp;
/**
* @see #currentTimeStamp()
*/
protected int currentTimeStamp;
/**
* The replay file.
*/
@@ -207,11 +212,18 @@ public class ReplaySender extends ChannelInboundHandlerAdapter {
}
/**
* Return the timestamp of the last packet sent.
* Return a fake {@link Minecraft#getSystemTime()} value that respects slowdown/speedup/pause and works in both,
* sync and async mode.
* Note: For sync mode this returns the last value passed to {@link #sendPacketsTill(int)}.
* @return The timestamp in milliseconds since the start of the replay
*/
public int currentTimeStamp() {
return lastTimeStamp;
if (asyncMode) {
int timePassed = (int) (System.currentTimeMillis() - lastPacketSent);
return lastTimeStamp + (int) (timePassed * getReplaySpeed());
} else {
return lastTimeStamp;
}
}
/**
@@ -746,9 +758,6 @@ public class ReplaySender extends ChannelInboundHandlerAdapter {
// Process packet
channelRead(ctx, pd.bytes);
// Store last timestamp
lastTimeStamp = nextTimeStamp;
} catch (EOFException eof) {
// Shit! We hit the end before finishing our job! What shall we do now?
// well, let's just pretend we're done...
@@ -761,6 +770,7 @@ public class ReplaySender extends ChannelInboundHandlerAdapter {
// This might be required if we change to async mode anytime soon
lastPacketSent = System.currentTimeMillis();
lastTimeStamp = timestamp;
}
} catch (Exception e) {
e.printStackTrace();

View File

@@ -1,6 +1,8 @@
package eu.crushedpixel.replaymod.mixin;
package com.replaymod.replay.mixin;
import eu.crushedpixel.replaymod.timer.EnchantmentTimer;
import com.replaymod.replay.ReplayHandler;
import com.replaymod.replay.ReplayModReplay;
import net.minecraft.client.Minecraft;
import net.minecraft.client.renderer.entity.RenderItem;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
@@ -10,6 +12,10 @@ import org.spongepowered.asm.mixin.injection.Redirect;
public class MixinRenderItem {
@Redirect(method = "renderEffect", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/Minecraft;getSystemTime()J"))
private long getEnchantmentTime() {
return EnchantmentTimer.getEnchantmentTime();
ReplayHandler replayHandler = ReplayModReplay.instance.getReplayHandler();
if (replayHandler != null) {
return replayHandler.getReplaySender().currentTimeStamp();
}
return Minecraft.getSystemTime();
}
}

View File

@@ -0,0 +1,21 @@
package com.replaymod.replay.mixin;
import com.replaymod.replay.ReplayHandler;
import com.replaymod.replay.ReplayModReplay;
import net.minecraft.client.Minecraft;
import net.minecraft.client.renderer.tileentity.TileEntityEndPortalRenderer;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Redirect;
@Mixin(TileEntityEndPortalRenderer.class)
public class MixinTileEntityEndPortalRenderer {
@Redirect(method = "func_180544_a", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/Minecraft;getSystemTime()J"))
private long replayModReplay_getEnchantmentTime() {
ReplayHandler replayHandler = ReplayModReplay.instance.getReplayHandler();
if (replayHandler != null) {
return replayHandler.getReplaySender().currentTimeStamp();
}
return Minecraft.getSystemTime();
}
}

View File

@@ -1,15 +0,0 @@
package eu.crushedpixel.replaymod.mixin;
import eu.crushedpixel.replaymod.timer.EnchantmentTimer;
import net.minecraft.client.renderer.tileentity.TileEntityEndPortalRenderer;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Redirect;
@Mixin(TileEntityEndPortalRenderer.class)
public class MixinTileEntityEndPortalRenderer {
@Redirect(method = "func_180544_a", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/Minecraft;getSystemTime()J"))
private long getEnchantmentTime() {
return EnchantmentTimer.getEnchantmentTime();
}
}

View File

@@ -1,33 +0,0 @@
package eu.crushedpixel.replaymod.timer;
public class EnchantmentTimer {
private static long lastRealTime = System.currentTimeMillis();
private static long lastFakeTime = System.currentTimeMillis();
private static long recordingTime = 0;
public static void resetRecordingTime() {
recordingTime = 0;
}
public static void increaseRecordingTime(long amount) {
recordingTime += amount;
}
public static long getEnchantmentTime() {
// TODO
// if(!(ReplayHandler.isInPath() && ReplayProcess.isVideoRecording())) {
// if(ReplayHandler.isInReplay()) {
// long timeDiff = System.currentTimeMillis() - lastRealTime;
// double toAdd = timeDiff * ReplayMod.replaySender.getReplaySpeed();
// lastFakeTime = Math.round(lastFakeTime + toAdd);
// lastRealTime = System.currentTimeMillis();
// return lastFakeTime;
// }
// lastFakeTime = lastRealTime = System.currentTimeMillis();
// return lastRealTime;
// }
return recordingTime;
}
}

View File

@@ -2,7 +2,9 @@
"required": true,
"package": "com.replaymod.replay.mixin",
"mixins": [
"MixinPlayerControllerMP"
"MixinPlayerControllerMP",
"MixinRenderItem",
"MixinTileEntityEndPortalRenderer"
],
"server": [],
"client": [],

View File

@@ -5,9 +5,7 @@
"MixinGuiSpectator",
"MixinMinecraft",
"MixinRenderArrow",
"MixinRenderItem",
"MixinRenderManager",
"MixinTileEntityEndPortalRenderer",
"MixinViewFrustum"
],
"server": [],