Merge branch 'johni/quick-mode' into develop

This commit is contained in:
Jonas Herzig
2018-12-19 10:12:23 +01:00
8 changed files with 2511 additions and 1148 deletions

View File

@@ -281,4 +281,17 @@ public class Utils {
super.draw(renderer, size, renderInfo);
}
}
public static <T extends Throwable> void throwIfInstanceOf(Throwable t, Class<T> cls) throws T {
if (cls.isInstance(t)) {
throw cls.cast(t);
}
}
public static void throwIfUnchecked(Throwable t) {
if (t instanceof RuntimeException) {
throw (RuntimeException) t;
} else if (t instanceof Error) {
throw (Error) t;
}
}
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -1,7 +1,11 @@
package com.replaymod.replay;
import com.google.common.base.Preconditions;
import com.google.common.util.concurrent.FutureCallback;
import com.google.common.util.concurrent.Futures;
import com.google.common.util.concurrent.ListenableFuture;
import com.replaymod.core.utils.Restrictions;
import com.replaymod.core.utils.Utils;
import com.replaymod.core.utils.WrappedTimer;
import com.replaymod.replay.camera.CameraEntity;
import com.replaymod.replay.camera.SpectatorCameraController;
@@ -11,6 +15,9 @@ import com.replaymod.replay.gui.overlay.GuiReplayOverlay;
import com.replaymod.replaystudio.data.Marker;
import com.replaymod.replaystudio.replay.ReplayFile;
import com.replaymod.replaystudio.util.Location;
import de.johni0702.minecraft.gui.container.GuiContainer;
import de.johni0702.minecraft.gui.element.advanced.GuiProgressBar;
import de.johni0702.minecraft.gui.popup.AbstractGuiPopup;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.embedded.EmbeddedChannel;
import net.minecraft.client.Minecraft;
@@ -18,6 +25,7 @@ import net.minecraft.client.entity.EntityOtherPlayerMP;
import net.minecraft.client.gui.GuiScreen;
import net.minecraft.client.gui.ScaledResolution;
import net.minecraft.client.resources.I18n;
import net.minecraft.crash.CrashReport;
import net.minecraft.entity.Entity;
import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.network.NetworkManager;
@@ -26,11 +34,16 @@ import org.lwjgl.opengl.Display;
import java.io.IOException;
import java.util.*;
//#if MC<10904
//$$ import de.johni0702.minecraft.gui.element.GuiLabel;
//$$ import de.johni0702.minecraft.gui.popup.GuiInfoPopup;
//$$ import de.johni0702.minecraft.gui.utils.Colors;
//#endif
//#if MC>=10800
import com.mojang.authlib.GameProfile;
import net.minecraft.client.network.NetHandlerPlayClient;
import net.minecraft.network.EnumPacketDirection;
import net.minecraftforge.common.MinecraftForge;
import net.minecraftforge.fml.client.FMLClientHandler;
import net.minecraftforge.fml.common.network.handshake.NetworkDispatcher;
@@ -48,10 +61,13 @@ import static net.minecraft.client.renderer.GlStateManager.*;
//$$ import java.net.SocketAddress;
//$$
//$$ import static com.replaymod.core.versions.MCVer.GlStateManager.*;
//$$ import static com.replaymod.replay.ReplayModReplay.LOGGER;
//#endif
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import static com.replaymod.core.versions.MCVer.*;
import static com.replaymod.replay.ReplayModReplay.LOGGER;
import static org.lwjgl.opengl.GL11.GL_COLOR_BUFFER_BIT;
import static org.lwjgl.opengl.GL11.GL_DEPTH_BUFFER_BIT;
@@ -67,7 +83,13 @@ public class ReplayHandler {
/**
* Decodes and sends packets into channel.
*/
private final ReplaySender replaySender;
private final FullReplaySender fullReplaySender;
//#if MC>=10904
private final QuickReplaySender quickReplaySender;
private boolean quickMode = false;
//#else
//$$ private static final String QUICK_MODE_MIN_MC = "1.9.4";
//#endif
/**
* Currently active replay restrictions.
@@ -85,6 +107,8 @@ public class ReplayHandler {
private EmbeddedChannel channel;
private int replayDuration;
/**
* The position at which the camera should be located after the next jump.
*/
@@ -96,11 +120,16 @@ public class ReplayHandler {
Preconditions.checkState(mc.isCallingFromMinecraftThread(), "Must be called from Minecraft thread.");
this.replayFile = replayFile;
replayDuration = replayFile.getMetaData().getDuration();
FML_BUS.post(new ReplayOpenEvent.Pre(this));
markers = new ArrayList<>(replayFile.getMarkers().or(Collections.emptySet()));
replaySender = new ReplaySender(this, replayFile, false);
fullReplaySender = new FullReplaySender(this, replayFile, false);
//#if MC>=10904
quickReplaySender = new QuickReplaySender(ReplayModReplay.instance, replayFile);
//#endif
setup();
@@ -109,7 +138,7 @@ public class ReplayHandler {
FML_BUS.post(new ReplayOpenEvent.Post(this));
replaySender.setAsyncMode(asyncMode);
fullReplaySender.setAsyncMode(asyncMode);
}
void restartedReplay() {
@@ -131,7 +160,10 @@ public class ReplayHandler {
FML_BUS.post(new ReplayCloseEvent.Pre(this));
replaySender.terminateReplay();
fullReplaySender.terminateReplay();
//#if MC>=10904
quickReplaySender.unregister();
//#endif
replayFile.save();
replayFile.close();
@@ -185,7 +217,8 @@ public class ReplayHandler {
NetworkDispatcher networkDispatcher = new NetworkDispatcher(networkManager);
channel.attr(NetworkDispatcher.FML_DISPATCHER).set(networkDispatcher);
channel.pipeline().addFirst("ReplayModReplay_replaySender", replaySender);
channel.pipeline().addFirst("ReplayModReplay_replaySender", fullReplaySender);
channel.pipeline().addFirst("ReplayModReplay_quickReplaySender", quickReplaySender);
channel.pipeline().addLast("packet_handler", networkManager);
channel.pipeline().fireChannelActive();
networkDispatcher.clientToServerHandshake();
@@ -194,7 +227,10 @@ public class ReplayHandler {
//$$ NetworkDispatcher networkDispatcher = new NetworkDispatcher(networkManager);
//$$ channel.attr(NetworkDispatcher.FML_DISPATCHER).set(networkDispatcher);
//$$
//$$ channel.pipeline().addFirst("ReplayModReplay_replaySender", replaySender);
//$$ channel.pipeline().addFirst("ReplayModReplay_replaySender", fullReplaySender);
//#if MC>=10904
//$$ channel.pipeline().addFirst("ReplayModReplay_quickReplaySender", quickReplaySender);
//#endif
//$$ channel.pipeline().addAfter("ReplayModReplay_replaySender", "fml:packet_handler", networkDispatcher);
//$$ channel.pipeline().fireChannelActive();
//#endif
@@ -225,7 +261,7 @@ public class ReplayHandler {
//$$ ChannelOutboundHandlerAdapter dummyHandler = new ChannelOutboundHandlerAdapter();
//$$ channel = new EmbeddedChannel(dummyHandler);
//$$ channel.pipeline().remove(dummyHandler);
//$$ channel.pipeline().addFirst("ReplayModReplay_replaySender", replaySender);
//$$ channel.pipeline().addFirst("ReplayModReplay_replaySender", fullReplaySender);
//$$ channel.pipeline().addAfter("ReplayModReplay_replaySender", "packet_handler", networkManager);
//$$ channel.pipeline().fireChannelActive();
//$$
@@ -247,13 +283,122 @@ public class ReplayHandler {
}
public ReplaySender getReplaySender() {
return replaySender;
//#if MC>=10904
return quickMode ? quickReplaySender : fullReplaySender;
//#else
//$$ return fullReplaySender;
//#endif
}
public GuiReplayOverlay getOverlay() {
return overlay;
}
//#if MC>=10904
public void ensureQuickModeInitialized(Runnable andThen) {
ListenableFuture<Void> future = quickReplaySender.getInitializationPromise();
if (future == null) {
InitializingQuickModePopup popup = new InitializingQuickModePopup(overlay);
future = quickReplaySender.initialize(progress -> popup.progressBar.setProgress(progress.floatValue()));
Futures.addCallback(future, new FutureCallback<Void>() {
@Override
public void onSuccess(@Nullable Void result) {
popup.close();
}
@Override
public void onFailure(@Nonnull Throwable t) {
String message = "Failed to initialize quick mode. It will not be available.";
Utils.error(LOGGER, overlay, CrashReport.makeCrashReport(t, message), popup::close);
}
});
}
Futures.addCallback(future, new FutureCallback<Void>() {
@Override
public void onSuccess(@Nullable Void result) {
andThen.run();
}
@Override
public void onFailure(@Nonnull Throwable t) {
// Exception already printed in callback added above
}
});
}
private class InitializingQuickModePopup extends AbstractGuiPopup<InitializingQuickModePopup> {
private final GuiProgressBar progressBar = new GuiProgressBar(popup).setSize(300, 20)
.setI18nLabel("replaymod.gui.loadquickmode");
public InitializingQuickModePopup(GuiContainer container) {
super(container);
open();
}
@Override
public void close() {
super.close();
}
@Override
protected InitializingQuickModePopup getThis() {
return this;
}
}
public void setQuickMode(boolean quickMode) {
if (quickMode == this.quickMode) return;
if (quickMode && fullReplaySender.isAsyncMode()) {
// If this method is called via runLater, then it cannot switch to sync mode by itself as there might be
// some rogue packets in the task queue after it. Instead the caller must switch to sync mode first and
// use runLater until all packets have been processed (when using setAsyncModeAndWait, one runLater should
// be sufficient).
throw new IllegalStateException("Cannot switch to quick mode while in async mode.");
}
this.quickMode = quickMode;
CameraEntity cam = getCameraEntity();
if (cam != null) {
targetCameraPosition = new Location(cam.posX, cam.posY, cam.posZ, cam.rotationYaw, cam.rotationPitch);
} else {
targetCameraPosition = null;
}
if (quickMode) {
quickReplaySender.register();
quickReplaySender.restart();
quickReplaySender.sendPacketsTill(fullReplaySender.currentTimeStamp());
} else {
quickReplaySender.unregister();
fullReplaySender.sendPacketsTill(0);
fullReplaySender.sendPacketsTill(quickReplaySender.currentTimeStamp());
}
moveCameraToTargetPosition();
}
public boolean isQuickMode() {
return quickMode;
}
//#else
//$$ public void ensureQuickModeInitialized(@SuppressWarnings("unused") Runnable andThen) {
//$$ GuiInfoPopup.open(overlay,
//$$ new GuiLabel().setI18nText("replaymod.gui.noquickmode", QUICK_MODE_MIN_MC).setColor(Colors.BLACK));
//$$ }
//$$
//$$ public void setQuickMode(@SuppressWarnings("unused") boolean quickMode) {
//$$ throw new UnsupportedOperationException("Quick Mode not supported on this version.");
//$$ }
//$$
//$$ public boolean isQuickMode() {
//$$ return false;
//$$ }
//#endif
public int getReplayDuration() {
return replayDuration;
}
/**
* Return whether camera movement by user inputs and/or server packets should be suppressed.
* @return {@code true} if these kinds of movement should be suppressed
@@ -363,6 +508,55 @@ public class ReplayHandler {
}
public void doJump(int targetTime, boolean retainCameraPosition) {
//#if MC>=10904
if (getReplaySender() == quickReplaySender) {
// Always round to full tick
targetTime = targetTime + targetTime % 50;
if (targetTime >= 50) {
// Jump to time of previous tick first
quickReplaySender.sendPacketsTill(targetTime - 50);
}
// Update all entity positions (especially prev/lastTick values)
for (Entity entity : loadedEntityList(world(mc))) {
if (entity instanceof EntityOtherPlayerMP) {
EntityOtherPlayerMP e = (EntityOtherPlayerMP) entity;
e.setPosition(e.otherPlayerMPX, e.otherPlayerMPY, e.otherPlayerMPZ);
e.rotationYaw = (float) e.otherPlayerMPYaw;
e.rotationPitch = (float) e.otherPlayerMPPitch;
}
entity.lastTickPosX = entity.prevPosX = entity.posX;
entity.lastTickPosY = entity.prevPosY = entity.posY;
entity.lastTickPosZ = entity.prevPosZ = entity.posZ;
entity.prevRotationYaw = entity.rotationYaw;
entity.prevRotationPitch = entity.rotationPitch;
}
// Run previous tick
try {
mc.runTick();
} catch (IOException e) {
throw new RuntimeException(e);
}
// Jump to target tick
quickReplaySender.sendPacketsTill(targetTime);
// Immediately apply player teleport interpolation
for (Entity entity : loadedEntityList(world(mc))) {
if (entity instanceof EntityOtherPlayerMP) {
EntityOtherPlayerMP e = (EntityOtherPlayerMP) entity;
e.setPosition(e.otherPlayerMPX, e.otherPlayerMPY, e.otherPlayerMPZ);
e.rotationYaw = (float) e.otherPlayerMPYaw;
e.rotationPitch = (float) e.otherPlayerMPPitch;
}
}
return;
}
//#endif
FullReplaySender replaySender = fullReplaySender;
if (replaySender.isHurrying()) {
return; // When hurrying, no Timeline jumping etc. is possible
}

View File

@@ -142,6 +142,17 @@ public class ReplayModReplay {
}
});
core.getKeyBindingRegistry().registerKeyBinding("replaymod.input.quickmode", Keyboard.KEY_Q, () -> {
if (replayHandler != null) {
replayHandler.getReplaySender().setSyncModeAndWait();
core.runLater(() ->
replayHandler.ensureQuickModeInitialized(() -> {
replayHandler.setQuickMode(!replayHandler.isQuickMode());
replayHandler.getReplaySender().setAsyncMode(true);
}));
}
});
core.getKeyBindingRegistry().registerKeyBinding("replaymod.input.rollclockwise", Keyboard.KEY_L, () -> {
// Noop, actual handling logic in CameraEntity#update
});

1147
src/main/java/com/replaymod/replay/ReplaySender.java Executable file → Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -128,7 +128,7 @@ public class GuiReplayOverlay extends AbstractGuiOverlay<GuiReplayOverlay> {
public void run(int time) {
replayHandler.doJump(time, true);
}
}).setLength(replayHandler.getReplaySender().replayLength());
}).setLength(replayHandler.getReplayDuration());
}
public double getSpeedSliderValue() {