Merge branch 'johni/quick-mode' into develop
This commit is contained in:
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
1143
src/main/java/com/replaymod/replay/FullReplaySender.java
Executable file
1143
src/main/java/com/replaymod/replay/FullReplaySender.java
Executable file
File diff suppressed because it is too large
Load Diff
1127
src/main/java/com/replaymod/replay/QuickReplaySender.java
Normal file
1127
src/main/java/com/replaymod/replay/QuickReplaySender.java
Normal file
File diff suppressed because it is too large
Load Diff
@@ -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
|
||||
}
|
||||
|
||||
@@ -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
1147
src/main/java/com/replaymod/replay/ReplaySender.java
Executable file → Normal file
File diff suppressed because it is too large
Load Diff
@@ -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() {
|
||||
|
||||
Reference in New Issue
Block a user